Playwright · Chromium、Firefox、WebKit

Playwright 移动代理配置指南

Playwright 支持在启动时和每个浏览器上下文中设置代理,因此它是最容易接入移动代理池的自动化框架——前提是您知道凭证要放在 proxy 对象里,而不是写在服务器 URL 中。本页介绍真正可用的配置方式、如何把会话映射到上下文,以及如何避免浏览器任务悄悄耗掉大量带宽。

问题所在:云端浏览器会被拦截,代理认证又很繁琐

会先后遇到两个问题。第一,从 CI 或云服务器运行的无头浏览器,仅凭 IP 就能被识别——整个 IP 段都是已知的——因此在您的选择器运行之前,反爬虫机制就已经介入,您花几天时间调整指纹,而问题根本不在指纹。第二,当您开始使用代理时,把凭证直接写进服务器 URL 这种直观做法是行不通的:Playwright 要求用户名和密码作为单独字段传入,格式错误时只会报一个毫无帮助的连接错误。

如果您遇到过以下情况

  • 同样的 URL,选择器在本地正常,在 CI 中却超时。
  • 添加代理后出现 net::ERR_TUNNEL_CONNECTION_FAILED,或在 goto() 处卡住。
  • 返回的是验证页、拦截页或空壳页面,而不是真实内容。
  • 因为所有资源都会加载,带宽费用随页面体积同步增长。
  • 并行 worker 共用同一个出口,彼此互相干扰。

可运行的代码

只要 PROXY_URL 中填入真实的连接字符串,下面的每段代码都能直接运行。首次充值后,可在控制台复制您的连接字符串。

设置连接 URL

bash
export PROXY_URL="http://USERNAME-mbl-us-sid-pw01-rot-sticky:[email protected]:7000"

# 每个浏览器上下文一个 -sid-。并行 worker 需要使用不同的 sid。

启动级代理(正确的认证写法)

javascript
import { 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 很重要:用户名包含带连字符的参数,密钥可能经过百分号编码。

上下文级代理 — 一个浏览器,多个会话

javascript
import { 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% 带宽

javascript
const 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.803%
25 GB$150$614%
50 GB$283$5.6619%
100 GB$500$529%

适用国家

修改代理用户名中的一个参数即可切换国家——无需重新部署,也无需第二个账户。 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,足够您在确定预算之前,先实际计量一次运行。