结合 Selenium 和 Playwright 的移动端代理
通过经过身份验证的移动代理驱动真实浏览器——Playwright的“按上下文”代理、那个让人白白浪费一个下午的Chrome凭证陷阱,以及如何正确解读407错误代码而非靠猜测。
- Playwright 中的第一类身份验证 — 用户名和密码是参数,而不是权宜之计。
- IP白名单 — 当Selenium从固定主机运行时,这是最稳定的选项。
- 按上下文划分的退出 IP 地址 — 在一个浏览器进程中运行多个身份,且不会相互影响。
- 真实运营商IP地址 — 由实际移动网络支持的移动指纹。
由真实的移动端退出IP支持的移动端视口和区域设置。
在一个进程中运行并行身份,且这些身份不共享同一地址。
仅在没有浏览器就无法获取数据时,才使用真正的浏览器。如果标记内容包含在初始响应中,HTTP 客户端的开销要小一个数量级——Python 指南中已介绍了这种情况。下文将介绍必须运行 JavaScript 以及代理必须能够与浏览器正常交互的情况。
| 工具 | 原生代理身份验证 | 按上下文设置代理 | 结论 |
|---|---|---|---|
| Playwright | 是的——用户名和密码是参数 | 是 | 代理服务器需要凭据时的默认选项 |
| Selenium | 不——URL 中的凭据将被忽略 | 不——每个驱动程序实例仅有一个代理 | 在启用IP白名单的情况下可行;否则操作起来比较麻烦 |
| Puppeteer | 部分实现——通过 page.authenticate() | 按浏览器计算,而非按上下文计算 | 对于已经使用该功能的 Node 堆栈来说没问题 |
剧作家:启动时的代理,按上下文确定的代理
Playwright 将代理凭据视为一等参数,并自行处理身份验证挑战。无需加载任何扩展,也无需注入任何头部:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(proxy={
"server": "http://proxy.pxm2.io:8000",
"username": "user",
"password": "pass",
})
ctx = browser.new_context(
locale="en-GB",
timezone_id="Europe/London",
viewport={"width": 390, "height": 844},
is_mobile=True,
)
page = ctx.new_page()
page.goto("https://example.com/listings", wait_until="domcontentloaded")
page.wait_for_selector(".result-card")
html = page.content()
browser.close()
当在一个进程中运行多个身份时,请按上下文而非按浏览器设置代理——new_context 接受相同的代理字典,因此每个上下文都会拥有独立的退出 IP、独立的 Cookie 存储和独立的存储空间,彼此之间不会产生交叉污染。 同一上下文中的标签页共享网络堆栈,因此若需独立身份,应创建独立的上下文,而非单独的标签页。
请注意,该代码片段中的上下文选项还承担着另一项功能。设置了 is_mobile 的移动视口、区域设置和时区并非可有可无的装饰——它们构成了一项断言中属于浏览器端的部分,而网络端的部分则是移动设备的出口 IP 地址。如果其中一项正确而另一项错误,其后果比两者都不正确更严重,因为这种不匹配本身就是一种信号。
Selenium 与“经过身份验证的代理”陷阱
Selenium 是一种较早的选项,在决定采用它之前,有几点需要注意:Chrome 的 --proxy-server 开关不接受用户名和密码。如果将它们传递到 URL 中,系统会默默忽略,随后浏览器会卡在原生认证对话框上,而 WebDriver 无法关闭该对话框。 其表现为:脚本在首次导航时会卡住,且完全不会显示任何错误信息。
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://proxy.pxm2.io:8000")
# NOTE: user:pass in the URL above is IGNORED by Chrome.
# Pick one of:
# 1. IP authorisation - allow-list your server IP, use no credentials
# 2. selenium-wire-lw - injects the Proxy-Authorization header
# 3. a small MV3 extension that answers onAuthRequired
driver = webdriver.Chrome(options=opts)
driver.get("https://example.com/listings")
当您的爬虫从固定主机运行时,IP授权是这三种方式中最稳健的。无需传输任何凭据,无需注入任何内容,也不需要维持任何额外的依赖项。
如果您要使用 Selenium Wire,请确认您正在安装的是哪个包。原始的 wkeeling/selenium-wire 仓库已于 2024 年 1 月归档,且不再维护;已固定的配置仍可正常工作,但随着依赖项的更新,新搭建的环境往往会出现问题。 正在维护的 selenium-wire-lw 分支保留了相同的 seleniumwire 导入,是新项目应采用的版本。
如果你主要是出于习惯才使用 Selenium,而且需要经过身份验证的代理,那么 Playwright 可以彻底解决这个问题——而且这种解决方式比上述三种变通方法都要更持久。
调试 407 错误和代理认证失败
“407 需要代理身份验证”意味着代理服务器收到了您的请求,但因无法验证您的身份而拒绝了该请求。在浏览器自动化操作中,这几乎总是意味着凭据从未送达,而非凭据本身有误。请按以下顺序排查,通常前两个步骤就能找到原因:
- 先让未经过身份验证的shape正常工作
将您的 IP 地址临时加入白名单,或者使用无需凭据的端点,并确认浏览器能否访问目标地址。这样可以区分代理问题和浏览器配置问题,而这两者的调试方法截然不同。
- 先运行“有头”模式,再运行“无头”模式simplified-chinese (Mainland)
在无头模式下,原生身份验证对话框不可见,而在真实窗口中则清晰可见。如果带头模式下显示了凭据提示框,那就说明你找到了它——这表明 Chrome 正在告诉你,该开关从未携带过你的用户名和密码。
- 通过同一浏览器验证出口IP地址
访问一个 IP-echo 服务并读取该地址。如果该地址是您自己的,则说明代理设置被完全忽略了——这是端点字符串格式错误的常见结果,因为大多数协议栈会采取“开放处理”策略,而非抛出异常。
- 只有到那时,才添加身份验证
既然已有通往目标的已知有效路径,那么认证失败现在只有一个可能的原因,而不是四个。
在断定是凭据有问题之前,还有一点值得检查:你所运行的主机是否在服务商的白名单上。服务器迁移后,或者容器启动后获得了新地址,都会导致与密码输入错误完全相同的407错误。
无头泄漏:使浏览器与IP地址匹配
一个干净的移动IP地址,即使搭配默认的无界面浏览器,仍然会显露出是机器人。代理仅解决了由多个层级构成的评分体系中的其中一层,而以下这些层级是它无法触及的:
- 自动化标记 — navigator.webdriver、缺失的插件和编解码器信息,以及一个显示为 SwiftShader 的 WebGL 渲染器,这些都表明这是一个自动化浏览器。请在虚拟显示器下运行 headful,或使用隐身插件,并通过指纹检测页面进行验证,而不是凭空猜测。
- 地理一致性 — 来自英国的IP地址与报告为“America/New_York”且语言为“en-US”的浏览器之间存在不匹配,这属于常规检查项。请在上下文中设置locale和timezone_id,使其与您所在的离开国家/地区相符。
- 设备一致性 — 如果你设置了手机视口和 is_mobile 属性,则退出 IP 应为手机 IP,且 User-Agent 应与这两者均一致。不完整的手机身份信息比完全没有更引人注目。
- 行为 — 系统会测量间隔是否完全均匀、鼠标是否静止以及表单填写是否瞬间完成。代理无法掩盖访问模式;只有改变访问模式才能做到这一点。
在添加另一层规避措施之前,请打开“网络”选项卡。你正在让整个浏览器渲染的页面,其数据通常是从一个内部端点获取的,该端点返回的是纯净的 JSON——你可以直接使用 HTTP 客户端调用该端点,无需浏览器,也完全不会留下任何指纹痕迹。
获取专用浏览器代理
PXM2实时节点——选择您希望目标用户看到请求来自的国家/地区,即可获得一个专属的4G/5G IP地址,该地址提供无限带宽和轮询功能:
法国
印度
新加坡
常见问题解答
如何在 Selenium 中使用需要身份验证的代理?
不能通过命令行实现——Chrome 的 --proxy-server 参数会忽略 URL 中的用户名和密码,随后浏览器会因出现一个 WebDriver 无法关闭的原生对话框而卡住。 有三种可行的解决方案:将您主机的 IP 地址加入白名单以避免输入凭据;使用一个维护良好的 Selenium Wire 分支来注入 Proxy-Authorization 头;或者加载一个小型扩展程序来响应 onAuthRequired 事件。
Playwright 是否支持代理身份验证?
是的,原生支持。launch 和 new_context 都接受一个包含 server、username 和 password 键的代理字典,而 Playwright 会自行处理身份验证。当您的代理需要凭据时,这是选择 Playwright 而非 Selenium 的最明确理由。
“407 需要代理身份验证”是什么意思?
代理服务器接收到了您的请求,但因无法验证您的身份而将其拒绝。在浏览器自动化操作中,这几乎总是意味着凭据从未到达代理服务器,而非凭据本身有误——可能是某个开关被无声地关闭了,某个扩展程序未加载,或者该主机未列入允许列表。
selenium-wire 项目还在维护吗?
原始的 wkeeling/selenium-wire 代码库已于 2024 年 1 月归档,此后便不再维护。已锁定的安装仍可正常运行,但随着依赖项的更新,新环境往往会出现问题。 对于新项目,请使用经过维护的 selenium-wire-lw 分支(该分支保留了相同的 seleniumwire 导入),或者通过将您的 IP 加入白名单或迁移至 Playwright 来完全避免此问题。
我能否为每个浏览器标签页或上下文使用不同的代理?
就上下文而言,是的——Playwright 的 new_context 会使用其自身的代理,因此每个上下文都会获得独立的出口 IP、Cookie 存储和存储空间,彼此之间不会发生数据泄漏。就同一上下文内的各个标签页而言,则不是:同一上下文中的标签页共享网络堆栈。如果您需要独立的身份,请使用不同的上下文。
相关移动代理指南
浏览器自动化是一条成本较高的途径。本系列的其他文章将介绍成本较低的方法,以及如何在最初阶段选择代理层。