Selenium配置代理IP的完整指南:从启动参数到认证扩展

市场研究、价格监控、广告验证这类业务任务,很多页面是 JS 动态渲染的,接口拿不到完整内容,只能让真实浏览器去渲染,Selenium 也因此成了这类任务的标配。但任务一旦跨区域执行,就会遇到一个很现实的问题:全部请求从单一出口发出,区域不对、链路不稳,成功率随规模明显下降。这时候就需要代理,让浏览器从目标市场的网络出口发起请求,把负载分散到多个出口上,任务才能长期稳定地跑。

这篇文章从 Selenium 的代理配置原理讲起,覆盖无认证与带认证两种配置方式、代理生效验证、轮换与粘性会话的策略选择,最后给出常见问题排查清单,读完可以直接套用到自己的项目里。

一、Selenium 代理配置的基础

(一)无认证代理

Chrome 系浏览器支持通过启动参数直接指定代理服务器,是最省事的配置方式:

python 复制代码
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--proxy-server=http://gate.b2proxy.com:7777')
driver = webdriver.Chrome(options=options)

Firefox 的配置方式略有不同,通过 preferences 设置:

python 复制代码
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
options = Options()
options.set_preference('network.proxy.type', 1)
options.set_preference('network.proxy.http', 'gate.b2proxy.com')
options.set_preference('network.proxy.http_port', 7777)
options.set_preference('network.proxy.ssl', 'gate.b2proxy.com')
options.set_preference('network.proxy.ssl_port', 7777)
driver = webdriver.Firefox(options=options)

一个容易忽略的点:--proxy-server 只能配置不带认证的代理。网关式代理服务普遍需要用户名密码,直接把 http://user:pass@host:port 写进去通常不会生效,需要下面的方案。

(二)带认证代理

Selenium 本身没有暴露"为代理自动填写认证信息"的接口,社区通行做法是给浏览器装一个本地扩展:扩展负责两件事,把网关地址写入 Chrome 的代理设置,并在请求需要认证时自动填入凭据。

需要注意,Chrome 139 起已不支持 Manifest V2 扩展,所以扩展要按 V3 规范写,权限声明里必须有 webRequestAuthProvider:

python 复制代码
manifest.json:
{
  "name": "proxy-auth",
  "version": "1.0.0",
  "manifest_version": 3,
  "permissions": ["proxy", "webRequest", "webRequestAuthProvider"],
  "host_permissions": ["<all_urls>"],
  "background": { "service_worker": "background.js" }
}
background.js:
chrome.runtime.onInstalled.addListener(() => {
  const config = {
    mode: "fixed_servers",
    rules: {
      singleProxy: {
        scheme: "http",
        host: "gate.b2proxy.com",
        port: 7777
      },
      bypassList: ["localhost", "127.0.0.1"]
    }
  };
  chrome.proxy.settings.set({ value: config, scope: "regular" }, () => {});
});
chrome.webRequest.onAuthRequired.addListener(
  function (details) {
    return {
      authCredentials: { username: "your_username", password: "your_password" }
    };
  },
  { urls: ["<all_urls>"] },
  ["blocking"]
);

用 Python 把这两个文件打包成 zip,通过 add_extension 加载:

python 复制代码
import json
import zipfile
import tempfile
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def build_auth_extension(host, port, username, password):
    manifest = {
        "name": "proxy-auth",
        "version": "1.0.0",
        "manifest_version": 3,
        "permissions": ["proxy", "webRequest", "webRequestAuthProvider"],
        "host_permissions": ["<all_urls>"],
        "background": {"service_worker": "background.js"},
    }
    background = """
chrome.runtime.onInstalled.addListener(() => {
  const config = {
    mode: "fixed_servers",
    rules: {
      singleProxy: { scheme: "http", host: "%s", port: %d },
      bypassList: ["localhost", "127.0.0.1"]
    }
  };
  chrome.proxy.settings.set({ value: config, scope: "regular" }, () => {});
});
chrome.webRequest.onAuthRequired.addListener(
  function (details) {
    return {
      authCredentials: { username: "%s", password: "%s" }
    };
  },
  { urls: ["<all_urls>"] },
  ["blocking"]
);
""" % (host, port, username, password)
    fd, path = tempfile.mkstemp(suffix=".zip")
    with zipfile.ZipFile(path, "w") as z:
        z.writestr("manifest.json", json.dumps(manifest))
        z.writestr("background.js", background)
    os.close(fd)
    return path
ext_path = build_auth_extension(
    host="gate.b2proxy.com",
    port=7777,
    username="your_username",
    password="your_password",
)
options = Options()
options.add_extension(ext_path)
driver = webdriver.Chrome(options=options)

几个要点:

凭据建议从环境变量或配置文件读取,不要硬编码进代码;

密码含 @、#、: 等特殊字符时,先做 URL 编码再填入,这是 407 报错的高发原因;

onAuthRequired 里填错凭据时浏览器会反复重试,表现为像卡住一样,可在回调里按请求 ID 计数,失败两次就停止,方便定位问题;

部分服务商支持 IP 白名单方式(把任务机出口 IP 登记到服务端),可以免去认证环节,直接 --proxy-server 即可,适合出口 IP 固定不变的任务机。

二、验证代理是否真的生效

配置完不等于生效。最直接的办法是让浏览器访问一个回显出口信息的接口(如 ipinfo.io/jsonhttpbin.org/ip),断言出口 IP 和区域是否符合预期:

python 复制代码
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import json
driver.get("https://ipinfo.io/json")
body = WebDriverWait(driver, 15).until(
    EC.presence_of_element_located((By.TAG_NAME, "body"))
).text
info = json.loads(body)
print(info.get("ip"), info.get("country"), info.get("city"))

把这段放到任务启动时跑一次,出口区域不对就立刻中止,比跑完整个任务才发现数据全部来自错误区域要划算得多。

三、轮换与粘性会话:按任务类型选择出口策略

代理配置只是第一步,真正影响任务稳定性的,是出口策略的选择。

网关式代理服务(以 B2Proxy 为例)的接入方式很统一:分配一个固定的网关地址(host:port)和用户名密码,出口 IP 由服务端管理,会话类型在凭据层面配置:

Rotating:每次请求自动更换出口,适合大批量、对出口连续性没有要求的任务,比如列表页遍历;

Sticky:时间窗口内出口保持不变,适合需要会话连续性的流程,比如分步表单、多页面衔接的查询流程。

Selenium 场景有个特殊性:浏览器打开一个页面,会同时发出几十上百个请求(HTML、JS、CSS、图片、接口),这些请求最好来自同一出口。所以 Selenium 任务里更常见的是 Sticky 模式,按会话窗口分配出口,而不是每个请求都换。Sticky 模式下同一凭据默认数分钟内出口不变,到期自动更换。

另外一个和 requests 场景不同的点:requests 可以在同一个请求周期内逐个换代理,Selenium 不行,代理配置是会话级的,在 driver 创建时生效。想换出口,就要结束当前会话、用新配置重建 driver。所以轮换的粒度通常按"任务批次"来:一批页面用一个出口,跑完换下一批,而不是每个页面都重启浏览器。并发多个批次时,每个实例独立创建 driver 和 options,互不影响:

python 复制代码
def build_driver(ext_path):
    options = Options()
    options.add_extension(ext_path)
    return webdriver.Chrome(options=options)
# 每台 worker 一个 driver,出口由服务端凭据分配
driver_a = build_driver(ext_path_a)
driver_b = build_driver(ext_path_b)

需要按地区定向时,多数服务商支持在凭据配置里指定国家/州/城市,生成的凭据即绑定到对应地区出口,代码里不需要额外处理。

四、常见问题排查

1. --proxy-server 配置了但请求没走代理

检查目标页面是 http 还是 https,个别环境下 https 请求需要同时配好代理协议对应的入口;

Chrome 对 localhost、127.0.0.1 等地址有隐式直连规则,排查时别用本机地址做验证目标;

确认浏览器没有加载其他代理插件造成冲突。

2. 407 Proxy Authentication Required

用户名密码错误,或密码中的特殊字符没有做 URL 编码;

认证扩展未加载成功,或扩展缺少 webRequestAuthProvider 权限(MV3 必填)。

3. 认证扩展加载失败

确认 manifest_version 是 3,Chrome 139 起已不支持 V2 扩展,网上很多旧教程的 V2 写法现在已经不生效;

扩展有报错可以在 chrome://extensions 打开开发者模式查看 service worker 日志;

打包的 zip 里只放 manifest.json 和 background.js 两个文件,避免多余目录层级导致加载失败。

4. 出口区域与预期不符

区域定向在服务端凭据层面配置,先确认当前凭据绑定的区域;

部分服务需要在凭据或请求参数里指定地区,检查网关配置对应的区域设置。

5. 会话中断

Sticky 模式下时间窗口到期出口会更换,长任务按窗口切分成多个批次,或者在窗口内完成需要连续性的部分;

浏览器自动更新或崩溃后,driver 会话失效,出口可能变化,任务重跑前重新验证一次出口。

五、小结

Selenium 里的代理配置,本质上是在回答"浏览器从哪里看网页"这个问题。单出口时链路单一、区域受限;接入代理后,出口由服务端管理,区域、会话、轮换都变成了配置项。

具体选型时建议:先明确任务的区域要求和会话连续性要求,再选择 Rotating 还是 Sticky;用回显接口验证出口区域;最后拿小规模流量跑通整条链路再放量。B2Proxy 这类网关式服务支持在官网申请免费测试流量,先用真实任务验证区域覆盖和会话稳定性,比看任何宣传都靠谱。

相关推荐
weixin_440730503 小时前
Playwright介绍、特点、小例子还有与传统selenium的区别
selenium·测试工具·playwright
chuntian_tester15 小时前
测试中的提示词工程
人工智能·测试工具·ai
测试狗科研平台20 小时前
电池材料表征、电芯制备与性能测试--测试狗先进能源中试服务
科技·测试工具·材料工程
啊阿狸不会拉杆2 天前
《计算机网络-自顶向下方法》2.8 小结、课后习题和 Wireshark 实验 读书笔记
计算机网络·测试工具·wireshark·因特网
PS测2 天前
Linux压力测试工具stress,能精确控制 CPU、内存、IO 负载类型和持续时间
测试工具
2601_962387822 天前
web自动化测试实战教程【selenium/unittest/pytest】【共193课
selenium·pytest·devops·web自动化测试·unittest
qianshi12132 天前
涂层划痕测试仪-精准评估涂层附着力,兼顾科研与工业质量检测
经验分享·测试工具·百度
PhotonixBay2 天前
3D共聚焦显微镜与探针式粗糙度仪:谁更适合亚表面损伤检测?
图像处理·人工智能·测试工具·3d
2601_962297252 天前
Python、Pytest、Allure、Selenium和Jenkins实现自动化测试集成实例
python·selenium·jenkins·pytest·allure