快速回答
如何在Selenium中使用带用户名和密码的代理?
运行一个添加Proxy-Authorization头的小型本地转发程序,并用--proxy-server=http://127.0.0.1:8899将Chrome指向它。Chrome忽略该参数内的凭证,而Selenium没有跨平台代理密码方案。CrawlRelay的住宅代理在gw.crawlrelay.com:7777上这样使用,基于US$0.50/GB,支持加密货币支付,免KYC。
Selenium设置快速概览
| 为什么用转发器? | Chrome的--proxy-server标记不支持用户名和密码 |
|---|---|
| 转发器 | 监听127.0.0.1:8899,添加Proxy-Authorization,转发到gw.crawlrelay.com:7777 |
| Chrome选项 | --proxy-server=http://127.0.0.1:8899 |
| 轮换 | 每个新CONNECT隧道都是一个新连接,通常代表新出口;为同一IP添加会话ID |
| 更简洁的替代方案 | Playwright 直接获取用户名和密码 |
本地转发器是如何工作的?
它接收浏览器的代理请求,插入一个包含您凭据的头部,然后双向复制字节。对于 HTTPS 网站,浏览器发送 CONNECT host:443,网关响应 200 Connection established,TLS 则在 Chrome 和网站之间端到端运行。转发器永远不会看到明文的页面内容。
这个精简版仅处理 HTTPS 网站,每条连接携带一个 CONNECT 隧道。几乎涵盖了今天的所有网站。请将它绑定到 127.0.0.1,确保没有其他机器能使用您的凭据。
import asyncio
import base64
import os
UPSTREAM = ("gw.crawlrelay.com", 7777)
USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
AUTH = b"Proxy-Authorization: Basic " + base64.b64encode(("%s:%s" % (USER, PASSWORD)).encode())
async def pipe(reader, writer):
try:
while True:
data = await reader.read(65536)
if not data:
break
writer.write(data)
await writer.drain()
except ConnectionError:
pass
finally:
writer.close()
async def handle(client_reader, client_writer):
try:
head = await client_reader.readuntil(b"\r\n\r\n") # request line and headers from the browser
except asyncio.IncompleteReadError:
client_writer.close()
return
lines = head.split(b"\r\n")
lines.insert(1, AUTH) # add the credentials right after the request line
up_reader, up_writer = await asyncio.open_connection(*UPSTREAM)
up_writer.write(b"\r\n".join(lines))
await up_writer.drain()
await asyncio.gather(pipe(client_reader, up_writer), pipe(up_reader, client_writer))
async def main():
server = await asyncio.start_server(handle, "127.0.0.1", 8899)
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main())如何让 Selenium 指向转发器?
在一个终端启动 forwarder.py,然后用本地代理地址运行您的 Selenium 脚本。Selenium 4.6 及以上内置了 Selenium Manager,自动下载匹配的 ChromeDriver,无需额外安装。
from selenium import webdriver
from selenium.webdriver.common.by import By
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://127.0.0.1:8899") # the local forwarder
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
driver.set_page_load_timeout(60)
driver.get("https://api.ipify.org")
print("exit IP:", driver.find_element(By.TAG_NAME, "body").text)
finally:
driver.quit()如何为每个 Selenium 会话获取一个 IP,或者为每个页面获取新 IP?
转发器会发送您提供的任何用户名,会话行为由用户名决定:
- 每连接轮换: 不带会话部分(
USERNAME-country-us)。Chrome 每个页面会打开多个连接,所以单页可能通过多个出口加载。 - 每个 Selenium 会话一个 IP: 在启动转发器前,将
CRAWLRELAY_USER设置为USERNAME-country-us-session-run42-ttl-30。之后的每个连接都会使用相同 IP,最长 30 分钟;IP 也可能更早更换。 - 多个会话并行: 为每个会话启动一个转发器,监听不同本地端口(如 8899 和 8900),并给每个 WebDriver 分配各自的端口。
Selenium-wire 或浏览器扩展怎么样?
selenium-wire 是一个通过拦截流量添加代理认证的流行包,但其作者在 2024 年 1 月已归档项目且不再维护。响应代理认证请求的浏览器扩展也可用,但必须打包并加载到每个配置文件中。上述转发器无任何依赖,适用于 Selenium 驱动的任意浏览器。对于新项目,Playwright 更加简单:它直接接受代理用户名和密码。
Selenium 与代理:常见问题
为什么在 Selenium 中 Chrome 代理会弹出登录窗口?
因为代理要求凭据,但 Chrome 没有:其 --proxy-server 参数只接受主机和端口。在无头模式下也会出现类似的 ERR_TUNNEL_CONNECTION_FAILED 问题。请改用添加凭据的本地转发器,或者切换到能接受用户名和密码的 Playwright。
转发器会不会拖慢爬虫速度?
几乎不会。它只在两个本地套接字和一个远程套接字之间复制字节,且不解密任何内容。网页加载时间主要取决于住宅代理出口和目标网站,而非运行在同一机器上的转发器。
我可以用 Firefox 替代 Chrome 搭配 Selenium 吗?
可以。通过 FirefoxOptions 设置 Firefox 代理偏好:将 network.proxy.type 设为 1,并将 network.proxy.http 和 network.proxy.ssl 设置为端口为 8899 的 127.0.0.1。保持转发器在前面:Firefox 会弹出代理凭据对话框,Selenium 无法可靠填写。
我的密码在转发器里安全吗?
只要转发器监听于 127.0.0.1,密码就保存在您的机器上,如示例所示。请从环境变量读取密码,绝不提交到版本库,并为每个爬虫使用专用子用户且流量有限制,以防密码泄露影响您的整体计划。
让 Selenium 使用住宅代理出口
US$1.50 用于测试 1 GB,US$0.50/GB 来自 50 GB。