知识

如何在 Selenium 中使用住宅代理(含需认证的代理)

Selenium 设代理主机很容易,但没有内建方式来传凭据。如何用 Selenium Wire、一个扩展、或 CDP 来处理需认证的代理。

Chris Collins

Chris Collins

2026年8月7日 · 2 分钟阅读

Selenium 是现存部署最广的浏览器自动化工具,抓一个重 JavaScript 的目标,它能干这活儿。但它有一个由来已久的短板,几乎让每个人第一次接入住宅代理时都栽跟头:设代理主机很平常,而提供用户名和密码却不然——因为 Selenium 没有内建的办法去做。把 Chrome 指向一个需认证的代理,它会弹出一个 Selenium 填不了的原生 407 登录对话框,而你的脚本就挂住了。下面是如何越过它,三种办法。

这与其他浏览器指南并列——用 Playwright 使用住宅代理用 Puppeteer——那两者都原生地处理代理认证。如果你不需要一个完整的浏览器,Python 里的代理配一个朴素的 HTTP 客户端更简单。

下面的一切都使用 Shifter 的住宅 gateway:一个端点 p.shifter.io:443,所有定位都编码在用户名里。换成别家供应商,就换掉主机和凭据;形态是一样的。

一段话讲清 gateway 模型

代理用户名同时承载你的认证你的定位。你不是靠切换端点来换国家或换会话,而是靠改用户名字符串:

customer-USERNAME-country-us-sid-abc123-ttl-600

country-us 定位美国,sid 固定一个粘性会话,ttl 把那个 IP 保持 N 秒。去掉 sid/ttl,每一条新连接都会轮换。密码是恒定的。在 Selenium 里,整个问题就是把那个用户名和密码送到代理去。

核心问题

设主机是容易的那一半。你在 Chrome 选项里传 --proxy-server,和在别处一模一样:

from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument('--proxy-server=http://p.shifter.io:443') # 只有主机
driver = webdriver.Chrome(options=options)

那对一个 IP 白名单、不带凭据的代理管用。但 gateway 是用用户名和密码认证的,而 Chromium 不会从那个 flag 读凭据,所以第一次导航就卡在一个 Selenium 无法关掉的 407 认证提示上。你需要三种办法之一来回应那个挑战。

办法 1:Selenium Wire(容易的那个)

Selenium Wire 扩展了 Selenium,直接接受代理凭据、替你处理认证。它是摩擦最小的选项,也是大多数 Python 抓取器会伸手去取的。

from seleniumwire import webdriver # pip install selenium-wire
import os
user = os.environ['SHIFTER_USER'] + '-country-us' # 定位在用户名里
pw = os.environ['SHIFTER_PASS']
seleniumwire_options = {
'proxy': {
'http': f'http://{user}:{pw}@p.shifter.io:443',
'https': f'http://{user}:{pw}@p.shifter.io:443',
'no_proxy': 'localhost,127.0.0.1',
}
}
driver = webdriver.Chrome(seleniumwire_options=seleniumwire_options)
driver.get('https://api.ipify.org')
print(driver.page_source) # 一个美国住宅 IP
driver.quit()

凭据——连同用户名里的定位标志——进 proxy 配置,而 Selenium Wire 透明地处理那个 407。它还让你在运行时靠重新赋值 driver.proxy 来改代理,这对不重启 Chrome 就轮换很方便。

办法 2:一个凭据扩展(裸 Selenium,无额外库)

如果你想留在裸 Selenium 上,经典技法是加载一个小小的 Chrome 扩展,用你的凭据去回应认证挑战。你即时构建它,并用 add_extension 传入。

# manifest.json 声明代理 + 认证权限;background.js 提供凭据。
background_js = """
chrome.webRequest.onAuthRequired.addListener(
() => ({ authCredentials: { username: USER, password: PASS } }),
{ urls: ['<all_urls>'] },
['blocking']
);
""".replace('USER', repr(user)).replace('PASS', repr(pw))
# 把 manifest.json + background.js 打成 zip,然后:
options.add_extension('proxy_auth.zip')

这让你保持零依赖,并且在任何 Selenium 语言绑定里都能用,因为活儿是扩展干的。要注意的是:上面那个阻塞式的 onAuthRequired 模式是一个 Manifest V2 技法,而 Chrome 正在把 MV2 淘汰、转向 MV3,所以在当前的 Chrome 上这个办法比过去更脆。如果你是从头开始,就优先用 Selenium Wire 或下面的 CDP 路子。

办法 3:Selenium 4 里的 CDP

Selenium 4 暴露了 Chrome DevTools Protocol,你可以通过 Fetch 域来回应代理认证——处理 Fetch.authRequired 并带着凭据把请求继续下去。它对现代 Selenium 是原生的、不需要额外的包,但手动接线很琐碎,本质上是在重新实现 Selenium Wire 已经封好的东西。当你想要零第三方依赖、并且对 CDP 熟悉时再去用它;否则 Selenium Wire 替你省了这份麻烦。

轮换地理与会话

因为定位住在用户名里,不同的身份就是不同的用户名,而在 Selenium 里代理是在浏览器级别设置的。这意味着轮换是按 driver 发生的、而不是按标签页。两个实用模式:用 Selenium Wire,在运行时重新赋值 driver.proxy,好在工作单元之间换用户名;用扩展或 CDP 办法,则每个身份跑一个 driver、并把它们做成池。无论哪种,都给每个逻辑工作单元它自己的 sid,并在单元之间轮换、而不是在会话中途轮换(粘性 vs 轮换讲了这个区分),并按负载均衡那篇所述把工作映射到身份。

复用 driver,并给并发封顶

启动 Chrome 很贵——每个请求都开一个新鲜 driver,每次都要付真实的启动时间和内存,正是延迟指南存在的意义所要消除的那部分开销。开一个 driver(或它们的一个小池)并跨请求复用它。而因为每个 driver 都是一个占着真实内存的完整浏览器,你不能跑成千上万个,保持一个有界的池,并按目标主机给在途工作封顶,好让脆弱的站点不被猛击、宽松的站点也不被饿死。越过一个目标的容忍度之后,更多并行买来的是封锁和内存溢出崩溃,而不是吞吐量(如何避免被封)。

浏览器只是”不被封”的一半

一个住宅 IP 处理了”看起来像人”的网络那一半,但 Selenium 驱动的仍是一个自动化浏览器,而站点也会对它做指纹——navigator.webdriver、自动化标志、以及 headless 的怪癖。一个有良好信誉的干净 IP 能让你避开很多挑战,但它并不能给一个明显自动化的浏览器打掩护。把 user-agent 和视口保持真实,以人的节奏驱动页面,并记住那些触发检测的错误对浏览器这一层和对 IP 这一层同样适用。这两者必须对得上。

验证你确实在走代理

在给别的任何东西做基准或调试之前,从浏览器内部确认出口 IP:

driver.get('http://ip-api.com/json')
print(driver.find_element('tag name', 'body').text) # 期望是目标国家

返回你自己的 IP,说明代理没被应用。卡在一个登录对话框上,说明认证那一步(Selenium Wire、扩展、或 CDP)缺失或配错了。整体挂住,说明本地出站被挡了。这三种都在超时诊断指南里有讲。

常见问题

为什么 Selenium 会卡在一个代理登录弹窗上? Chromium 会为一个需认证的代理弹出一个原生的 407 认证对话框,而 Selenium 无法与浏览器的原生对话框交互。你必须用别的办法回应这个挑战:Selenium Wire、一个凭据扩展、或 CDP 的 Fetch.authRequired。只设 --proxy-server 只提供主机、不提供凭据。

我能把 user:pass@host 放进 --proxy-server 吗? 不能。Chromium 不从 --proxy-server flag 里读凭据。在那里提供主机,并通过上面三种办法之一供给用户名和密码。因为 gateway 把定位编码在用户名里,那个完整用户名(带 -country-...)就是你作为代理用户名传的东西。

我一定得用 Selenium Wire 吗? 不必,但在 Python 里它是最简单的路。零依赖的替代方案是一个凭据扩展(在任何语言绑定里都能用,尽管经典的 MV2 模式正在被淘汰)或 Selenium 4 里的 CDP(原生、但接线工作更多)。

在 Selenium 里我怎么轮换 IP? 变换代理用户名,这会经同一个 gateway 改变身份。在 Selenium Wire 里你可以在运行时重新赋值 driver.proxy;否则每个身份跑一个 driver 并把它们做成池。在用户名里去掉 sid,就能在每条新连接上轮换。

Selenium、Playwright,还是 Puppeteer? Playwright 和 Puppeteer 都原生地接收代理凭据,所以它们避开了这整套舞步;Selenium 需要这里的某个变通办法。如果 Selenium 正是你技术栈已经在用的,它仍是个不错的选择;如果你从头开始、又想要无痛的代理认证,另外那两个更顺滑。

底线

一旦你解决了它那唯一的短板,Selenium 加住宅代理就跑得很好:它会在 --proxy-server 里接收代理主机,但为一个需认证的代理供给凭据时它需要帮忙。用 Selenium Wire 求摩擦最小,用一个凭据扩展来保持零依赖,或用 Selenium 4 里的 CDP 走原生路子。然后靠变换用户名来轮换地理与会话,复用一个长生命周期的 driver,给你的并发封顶(因为每一个都是一个真实的浏览器),并把浏览器指纹保持得和 IP 一样像人。

把这些做对,Selenium 就能搞定那些朴素 HTTP 客户端搞不定的、交互式的、重 JavaScript 的目标。把它指向住宅 gateway,并记住池的质量决定了你到底会不会频繁被挑战(IP 信誉)。定价页面有按 GB 计费的套餐,可以拿它对着你自己的目标试用。

准备好开始了吗?

试用 Shifter 住宅代理,205M+ 个 IP,195+ 个国家,低至 $0.75/GB。

立即开始