网络

动态代理切换:按上下文配置

如何配置运行时代理切换和按上下文代理设置,以同时管理多个网络身份。

文档中心

想直接看维护中的产品文档?

这篇文章对应的主题已经有文档中心页面。需要规范流程、当前参数和长期参考时,优先看 docs。

简介

许多隐私工作流需要的不仅仅是整个浏览器会话的单一代理。你可能需要为不同的账户、地理区域或目标域名使用不同的代理。静态代理配置(一个代理服务浏览器生命周期内的所有流量)在这些场景中是有限的。

BotBrowser 通过 Playwright 和 Puppeteer 的上下文 API 支持动态代理切换。每个浏览器上下文可以通过不同的代理路由流量,每个代理有自己的地理身份,你可以在运行时创建和关闭上下文而无需重启浏览器。本文涵盖按上下文代理配置、代理轮换策略以及复杂工作流的多代理设置。

隐私影响

当多个账户或身份共享单一代理时,它们的流量模式通过 IP 地址关联。观察到同一 IP 在相似时间访问多个账户的追踪系统可以关联这些账户。即使指纹配置文件不同,共享的网络身份也会创建连接。

按上下文代理分配解决了这个问题。每个上下文通过不同的代理路由,因此不同身份的流量永远不会共享 IP。结合 BotBrowser 的按上下文指纹隔离,每个上下文呈现完全独立的身份:不同的指纹、不同的 IP、不同的地理元数据。

动态代理切换对于跨多个区域的工作流也很重要。一个需要在某个任务中显示为美国、在另一个任务中显示为德国的身份,可以在任务之间切换代理,BotBrowser 会自动调整时区、地区和语言以匹配。

技术背景

浏览器上下文和网络隔离

Playwright 或 Puppeteer 中的浏览器上下文是单个浏览器实例内的隔离浏览会话。每个上下文有自己的 Cookie、localStorage、sessionStorage 和缓存。Playwright 将此隔离扩展到网络配置:每个上下文可以有自己的代理设置。

当你使用 browser.newContext({ proxy: ... }) 创建上下文时,该上下文的所有流量通过指定的代理路由。同一浏览器实例中的其他上下文不受影响。这是上下文级别的真正网络隔离。

静态与按上下文代理配置

静态配置在浏览器启动时通过 --proxy-server 设置代理。该浏览器实例中的所有上下文共享同一代理:

chrome --bot-profile="/path/to/profile.enc" \
       --proxy-server=socks5://user:pass@proxy:1080

按上下文配置在创建单个上下文时设置代理:

const context = await browser.newContext({
  proxy: { server: 'socks5://proxy:1080', username: 'user', password: 'pass' },
});

按上下文代理覆盖该特定上下文的浏览器级代理。如果未指定上下文级代理,上下文继承浏览器级代理。

BotBrowser 如何扩展上下文代理

标准 Chromium 通过 Playwright 的 API 支持按上下文代理,但不会自动为每个上下文派生地理设置。BotBrowser 扩展了此行为:当配置了按上下文代理时,BotBrowser 从代理的 IP 为该特定上下文自动派生时区、地区和语言。

这意味着每个上下文无需手动配置即可获得完整的地理身份。

常见方案及其局限性

启动单独的浏览器实例

多代理设置最简单的方案是为每个代理启动单独的浏览器实例:

# 实例 1
chrome --bot-profile="/profiles/profile-a.enc" \
       --proxy-server=socks5://proxy-us:1080 \
       --user-data-dir="/tmp/session-1"

# 实例 2
chrome --bot-profile="/profiles/profile-b.enc" \
       --proxy-server=socks5://proxy-de:1080 \
       --user-data-dir="/tmp/session-2"

这可以工作但消耗更多内存和 CPU。每个浏览器实例有自己的渲染进程、GPU 进程和工具进程。运行 10 个实例比在单个实例中运行 10 个上下文使用的资源多得多。

框架级代理轮换

一些框架通过 page.route() 或拦截器 API 提供代理轮换。这些方案在 JavaScript/CDP 级别修改流量,这意味着:

  • 初始连接可能在拦截器激活之前使用错误的代理
  • WebSocket 连接可能不被覆盖
  • DNS 预取可能通过原始代理路径泄露

在浏览器引擎级别配置的按上下文代理没有这些时序问题。

代理中间件服务器

运行一个处理轮换的本地代理中间件增加了基础设施复杂性和单点故障。浏览器连接到本地中间件,中间件转发到不同的上游代理。这可以工作但引入了延迟并需要维护额外的软件。

BotBrowser 的方案

带自动检测的按上下文代理

BotBrowser 的按上下文代理支持(ENT Tier1)为每个上下文提供完整的地理身份:

const { chromium } = require('playwright-core');

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/profile.enc',
  ],
  headless: true,
});

// 美国上下文 - BotBrowser 自动派生美国地理设置
const usContext = await browser.newContext({
  proxy: { server: 'socks5://us-proxy:1080', username: 'user', password: 'pass' },
});

// 德国上下文 - BotBrowser 自动派生德国地理设置
const deContext = await browser.newContext({
  proxy: { server: 'socks5://de-proxy:1080', username: 'user', password: 'pass' },
});

每个上下文自动接收与其代理地理位置匹配的时区、地区和语言设置。

按上下文手动地理覆盖

当你需要为上下文指定特定的地理设置时,将代理与 Playwright 的地区和时区选项结合:

async function createGeoContext(browser, config) {
  return browser.newContext({
    proxy: {
      server: config.proxyUrl,
      username: config.user,
      password: config.pass,
    },
    locale: config.locale,
    timezoneId: config.timezone,
  });
}

const usCtx = await createGeoContext(browser, {
  proxyUrl: 'socks5://us-east:1080',
  user: 'u', pass: 'p',
  locale: 'en-US',
  timezone: 'America/New_York',
});

const jpCtx = await createGeoContext(browser, {
  proxyUrl: 'socks5://jp-tokyo:1080',
  user: 'u', pass: 'p',
  locale: 'ja-JP',
  timezone: 'Asia/Tokyo',
});

请求之间的代理轮换

对于需要跨导航的 IP 多样性的工作流,每次轮换创建新上下文:

const proxyPool = [
  { server: 'socks5://proxy-1:1080', username: 'user', password: 'pass' },
  { server: 'socks5://proxy-2:1080', username: 'user', password: 'pass' },
  { server: 'socks5://proxy-3:1080', username: 'user', password: 'pass' },
];

async function navigateWithRotation(browser, url, index) {
  const proxy = proxyPool[index % proxyPool.length];
  const context = await browser.newContext({ proxy });
  const page = await context.newPage();

  try {
    await page.goto(url);
    const content = await page.content();
    return content;
  } finally {
    await context.close();
  }
}

// 每次调用使用不同的代理
for (let i = 0; i < 10; i++) {
  await navigateWithRotation(browser, 'https://example.com', i);
}

使用后关闭上下文确保干净状态:轮换之间没有 Cookie、缓存或连接重用。

会话粘性代理分配

对于每个账户需要跨多次页面加载使用持久代理的工作流,保持上下文打开:

const accounts = [
  { name: 'account-a', proxy: 'socks5://proxy-us:1080' },
  { name: 'account-b', proxy: 'socks5://proxy-uk:1080' },
  { name: 'account-c', proxy: 'socks5://proxy-de:1080' },
];

const contexts = {};

for (const account of accounts) {
  contexts[account.name] = await browser.newContext({
    proxy: { server: account.proxy, username: 'user', password: 'pass' },
  });
}

// 每个账户使用其专用上下文和代理
const pageA = await contexts['account-a'].newPage();
await pageA.goto('https://example.com/login');
// ... 使用账户 A 的代理登录和执行任务

const pageB = await contexts['account-b'].newPage();
await pageB.goto('https://example.com/login');
// ... 使用账户 B 的代理登录和执行任务

配置和用法

CLI 级默认加按上下文覆盖

在 CLI 级别设置默认代理,然后按上下文覆盖:

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/profile.enc',
    // 未指定代理的上下文的默认代理
    '--proxy-server=socks5://default-proxy:1080',
  ],
  headless: true,
});

// 此上下文使用默认代理
const defaultCtx = await browser.newContext();

// 此上下文使用特定代理覆盖
const specialCtx = await browser.newContext({
  proxy: { server: 'socks5://special-proxy:1080', username: 'u', password: 'p' },
});

与 BotBrowser CLI 标志结合

对于最高的地理一致性,将按上下文代理与 BotBrowser 的 CLI 级标志结合:

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/profile.enc',
    '--bot-local-dns',
    '--bot-webrtc-ice=google',
  ],
  headless: true,
});

--bot-local-dns--bot-webrtc-ice 标志全局应用,保护所有上下文免受 DNS 和 WebRTC 泄露。

Puppeteer 多实例方案

Puppeteer 不像 Playwright 那样支持按上下文代理。对于 Puppeteer,使用单独的浏览器实例:

const puppeteer = require('puppeteer-core');

async function createInstance(profile, proxy, timezone, locale) {
  return puppeteer.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      `--bot-profile=${profile}`,
      `--proxy-server=${proxy}`,
      `--bot-config-timezone=${timezone}`,
      `--bot-config-locale=${locale}`,
      `--user-data-dir=${'/tmp/session-' + Math.random().toString(36).slice(2)}`,
    ],
    headless: true,
    defaultViewport: null,
  });
}

const usBrowser = await createInstance(
  '/profiles/us.enc', 'socks5://us-proxy:1080',
  'America/New_York', 'en-US'
);

const deBrowser = await createInstance(
  '/profiles/de.enc', 'socks5://de-proxy:1080',
  'Europe/Berlin', 'de-DE'
);

验证

设置动态代理切换后,独立验证每个上下文:

async function verifyContext(context, expectedCountry) {
  const page = await context.newPage();

  // 检查 IP
  await page.goto('https://httpbin.org/ip');
  const ip = await page.textContent('body');
  console.log(`${expectedCountry} IP:`, ip);

  // 检查时区
  const tz = await page.evaluate(() =>
    Intl.DateTimeFormat().resolvedOptions().timeZone
  );
  console.log(`${expectedCountry} Timezone:`, tz);

  // 检查地区
  const locale = await page.evaluate(() =>
    Intl.NumberFormat().resolvedOptions().locale
  );
  console.log(`${expectedCountry} Locale:`, locale);

  await page.close();
}

await verifyContext(usContext, 'US');
await verifyContext(deContext, 'DE');

确认每个上下文显示不同的 IP、时区和地区。验证一个上下文的流量没有泄露到另一个。

最佳实践

  1. 使用完毕后关闭上下文。 打开的上下文消耗内存。使用后关闭以释放资源。

  2. 按上下文代理使用 Playwright。 Playwright 的原生按上下文代理支持比运行单独浏览器实例更高效。

  3. 将代理地理位置与地区匹配。 始终将时区、地区和语言与代理位置对齐。使用按上下文代理时 BotBrowser 自动处理此项。

  4. 全局设置 --bot-local-dns 和 --bot-webrtc-ice。 这些标志保护所有上下文免受网络泄露,无论代理配置如何。

  5. 为不同账户使用不同的指纹配置文件。 虽然按上下文代理隔离了网络身份,但为每个账户使用不同的配置文件还提供了指纹隔离。

  6. 不要与 BotBrowser 一起使用 page.authenticate()。 page.authenticate() API 可能干扰 BotBrowser 的地理自动检测。请在代理 URL 中使用嵌入的凭据。

常见问题

创建后可以更改上下文的代理吗? 不可以。代理设置在创建上下文时固定。要切换代理,关闭当前上下文并创建新的。

可以同时运行多少个上下文? 没有硬性限制。每个上下文消耗的内存与其打开的页面和缓存资源成正比。实际上,在具有足够 RAM 的机器上,单个浏览器实例可以运行数百个上下文。

按上下文代理支持 SOCKS5H 吗? 支持。--proxy-server 支持的所有代理协议在按上下文代理配置中也受支持。

BotBrowser 是否为按上下文代理自动检测地理位置? 是的(ENT Tier1)。当你设置按上下文代理时,BotBrowser 检测代理的 IP 并为该上下文自动派生时区、地区和语言。

我可以混合使用静态和按上下文代理吗? 可以。在启动时使用 --proxy-server 设置默认代理。指定自己代理的上下文覆盖默认值。没有代理设置的上下文继承浏览器级默认值。

关闭上下文时 Cookie 会怎样? 该上下文的所有 Cookie、localStorage 和缓存都被丢弃。如果你需要 Cookie 持久化,在关闭上下文前导出 Cookie。

Puppeteer 支持按上下文代理切换吗? Puppeteer 对按上下文代理的支持有限。对于完整的按上下文代理控制,请使用 Playwright。对于 Puppeteer,使用不同 --proxy-server 值启动单独的浏览器实例。

总结

动态代理切换使得在单个浏览器实例内实现复杂的多身份工作流成为可能。BotBrowser 的按上下文代理支持结合自动地理检测,为每个上下文提供完整的网络身份:唯一的 IP、匹配的时区、适当的地区和一致的语言设置。

代理基础知识请参阅代理配置。跨所有上下文防止网络泄露请结合DNS 泄露防护WebRTC 泄露防护。完整的身份隔离请参阅多账户浏览器隔离

#代理#Dynamic#Switching#Per-Context#Network

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。