Puppeteer · Chrome DevTools 协议

Puppeteer 移动代理配置指南

Puppeteer 通过启动参数把代理传给 Chrome,而 Chrome 不接受写在该参数中的凭证——这就是为什么最直观的写法只会弹出一个代理认证对话框并卡住,而不是打开页面。正确的做法分两步:启动参数中只写服务器地址,凭证通过 page.authenticate 传入。其余一切都由此展开。

问题所在:--proxy-server 不接受密码

Chrome 的 --proxy-server 参数只接受协议、主机和端口,别无其他。把凭证写进去,Chrome 会直接忽略,上游返回 407,而 Puppeteer 会停在一个无头模式下看不见的认证弹窗上——所以症状是页面导航永远不结束,而不是一个指明原因的报错。更深一层,促使大家使用 Puppeteer 的那个问题依然存在:从云服务器 IP 运行的无头 Chrome 很容易被识别,因此您自动化操作的内容,可能并不是真实访客看到的内容。

如果您遇到过以下情况

  • 在 --proxy-server 中加入凭证后,goto() 一直卡到超时,没有任何报错。
  • 出现 ERR_INVALID_AUTH_CREDENTIALS,或网络日志中出现无声的 407。
  • 认证在第一个页面有效,之后的每个页面都失败。
  • 新页面或弹窗绕过了您在第一个页面上设置的代理认证。
  • 因为没有拦截任何请求,带宽费用随完整页面体积增长。

可运行的代码

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

设置连接 URL

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

# 每个会话一个浏览器实例。Chrome 在整个进程范围内应用代理参数,
# 所以第二个身份就需要第二个浏览器。

正确写法:参数指定主机,authenticate 传入凭证

javascript
import puppeteer from 'puppeteer';

const url = new URL(process.env.PROXY_URL);

const browser = await puppeteer.launch({
  headless: 'new',
  args: [
    // 这里只写主机和端口——Chrome 会忽略写在这里的凭证。
    `--proxy-server=http://${url.host}`,
  ],
});

const page = await browser.newPage();

// 大多数示例都漏掉了这一步。
await page.authenticate({
  username: decodeURIComponent(url.username),
  password: decodeURIComponent(url.password),
});

await page.goto('https://api.ipify.org?format=json', { waitUntil: 'domcontentloaded' });
console.log(await page.evaluate(() => document.body.innerText));

await browser.close();

page.authenticate 是按页面生效的。每个新页面或弹窗都需要单独调用——见下文。

为每个页面认证,包括弹窗

javascript
const creds = {
  username: decodeURIComponent(url.username),
  password: decodeURIComponent(url.password),
};

// 捕获网站自行打开的页面,否则它们会遇到一个您看不到的 407。
browser.on('targetcreated', async (target) => {
  if (target.type() !== 'page') return;
  const p = await target.page();
  if (p) await p.authenticate(creds);
});

const page = await browser.newPage();
await page.authenticate(creds);

没有 targetcreated 钩子时,目标网站的一次 window.open 就会让任务悄悄中断。

拦截请求——流量按 GB 计费

javascript
await page.setRequestInterception(true);

page.on('request', (req) => {
  if (['image', 'media', 'font', 'stylesheet'].includes(req.resourceType())) {
    return req.abort();
  }
  return req.continue();
});

// 使用移动端视口,让网站向您的移动 IP 返回移动版页面。
await page.setViewport({ width: 390, height: 844, isMobile: true, deviceScaleFactor: 3 });
await page.goto('https://target.example/product/123', { waitUntil: 'domcontentloaded' });

必须在 goto 之前启用拦截,并且每个请求都必须恰好调用一次 abort 或 continue。

应该使用哪种轮换模式

推荐-rot-sticky

Chrome 把代理作为进程级启动参数,整个浏览器共用一个出口——在 Puppeteer 中,身份的最小单位是浏览器实例,而不是页面。因此粘性会话是最自然的选择:用 -rot-sticky 和独立的 -sid- 固定浏览器,完成整个会话后再关闭它。需要不同身份时,用不同的 sid 启动第二个浏览器,而不是试图在运行中的 Chrome 底下切换出口——那会让您已建立的会话状态全部失效。

何时选择其他模式

模式适用场景
auto10执行无状态任务的短时浏览器,每次启动只处理少量相互独立的页面。
hard持续数小时、不能更换设备的长时间监控会话。
auto30需要在长时间运行中获得一定 IP 多样性、又不干扰短会话的后台爬取。

预计带宽用量

Puppeteer 驱动的是真实的 Chrome,所以 Chrome 下载什么,它就下载什么。在这里,请求拦截不是可有可无的优化——在按 GB 计费的代理池上,它是您最主要的成本控制手段。

任务类型单次用量每 GB 可完成
典型页面,全部资源,全新配置文件2–5 MB约 200–520 页
同一页面,中止图片/媒体/字体请求400–900 KB约 1,150–2,600 页
同一页面,复用已预热缓存的浏览器150–500 KB约 2,000–7,000 页
屏蔽文档,仅保留 XHR/fetch5–50 KB约 20,000–200,000 次调用

为每个 URL 启动新浏览器会丢掉缓存、重新下载所有资源——这是 Puppeteer 账单超出预估数倍的最常见原因。在一个会话的多个页面之间复用同一个浏览器,积极拦截请求,只在真正出错的页面上截图。以上为商业网站的典型范围,请先对自己的目标计量,再确定预算。

带宽价格每 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 指令和速率限制,以及适用于您的数据保护法律。

本场景的使用要求

  • 只对您拥有或已获授权的网站进行自动化操作。
  • 无头浏览器产生负载的速度远超人类——请主动限速,并遵守 Retry-After。
  • 不得使用自动化批量注册账户、破解不该由您通过的验证码,或绕过访问控制。
  • 尽可能避免合成流量混入他人的分析数据,也绝不要产生非预期的计费事件。

任何套餐均严禁

  • 禁止撞库、盗用账户,或任何冒充他人进行身份验证的行为。
  • 禁止绕过您未获授权测试的访问控制、付费墙或安全措施。
  • 禁止收集您没有合法依据处理的个人数据。
  • 禁止产生合理运营方会视为拒绝服务攻击的负载——请限速,遇到 429 时退避,并遵守 Retry-After。

用于欺诈、入侵或批量滥用的账户将被终止且不予退款,我们会配合有效的法律程序。完整条款请参阅我们的可接受使用政策。

Puppeteer常见问题

为什么把凭证写在 --proxy-server 的 URL 里不起作用?

Chrome 的这个参数只接受协议、主机和端口。写在其中的凭证会被忽略,上游返回 407,而无头 Chrome 会停在一个看不见的认证对话框上——所以症状是卡住而不是报错。请改用 page.authenticate 传入凭证。

每个页面都要调用 page.authenticate 吗?

是的。它按页面生效,而不是按浏览器。请在您创建的每个页面上调用它,并添加 browser.on("targetcreated") 钩子,让网站自行打开的页面也能完成认证。

一个浏览器中的不同页面可以使用不同代理吗?

通过启动参数做不到——Chrome 在整个进程范围内应用该参数,所以一个浏览器只有一个出口。请为每个身份启动单独的浏览器实例,并各自设置 -sid-。如果需要在单个浏览器中按上下文使用不同代理,Playwright 原生支持这一点。

Puppeteer 该用粘性会话还是轮换?

粘性会话。浏览器是身份的最小单位,在其整个生命周期内保存着 cookie 和会话状态,所以出口应在这段时间内保持固定。需要新身份时,用新的 sid 启动新的浏览器。

如何降低带宽消耗?

在导航之前启用 setRequestInterception,中止图片、媒体、字体和样式表请求,然后复用同一个浏览器以保持缓存有效。这通常能减少 70–80% 的流量。

可以配合 puppeteer-extra 和 stealth 插件使用吗?

可以——代理配置使用的是普通的 Puppeteer 启动和页面 API,所以封装了启动器的插件会自动继承。代理和指纹是两个独立的问题,而运营商 IP 解决的恰恰是指纹插件无法解决的那一个。

相关指南

用您自己的目标网站试一试

本页的带宽估算是如实给出的参考范围,而非承诺——唯一重要的数字,是您的目标网站实际消耗的流量。首次充值额外赠送 $2,足够您在确定预算之前,先实际计量一次运行。