# 在 Puppeteer 和无头 Chrome 中使用代理

Chrome 通过启动参数接收代理，Puppeteer 则为每个页面提供凭据。一个浏览器对应一个出口，粘性会话可让该出口持续有效。


## 使用代理启动并进行身份验证

```javascript
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  headless: true,
  args: ["--proxy-server=http://res.proxshift.com:9000", "--no-first-run"],
});
const page = await browser.newPage();
await page.authenticate({ username: "USER-cc-us-sid-run1-ttl-30m", password: "PASS" });

await page.goto("https://api.ipify.org", { waitUntil: "domcontentloaded", timeout: 45000 });
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
```

每次打开页面后，都要在首次导航前调用 `page.authenticate`；Chrome 会按页面请求代理凭据。用户名中包含国家/地区和会话 ID，因此在设定的有效期内，整个浏览器都会使用同一个家庭住宅出口。


## 每个浏览器使用一个代理

Chrome 会将 `--proxy-server` 应用于整个浏览器实例。若需要多个国家/地区或会话，请运行多个浏览器（无头模式的开销很低），或建立 `puppeteer.launch` 工作池，让每个 worker 持有一个会话 ID。声称支持按页面代理的库会插入本地转发器；每个会话使用一个浏览器更为简单。


## 屏蔽图片、字体和媒体

```javascript
await page.setRequestInterception(true);
page.on("request", (req) => {
  const t = req.resourceType();
  if (["image", "media", "font", "stylesheet"].includes(t)) return req.abort();
  req.continue();
});
```


## 保持环境一致

- 将 `Accept-Language` 和时区设为出口所在国家/地区：使用 `page.setExtraHTTPHeaders({\"Accept-Language\": \"de-DE,de;q=0.9\"})` 和 `page.emulateTimezone(\"Europe/Berlin\")`。
- 在启动参数中加入 `--force-webrtc-ip-handling-policy=disable_non_proxied_udp`，防止 WebRTC 泄漏。
- 每个账号使用一个浏览器；若账号需要表现为曾经访问过的设备，请复用其用户数据目录。


## 常见错误

| 现象 | 原因 | 解决方法 |
| --- | --- | --- |
| net::ERR_TUNNEL_CONNECTION_FAILED | 网关拒绝 CONNECT：凭据或参数错误，或没有可用出口 | 使用 cURL -v 重现问题并读取网关状态码 |
| net::ERR_PROXY_AUTH_UNSUPPORTED / 弹出提示 | 在导航后才调用 authenticate() | 在 goto 前调用 page.authenticate |
| 每个浏览器都显示相同 IP | 重复使用了同一个会话 ID | 每个浏览器使用不同的 -sid-，或将其省略 |
| 首次加载非常慢 | 家庭住宅出口加上资源繁重的页面 | 屏蔽资源；将超时提高至 45 秒 |


## 问题

**Puppeteer 可以使用带密码的 SOCKS5 吗？**

Chrome 不支持 SOCKS5 身份验证。请配合 page.authenticate 使用 9000 端口的 HTTP 代理，或将服务器 IP 加入白名单，并传入 --proxy-server=socks5://res.proxshift.com:9001。

来源：https://proxshift.com/zh/guides/puppeteer-proxy
