如何使用Selenium Wire
Selenium 是一个用于自动化 Web 浏览器的开源框架。然而,它有一些相关的限制,比如不能自动拦截和改变网络流量。然而,有一个解决方案:Selenium Wire,一个增强基础 Selenium 的插件。
什么是Selenium Wire
Selenium Wire 是一个 Python 库,它增强了 Selenium 的 Python 绑定,以便您可以访问浏览器发出的实际请求。它提供了一个易于使用的界面来拦截和修改网络流量。
此外,网络抓取工具可以使用 Selenium Wire 来分析网站的网络流量,例如请求、响应和 WebSocket 消息。
在 Python 中安装 Selenium Wire 并开始使用
让我们设置 Selenium Wire 并了解如何开始使用它。但首先,确保安装了Python v3.7或更高版本。你可以像这样在你的机器上检查 Python 版本:
python --version
然后,使用以下命令安装 Selenium Wire pip
:
pip install selenium-wire
这也将在扩展时安装其主要依赖项 Selenium。
尽管 Selenium 支持所有主流浏览器,但我们将在本教程中使用 Chrome。我们还要安装Python WebDriver Manager以确保为您的浏览器安装正确的驱动程序。这是推荐的,因为大多数机器会自动更新浏览器而不是驱动程序。
这是一个显示 Selenium 主要用法的脚本。它初始化ChromeDriver的实例并在新的浏览器选项卡中打开ScrapeMe(我们的目标站点)。然后它会终止实例并打印站点的文本内容。
from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By service = ChromeService(executable_path=ChromeDriverManager().install()) # Creates an instance of the chrome driver (browser) driver = webdriver.Chrome(service=service) # Hit target site driver.get('https://scrapeme.live/shop/') # Get body element of target site body = driver.find_element(By.TAG_NAME, 'body') # Print body text content print(body.text) # Kill browser instance driver.quit()
现在,要使用,将fromselenium-wire
的导入更新为:webdriver
selenium
seleniumwire
from seleniumwire import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager # ...
运行由 Selenium Wire 提供支持的脚本,您将在新的浏览器实例中看到 ScrapeMe 站点:
关闭后,您将在终端中看到页面文本:
解析对 JSON 的响应
ScrapeMe 包含一个 Pokémon 列表及其各自的价格,我们会将它们抓取到一个 JSON 对象中。
使用该类woocommerce-loop-product__title
获取每个生物的名称,并使用该类woocommerce-Price-amount
获取其单独的价格。
通过遍历所有 Pokémon 并将名称和价格作为键值对存储在字典中来执行这些步骤data
。
import json # ... driver.get('https://scrapeme.live/shop/') products = driver.find_elements(By.CSS_SELECTOR, '.products > li') data = {} for product in products: pokemon_name = product.find_element(By.CLASS_NAME, 'woocommerce-loop-product__title').text pokemon_price = product.find_element(By.CLASS_NAME, 'woocommerce-Price-amount').text data[pokemon_name] = pokemon_price print(json.dumps(data, ensure_ascii=False)) # Kill browser instance driver.quit()
Selenium Wire chrome选项
Selenium Wire 允许您在创建 WebDriver 时指定额外的 Chrome 选项以进一步自定义它。您可以将它们作为参数传递给参数seleniumwire_options
。
driver = webdriver.Chrome( service=service, # seleniumwire options seleniumwire_options={} )
这是 Selenium Wire 提供的所有选项的列表:
选项 | 描述 | 例子 | 默认值 |
---|---|---|---|
addr |
当前机器的 IP 或主机名 | { 'addr': '159.89.173.104' } |
127.0.0.1 |
auto_config |
自动配置浏览器以获取请求 | { 'auto_config': False } |
True |
ca_cert |
根 (CA) 证书的路径 | { 'ca_cert': '/path/to/ca.crt' } |
默认路径 |
ca_key |
私钥的路径 | { 'ca_key': '/path/to/ca.key' } |
默认密钥 |
disable_capture |
禁用请求捕获 | { 'disable_capture': True } |
False |
disable_encoding |
请求未压缩的服务器数据 | { 'disable_encoding': True } |
False |
enable_har |
存储 HTTP 事务的 HAR 存档,可以使用driver.har |
{ 'enable_har': True } |
False |
exclude_hosts |
Selenium Wire 应忽略的地址 | { 'exclude_hosts': ['zenrows.com'] } |
[] |
ignore_http_methods |
Selenium Wire 将忽略这些大写的 HTTP 方法 | { 'ignore_http_methods': ['TRACE'] } |
['OPTIONS'] |
port |
Selenium Wire 的后端端口 | { 'port': 8080 } |
随机端口 |
proxy |
代理服务器的上游配置 | { 'proxy': { 'http': 'http://190.43.92.130:999', 'https': 'http://5.78.76.237:8080', } } |
{} |
request_storage |
存储类型 | { 'request_storage': 'memory } |
disk |
request_storage_base_dir |
Selenium Wire 请求和响应的默认基于磁盘的存储位置 | { 'request_storage_base_dir': '/path/to/storage_folder' } |
系统临时文件夹 |
request_storage_max_size |
存储在内存中的最大请求数 | { 'request_storage_max_size': 100 } |
无限 |
suppress_connection_errors |
抑制与连接相关的回溯 | { 'suppress_connection_errors': False } |
True |
verify_ssl |
验证 SSL 证书 | { 'verify_ssl': False } |
False |
了解请求和响应对象
Selenium 提供对表示 HTTP 请求和响应的两个对象的访问:request
和response
对象。
您可以在下面看到对象的属性request
:
属性 | 描述 |
---|---|
body |
将请求正文作为字节字符串返回 |
cert |
字典格式的 SSL 证书信息 |
date |
提出请求的时间 |
headers |
请求头字典 |
host |
请求主机 |
method |
HTTP方法 |
params |
参数字典 |
path |
请求路径 |
querystring |
请求参数 |
response |
与请求关联的响应对象 |
url |
请求网址 |
ws_messages |
对于 WebSocket 握手请求,这是发送和接收的所有消息的列表 |
对象的属性response
如下:
属性 | 描述 |
---|---|
body |
将响应主体作为字节字符串返回 |
date |
收到回复的时间 |
headers |
请求头字典 |
reason |
原因短语 |
status_code |
响应的状态码 |
在 Python 中使用 Selenium Wire Proxy 避免被阻塞
不幸的是,在 Python 中使用 Selenium Wire 来抓取网站也可能会阻止您的抓取工具。但是,有一些方法可以避免这种情况,让我们来探索一下吧!
使用带有 Selenium Wire 的代理
代理充当您的机器和您要抓取的网站之间的中间人。您可以隐藏您的 IP 地址并通过路由流量来避免检测。
通过获取用于网络抓取的免费代理并proxy
在字典中指定选项,将代理与 Selenium Wire 结合使用seleniumwire_options
。像这样更新你的脚本:
# ... PROXY = 'http://61.28.233.217:3128' seleniumwire_options = { 'proxy': { 'http': PROXY, 'https': PROXY, }, } # Creates an instance of the chrome driver (browser) driver = webdriver.Chrome( service=service, # seleniumwire options seleniumwire_options=seleniumwire_options, ) # Hit target site driver.get('https://httpbin.org/anything') body = driver.find_element(By.TAG_NAME, 'body') print(body.text) # ...
中指定的代理PROXY
是免费的,可能不适合您。如果是这样,请使用以下格式从Free Proxy List等提供商处获取新的:
<PROXY_PROTOCOL>://<PROXY_IP_ADDRESS>:<PROXY_PORT>
Selenium Wire 中的代理身份验证
某些代理在授予访问权限之前需要凭据,但您可以通过使用以下格式添加用户名和密码来使用 Selenium Wire 执行代理身份验证:
<PROXY_PROTOCOL>://<USERNAME>:<PASSWORD>@<PROXY_IP_ADDRESS>:<PROXY_PORT>
带有凭据的更新脚本如下所示:
# ... PROXY = 'http://username:[email protected]:3128' # ...
错误:带代理的Selenium Wire不工作
在某些情况下,您的代理可能无法使用 Selenium Wire,并且您会看到常见错误ERR_TUNNEL_CONNECTION_FAILED
。在这种情况下,您的代理未运行或无法从您的网络访问,或者您的代理配置不正确。
您可以执行以下操作来修复它:
- 首先通过浏览器 ping 代理以确保它处于活动状态。如果不是,请检查您的代理服务器的 IP 和端口是否正确,以及是否没有防火墙阻止您与代理服务器的连接。
- 如果您仍然遇到错误,则需要使用不同的代理服务器或网络。
PROXY
每当您运行脚本时,使用您复制的 URL 更新变量以获得不同的 IP :
# ... PROXY = 'http://<YOUR_ZENROWS_API_KEY>:[email protected]:8001' # ...
在 Selenium Wire 中自定义标头
在网络抓取时使用默认标头可能会被阻止,主要是因为用户代理字符串。但是由于 Selenium Wire 使我们能够拦截请求,我们可以为每个请求设置一个新的自定义 UA。
为此,请使用以下代码更新您的脚本:
# ... # Creates an instance of the chrome driver (browser) driver = webdriver.Chrome( service=service, # seleniumwire options seleniumwire_options={}, ) CUSTOM_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' SEC_CH_UA = '"Google Chrome";v="112", " Not;A Brand";v="99", "Chromium";v="112"' REFERER = 'https://google.com' def request_interceptor(request): # Delete previous UA del request.headers["user-agent"] # Set new custom UA request.headers["user-agent"] = CUSTOM_UA # Delete previous Sec-CH-UA del request.headers["sec-ch-ua"] # set Sec-CH-UA request.headers["sec-ch-ua"] = SEC_CH_UA # Set referer request.headers["referer"] = REFERER driver.request_interceptor = request_interceptor # Hit target site driver.get('https://httpbin.org/anything') # ...
将未检测到的 ChromeDriver 添加到 Selenium Wire
Undetected Chromedriver是一个修补的 ChromeDriver,它避免了网站用来识别和阻止自动流量的流行机器人检测机制。幸运的是,Selenium Wire 集成了 Undetected ChromeDriver,如果在安装它的相同环境中发现的话。
让我们安装补丁驱动程序:
pip install undetected-chromedriver
现在,用打过补丁的驱动程序更新您当前的驱动程序:
# ... import seleniumwire.undetected_chromedriver as uc service = ChromeService(executable_path=ChromeDriverManager().install()) driver = uc.Chrome( service=service, seleniumwire_options={} ) # Hit target site driver.get('https://scrapeme.live/shop/') body = driver.find_element(By.TAG_NAME, 'body') print(body.text) driver.quit()
您的脚本将像以前一样工作,但具有一组机器人检测绕过功能。
但是,它会在具有高级机器人检测功能的网站上失败,例如G2。
让我们看看抓取 G2 时会发生什么:
from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By import seleniumwire.undetected_chromedriver as uc from selenium.webdriver.support.wait import WebDriverWait service = ChromeService(executable_path=ChromeDriverManager().install()) driver = uc.Chrome( service=service, seleniumwire_options={} ) # Hit target site driver.get('https://g2.com') # Wait for element with a class of "off-canvas-wrapper" WebDriverWait(driver, timeout=300).until(lambda d: d.find_element(By.CLASS_NAME,"off-canvas-wrapper")) body = driver.find_element(By.TAG_NAME, 'body') print(body.text) driver.quit()
因此,解决此问题的更好方法是使用 ZenRows。
这是您需要做的。
从安装开始requests
:
pip install requests
然后,执行以下脚本(代码由 ZenRows 在激活高级代理、反机器人和 JS 渲染功能时在其应用程序主页上自动生成):
import requests apikey = '<YOUR_ZENROWS_API_KEY>' zenrows_base_url = 'https://api.zenrows.com/v1/' target_url = 'https://g2.com' params = { 'apikey': apikey, 'url': target_url, 'antibot': True, 'premium_proxy': True, 'wait_for': True, # wait for an element with class of "off-canvas-wrapper" which is in the homepage 'wait_for': '.off-canvas-wrapper' } response = requests.get(zenrows_base_url, params=params) print(response.text) # G2 home page HTML
使用您的API 密钥更新<YOUR_ZENROWS_API_KEY>
并运行脚本。
优化Selenium Wire
拦截流量使您能够检查浏览器发送和接收的请求和响应。这对于优化 Selenium Wire 很有用,Selenium Wire 需要平衡流量拦截和浏览器配置文件配置。
早些时候,我们拦截了自定义标头的请求,但我们也可以为资产(例如图像)执行此操作,因为我们只是在抓取文本。
如下所示更新您的脚本以提高抓取速度以避免发出图像请求:
# ... service = ChromeService(executable_path=ChromeDriverManager().install()) # Creates an instance of the chrome driver (browser) driver = webdriver.Chrome( service=service, # seleniumwire options seleniumwire_options={}, ) def request_interceptor(request): # Block image assets if request.path.endswith(('.png', '.jpg', '.gif')): request.abort() driver.request_interceptor = request_interceptor # Hit target site driver.get('https://scrapeme.live/shop/') body = driver.find_element(By.TAG_NAME, 'body') print(body.text) # ...
结论
在本教程中,我们介绍了使用 Selenium Wire 的基础知识。现在你知道了:
- 如何使用 Selenium Wire 执行请求。
- 如何使用高级代理及其好处。
- 如何在 Selenium Wire 中自定义标头。
- 如何集成 Undetected ChromeDriver 和更好的替代方案。
- 如何优化Selenium Wire。
您可以访问官方文档以了解更多信息。
尽管该库有助于拦截和修改网络请求和响应,以及模拟不同的场景,但它在具有高级机器人检测功能的站点上效果不佳。