# Playwright 代理设置，旋转住宅 IP（Python 和 Node.js）

> Playwright 代理设置（Python 和 Node.js）：为每个浏览器或上下文使用认证住宅代理，支持会话粘滞，页面更轻量。

- URL: https://crawlrelay.com/zh/docs/playwright/
- 最后更新: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

启动时为每个浏览器提供一个出口代理，或为每个上下文提供一个国家代理或一个固定会话代理。然后通过阻止不需要的流量来减少流量消耗。

**快速回答 — 如何在 Playwright 中使用带认证的代理？**

向 `launch()` 或 `new_context()` 传递包含 `server`、`username` 和 `password` 的 `proxy` 对象：`server` 是 `http://gw.crawlrelay.com:7777`，而凭据应单独放在对应字段，绝不放在服务器 URL 中。使用 **CrawlRelay** 住宅代理时，将国家和会话 ID 放入用户名，为每个上下文分配独立出口。流量费用 US$0.50/GB，起步 50 GB。支持加密货币支付，无需 KYC。

## Playwright 快速设置

| 属性 | 值 |
| --- | --- |
| 代理选项 | `{"server": "http://gw.crawlrelay.com:7777", "username": ..., "password": ...}` |
| 位置 | 整个浏览器使用 `launch(proxy=...)`，或每个上下文使用 `new_context(proxy=...)` |
| 端口 | HTTP 端口为 7777；HTTPS 网站通过 CONNECT 访问 |
| SOCKS5 | Chromium 不支持 SOCKS5 认证：请使用 HTTP 端口 |
| 每个上下文一个 IP | 在上下文用户名中使用 `-session-<id>-ttl-<minutes>` |
| 典型页面流量 | 加载所有资源大约 2 MB；阻止图片、字体和媒体后只剩几百 KB |

## 如何给每个浏览器上下文分配各自的国家或会话？

为每个国家或固定会话创建一个上下文。上下文比浏览器轻量，每个上下文独立保存 Cookie、存储和代理凭据。保证上下文的地区设置与出口国家匹配，使网站识别为同一访客。

*Python（同步 API）：每个国家一个上下文*

```python
from playwright.sync_api import sync_playwright

GATEWAY = "http://gw.crawlrelay.com:7777"
LOCALES = {"us": "en-US", "de": "de-DE", "jp": "ja-JP"}

with sync_playwright() as p:
    browser = p.chromium.launch()
    for country, locale in LOCALES.items():
        context = browser.new_context(
            proxy={"server": GATEWAY, "username": "USERNAME-country-%s" % country, "password": "PASSWORD"},
            locale=locale,
        )
        page = context.new_page()
        page.goto("https://api.ipify.org", wait_until="domcontentloaded")
        print(country, page.inner_text("body"))
        context.close()
    browser.close()
```

## 如何在 Node.js 的 Playwright 中设置代理并节省流量？

该选项在 Node.js 中与其他环境相同。渲染页面大部分流量来自图片、字体和媒体：HTTP Archive Web Almanac 2025 移动端的中位页面大小为 2,164 KB。如果仅需要文本和链接，可在路由处理程序中中止这些请求以节省流量。

*Node.js：启动时的粘性会话，阻止大量资源*

```javascript
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server: "http://gw.crawlrelay.com:7777",
    username: "USERNAME-country-us-session-pw1-ttl-30",
    password: "PASSWORD",
  },
});
const context = await browser.newContext();
// Images, fonts and media are most of a page's bytes
await context.route("**/*", (route) =>
  ["image", "font", "media"].includes(route.request().resourceType()) ? route.abort() : route.continue()
);
const page = await context.newPage();
const response = await page.goto("https://example.com/", { waitUntil: "domcontentloaded" });
console.log(response.status(), await page.title());
await browser.close();
```

## 如何使用 asyncio 并行运行多个页面？

使用带有固定上下文池的异步 API。每个上下文在其生命周期内保持一个粘性会话，因此网站看到的是少数稳定访客，而非每个请求一个 IP。在达到设定页面数或出现错误后，回收上下文并生成新会话 ID。

*Python（异步 API）：粘性上下文池*

```python
import asyncio
import uuid

from playwright.async_api import async_playwright

GATEWAY = "http://gw.crawlrelay.com:7777"
WORKERS = 4


async def worker(browser, queue):
    session = uuid.uuid4().hex[:10]
    context = await browser.new_context(proxy={
        "server": GATEWAY,
        "username": "USERNAME-country-us-session-%s-ttl-30" % session,
        "password": "PASSWORD",
    })
    page = await context.new_page()
    while not queue.empty():
        url = await queue.get()
        try:
            response = await page.goto(url, wait_until="domcontentloaded", timeout=45000)
            print(session, url, response.status if response else None)
        except Exception as exc:  # timeouts and network errors: log and move on
            print(session, url, type(exc).__name__)
        finally:
            queue.task_done()
    await context.close()


async def main(urls):
    queue = asyncio.Queue()
    for url in urls:
        queue.put_nowait(url)
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        await asyncio.gather(*(worker(browser, queue) for _ in range(WORKERS)))
        await browser.close()


asyncio.run(main(["https://example.com/", "https://example.org/"]))
```

## 为什么我会遇到 ERR_TUNNEL_CONNECTION_FAILED 或 ERR_PROXY_CONNECTION_FAILED？

- **net::ERR_TUNNEL_CONNECTION_FAILED** 意味着代理未接受 CONNECT 请求。最常见的原因是凭证写在`server`内而非`username`和`password`，导致 Chromium 没有发送。详见[隧道错误](https://crawlrelay.com/zh/docs/errors/err-tunnel-connection-failed/)]。
- **net::ERR_PROXY_CONNECTION_FAILED** 意味着浏览器根本没能连接到网关：主机或端口有错别字，或网络屏蔽该端口。详见[代理连接错误](https://crawlrelay.com/zh/docs/errors/err-proxy-connection-failed/)]。
- `goto`上的“超时 30000ms”通常意味着你正等待`load`或`networkidle`，页面不断加载跟踪器。请先等待`domcontentloaded`，然后等待你需要的选择器。

## Playwright 抓取器不应该做什么？

使用 Playwright 来渲染需要 JavaScript 的公共页面，而非冒充用户。CrawlRelay的[可接受使用政策](https://crawlrelay.com/zh/acceptable-use/)禁止通过我们的网关批量创建账户、接管账户、自动识别 CAPTCHA 或绕过网站刻意设置的屏蔽。我们不提供隐形插件或指纹伪装的文档。如果网站对你的浏览器发起挑战，请放慢速度，查看其 robots.txt 和条款，申请访问权限或使用其 API。

## Playwright 和代理：常见问题

### 我可以为每个 Playwright 上下文使用不同的代理吗？

可以。在 Python 中将`proxy`传递给`browser.new_context()`，在 Node.js 中传递给`browser.newContext()`。每个上下文拥有自己的出口 IP、Cookie 和存储。Windows 上的非常旧版本 Playwright 需要启动时指定代理；如果报错，请用任意占位代理启动，再为每个上下文设置真实代理。

### Playwright 支持带认证的 SOCKS5 代理吗？

Chromium 不支持 SOCKS5 的用户名和密码认证，因此不支持。请使用网关的 HTTP 端口（7777）：它通过 CONNECT 隧道处理 HTTPS 站点，并支持所有 Playwright 控制的浏览器引擎中的用户名密码认证。

### 如何检查 Playwright 页面使用的 IP 和国家？

在目标页面同一上下文中先打开`https://api.ipify.org`，读取响应正文，参考第一个示例。请记住，粘性会话仅在其住宅代理设备保持在线期间绑定该 IP；建议在每个上下文开始时记录 IP 以侦测变化。

### Playwright 页面通过代理使用多少流量？

所有流量都计费：HTML、脚本、样式、图片、字体和 API 调用。完整页面中位流量约 2 MB；阻止图片、字体和媒体可将其减少至几百 KB。通过在`response`处理程序中累计 50 个页面的响应大小，测量你实际页面的流量。

### 抓取时我应该用无头还是有头浏览器？

无头是服务器的常用选择，占用资源更少。有头浏览器仅用于调试页面显示。两者规则相同：以礼貌速度渲染公共页面，尊重网站 robots.txt 和使用条款。

**通过住宅代理渲染公共页面** US$5.94 适用于 10 GB，US$0.50/GB 来自 50 GB。 [查看价格](https://crawlrelay.com/zh/pricing/) · [Puppeteer 指南](https://crawlrelay.com/zh/docs/puppeteer/)

---

CrawlRelay: CrawlRelay 是为开发者设计的住宅代理和 4G/5G 移动代理轮换服务，适用于爬虫和 AI 数据管道：按需付费且流量永不过期，最低流量从 US$0.50/GB 起，支持加密货币支付，无需上传身份证明。无需 KYC —— 无需身份证、自拍和任何证件。支持加密货币支付。
