如何使用selenium wire

如何使用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的导入更新为:webdriverseleniumseleniumwire

from seleniumwire import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
# ...

运行由 Selenium Wire 提供支持的脚本,您将在新的浏览器实例中看到 ScrapeMe 站点:

medium_scrapme_browser_instance_485806416d

关闭后,您将在终端中看到页面文本:

文本内容输出

解析对 JSON 的响应

ScrapeMe 包含一个 Pokémon 列表及其各自的价格,我们会将它们抓取到一个 JSON 对象中。

使用该类woocommerce-loop-product__title获取每个生物的名称,并使用该类woocommerce-Price-amount获取其单独的价格。

medium_inspect_one_pokemon_b9593f155b

通过遍历所有 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()

运行脚本,您应该会在终端上看到如下输出:

medium_image7_fa7bc16e92

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
[/su_table]

了解请求和响应对象

Selenium 提供对表示 HTTP 请求和响应的两个对象的访问:requestresponse对象。

您可以在下面看到对象的属性request

属性 描述
body 将请求正文作为字节字符串返回
cert 字典格式的 SSL 证书信息
date 提出请求的时间
headers 请求头字典
host 请求主机
method HTTP方法
params 参数字典
path 请求路径
querystring 请求参数
response 与请求关联的响应对象
url 请求网址
ws_messages 对于 WebSocket 握手请求,这是发送和接收的所有消息的列表
[/su_table]

对象的属性response如下:

属性 描述
body 将响应主体作为字节字符串返回
date 收到回复的时间
headers 请求头字典
reason 原因短语
status_code 响应的状态码
[/su_table]

在 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)

# ...

运行脚本,您将获得一个与您机器不同的 IP 地址。

medium_selenium_wire_proxy_d4d9c93c5e

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。在这种情况下,您的代理未运行或无法从您的网络访问,或者您的代理配置不正确。

您可以执行以下操作来修复它:

  1. 首先通过浏览器 ping 代理以确保它处于活动状态。如果不是,请检查您的代理服务器的 IP 和端口是否正确,以及是否没有防火墙阻止您与代理服务器的连接。
  2. 如果您仍然遇到错误,则需要使用不同的代理服务器或网络。

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')

# ...

运行脚本,您会看到您的 UA 将更改为您设置的值。

medium_customize_user_agent_de9ca051a9

将未检测到的 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()

G2 将我们检测为机器人,这使得脚本无法通过人工验证页面。

medium_g2_human_verification_ab28c9c539

当达到超时时,我们的刮板会失败。

medium_timeout_reached_a9aca94e54

因此,解决此问题的更好方法是使用 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>并运行脚本。

您现在可以抓取页面了:

medium_image2_f40f8d3d6b

优化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。

您可以访问官方文档以了解更多信息。

尽管该库有助于拦截和修改网络请求和响应,以及模拟不同的场景,但它在具有高级机器人检测功能的站点上效果不佳。

类似文章