Playwright 移动代理配置指南
Playwright 支持在启动时和每个浏览器上下文中设置代理,因此它是最容易接入移动代理池的自动化框架——前提是您知道凭证要放在 proxy 对象里,而不是写在服务器 URL 中。本页介绍真正可用的配置方式、如何把会话映射到上下文,以及如何避免浏览器任务悄悄耗掉大量带宽。
问题所在:云端浏览器会被拦截,代理认证又很繁琐
会先后遇到两个问题。第一,从 CI 或云服务器运行的无头浏览器,仅凭 IP 就能被识别——整个 IP 段都是已知的——因此在您的选择器运行之前,反爬虫机制就已经介入,您花几天时间调整指纹,而问题根本不在指纹。第二,当您开始使用代理时,把凭证直接写进服务器 URL 这种直观做法是行不通的:Playwright 要求用户名和密码作为单独字段传入,格式错误时只会报一个毫无帮助的连接错误。
如果您遇到过以下情况
- 同样的 URL,选择器在本地正常,在 CI 中却超时。
- 添加代理后出现 net::ERR_TUNNEL_CONNECTION_FAILED,或在 goto() 处卡住。
- 返回的是验证页、拦截页或空壳页面,而不是真实内容。
- 因为所有资源都会加载,带宽费用随页面体积同步增长。
- 并行 worker 共用同一个出口,彼此互相干扰。
可运行的代码
只要 PROXY_URL 中填入真实的连接字符串,下面的每段代码都能直接运行。首次充值后,可在控制台复制您的连接字符串。
设置连接 URL
bashexport PROXY_URL="http://USERNAME-mbl-us-sid-pw01-rot-sticky:[email protected]:7000"
# 每个浏览器上下文一个 -sid-。并行 worker 需要使用不同的 sid。启动级代理(正确的认证写法)
javascriptimport { chromium } from 'playwright';
const url = new URL(process.env.PROXY_URL);
const browser = await chromium.launch({
proxy: {
// 这里只写主机和端口。凭证不要放在这个字符串里。
server: `http://${url.host}`,
username: decodeURIComponent(url.username),
password: decodeURIComponent(url.password),
},
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body')); // 您的运营商出口 IP
await browser.close();decodeURIComponent 很重要:用户名包含带连字符的参数,密钥可能经过百分号编码。
上下文级代理 — 一个浏览器,多个会话
javascriptimport { chromium } from 'playwright';
const base = new URL(process.env.PROXY_URL);
function proxyForSession(sid) {
return {
server: `http://${base.host}`,
username: decodeURIComponent(base.username).replace(/-sid-[a-z0-9_]+/, `-sid-${sid}`),
password: decodeURIComponent(base.password),
};
}
// 只启动一次浏览器;通过各自的 sid 为每个上下文分配独立设备。
const browser = await chromium.launch();
const sids = ['job-a', 'job-b', 'job-c'];
const contexts = await Promise.all(
sids.map((sid) => browser.newContext({ proxy: proxyForSession(sid) })),
);
for (const [i, ctx] of contexts.entries()) {
const page = await ctx.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(sids[i], await page.textContent('body'));
}
await browser.close();"一个上下文对应一个会话"是正确的思路:上下文隔离 cookie 和存储,sid 隔离设备。
通过拦截请求节省 70–80% 带宽
javascriptconst context = await browser.newContext({ proxy: proxyForSession('job-a') });
// 流量按 GB 计费,所以不要下载您不会用到的内容。
await context.route('**/*', (route) => {
const type = route.request().resourceType();
if (['image', 'media', 'font', 'stylesheet'].includes(type)) {
return route.abort();
}
return route.continue();
});
const page = await context.newPage();
await page.goto('https://target.example/product/123', { waitUntil: 'domcontentloaded' });
console.log(await page.textContent('[data-testid="price"]'));如果需要断言计算后的布局或截图,请把 stylesheet 从列表中移除。
应该使用哪种轮换模式
-rot-sticky浏览器上下文就是一个会话:它保存着 cookie、本地存储,通常还有登录状态。如果出口 IP 在它底下发生变化,网站会认为这个会话"瞬移"了,并经常使其失效——表现为测试不稳定或爬取中途被登出,而不是明显的代理错误。请为每个上下文使用 -rot-sticky 并设置独立的 -sid-,让上下文的生命周期与 IP 的生命周期保持一致。需要新身份时,用新的 sid 创建新的上下文,而不是在旧上下文下轮换 IP。
何时选择其他模式
| 模式 | 适用场景 |
|---|---|
| auto10 | 每个页面相互独立、无需保持会话的无状态爬取——例如批量检查链接。 |
| hard | 完全不能更换设备的长时间上下文,例如持续数小时的监控会话。 |
| auto30 | 后台爬取,缓慢的 IP 更替可以增加 IP 多样性,又不会干扰短会话。 |
预计带宽用量
真实浏览器会下载的内容,Playwright 都会下载——这正是它的意义所在,也是成本所在。在按 GB 计费的代理池上,请求拦截是脚本中性价比最高的一行代码。
| 任务类型 | 单次用量 | 每 GB 可完成 |
|---|---|---|
| 典型页面,全部资源,无缓存 | 2–5 MB | 约 200–520 页 |
| 同一页面,屏蔽图片/媒体/字体 | 400–900 KB | 约 1,150–2,600 页 |
| 同一页面,上下文缓存已预热(重复访问) | 150–500 KB | 约 2,000–7,000 页 |
| 仅拦截 API(同时屏蔽文档) | 5–50 KB | 约 20,000–200,000 次调用 |
在多次页面跳转之间复用上下文,保持缓存有效——为每个页面启动新浏览器会重新下载所有资源,这是 Playwright 账单超出预估数倍的最常见原因。把预热的上下文与路由拦截结合起来,浏览器任务的流量只会是普通 HTTP 抓取的几倍。以上为商业网站的典型范围,请对您自己的目标进行计量。
| 带宽 | 价格 | 每 GB | 节省 |
|---|---|---|---|
| 1 GB | $7 | $7 | — |
| 5 GB | $35 | $7 | — |
| 10 GB | $68 | $6.80 | 3% |
| 25 GB | $150 | $6 | 14% |
| 50 GB | $283 | $5.66 | 19% |
| 100 GB | $500 | $5 | 29% |
适用国家
修改代理用户名中的一个参数即可切换国家——无需重新部署,也无需第二个账户。 IP 池规模因国家而异,也取决于您使用的是移动(mbl) 还是住宅(peer)IP 池;实时可用情况请查看各国家页面和您的控制台。
仅限合法用途
ProxyMobile 仅用于在您拥有或已获授权测试的系统上进行测量、质量保证和研究。购买带宽并不代表您获得了访问第三方系统的许可。您仍需自行遵守每个目标网站的服务条款、robots 指令和速率限制,以及适用于您的数据保护法律。
本场景的使用要求
- 只对您拥有的网站,或您已获授权测试或采集的网站进行自动化操作。
- 遵守 robots 指令和速率限制——无头浏览器产生负载的速度远超人类,限速是您的责任。
- 不得使用自动化批量注册账户、破解不该由您通过的验证码,或绕过访问控制。
- 当目标网站要求时,如实标明您的自动化身份;收到 Retry-After 时请遵守。
任何套餐均严禁
- 禁止撞库、盗用账户,或任何冒充他人进行身份验证的行为。
- 禁止绕过您未获授权测试的访问控制、付费墙或安全措施。
- 禁止收集您没有合法依据处理的个人数据。
- 禁止产生合理运营方会视为拒绝服务攻击的负载——请限速,遇到 429 时退避,并遵守 Retry-After。
用于欺诈、入侵或批量滥用的账户将被终止且不予退款,我们会配合有效的法律程序。完整条款请参阅我们的可接受使用政策。
Playwright常见问题
为什么把凭证写在 URL 里,Playwright 的代理连接会失败?
因为 Playwright 要求凭证单独传入。server 字段只接受主机和端口——请把凭证放在 proxy 对象的 username 和 password 字段中,并先用 decodeURIComponent 解码,因为连接 URL 是经过百分号编码的。
可以为每个浏览器上下文使用不同的代理吗?
可以,而且这是推荐做法。向 newContext() 传入 proxy,并为每个上下文改写 -sid- 参数,这样每个上下文都有自己的设备,而浏览器只需启动一次。
Playwright 该用粘性会话还是轮换?
几乎所有情况下都用粘性会话。上下文保存着 cookie 和登录状态,如果 IP 在底层轮换,会话看起来就像"瞬移"了,随后会被判定失效。需要新身份时,用新的 sid 创建新的上下文。
如何避免浏览器任务耗费大量带宽?
用 context.route() 屏蔽图片、媒体和字体,并复用上下文以保持缓存有效。两者结合通常能减少 70–80% 的流量,使浏览器任务的消耗只比普通 HTTP 抓取高几倍。
可以在 CI 和 playwright.config 中使用吗?
可以。代理就是一个普通的配置项,您可以在 playwright.config 中按项目分别设置,用同一套测试覆盖多个市场。请为每个并行 worker 设置不同的 -sid-,避免它们共用同一台设备。
Firefox 和 WebKit 也支持吗?
支持。proxy 选项属于 Playwright 通用的启动和上下文 API,并非 Chromium 专属功能,因此同样的配置适用于全部三种浏览器引擎。
相关指南
用您自己的目标网站试一试
本页的带宽估算是如实给出的参考范围,而非承诺——唯一重要的数字,是您的目标网站实际消耗的流量。首次充值额外赠送 $2,足够您在确定预算之前,先实际计量一次运行。
