首页
/ Puppeteer `Page.setUserAgent()` 完全指南:覆盖 User-Agent 与 User-Agent Client Hints

Puppeteer `Page.setUserAgent()` 完全指南:覆盖 User-Agent 与 User-Agent Client Hints

2026-09-07 14:36:14作者:裴麒琰

Page.setUserAgent() 是 Puppeteer 中用于自定义页面发送请求时 User-Agent 字符串及浏览器客户端提示(User-Agent Client Hints)的核心方法,常用于伪装浏览器标识、模拟移动端设备、规避站点针对桌面 Chrome 的差异化检测等场景。本文以 官方 API 文档 为骨架,结合仓库内 Chrome DevTools Protocol(CDP)与 WebDriver BiDi 两条实现链路及完整测试用例,讲解两个重载签名、各参数含义、底层生效机制与实战技巧,帮助你彻底掌握该 API。

方法概览:为什么需要手动设置 User-Agent

现代网站普遍通过 navigator.userAgent 与请求头中的 User-Agent 判断访问端类型,并据此提供桌面/移动版页面、限制自动化访问或注入差异化内容。而在最新浏览器中,User-Agent Client Hints 体系(通过 navigator.userAgentData 暴露)让站点可以获得更细粒度的品牌、架构、平台等信息,单纯改写 UA 字符串已不足以"骗过"依赖 Client Hints 的服务端检测。

Page.setUserAgent() 之所以重要,在于它同时覆盖两条通道

  • 请求头 User-Agent(通过协议层 Network.setUserAgentOverride 等实现);
  • 客户端提示元数据(architecture、model、platform、platformVersion、mobile 等),影响 navigator.userAgentData 及其 High-Entropy 值。

该方法是 Page 类 的抽象方法,在 Chromium(CDP)与 Firefox(WebDriver BiDi)两套后端上均有完整实现,签名同时声明于公共 API 层。此外它还被 Page.emulate() 在模拟设备描述符时自动调用,因此也是设备模拟机制的地基。

完整签名与参数说明

当前仓库 puppeteerpuppeteer-core 版本为 25.8.0(见 packages/puppeteer/package.jsonpackages/puppeteer-core/package.json)。方法声明位于 公共抽象类,存在两个重载

重载一:位置参数形式(已废弃)

abstract setUserAgent(
  userAgent: string,
  userAgentMetadata?: Protocol.Emulation.UserAgentMetadata,
): Promise<void>;
参数 类型 说明
userAgent string 本页面要使用的具体 User-Agent 字符串
userAgentMetadata Protocol.Emulation.UserAgentMetadata (可选) 对应的客户端提示元数据

返回: Promise<void> —— 当 User-Agent 设置完成后 resolve。

⚠️ 废弃提示:官方文档明确标注该重载已经 obsolete,应改用下方重载二(options 对象形式)。在源码中对应注释 @deprecated Use Page.(setUserAgent:2) instead(见 Page.ts)。

重载二:options 对象形式(推荐)

abstract setUserAgent(options: {
  userAgent?: string;
  userAgentMetadata?: Protocol.Emulation.UserAgentMetadata;
  platform?: string;
}): Promise<void>;
参数 类型 说明
options { userAgent?: string; userAgentMetadata?: ...; platform?: string } 包含 UA 及可选的 User-Agent 元数据的对象

返回: Promise<void> —— 当 User-Agent 设置完成后 resolve。

options 形式支持三个字段,其中 userAgentplatform 均为可选,意味着你可以只覆盖其中之一而保持其余值不变:

字段 类型 含义 关键行为
userAgent string 期望的完整 UA 字符串 缺省时继承浏览器当前默认 UA(Browser.userAgent()
userAgentMetadata Protocol.Emulation.UserAgentMetadata UA Client Hints 元数据,控制 navigator.userAgentData 可省略
platform string 需要模拟的平台名,影响 navigator.platform 可省略;传空字符串表示"不覆盖平台"(见下文 BiDi 分支的特殊处理)

基础用法示例

以下代码在导航之前为当前页面设置自定义 UA,使该页面此后发起的请求(含子框架请求)都携带该字符串:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

// 推荐:options 对象形式
await page.setUserAgent(
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
);

// 若希望同步覆盖平台标识
await page.setUserAgent({
  userAgent: 'Mozilla/5.0 ... Chrome/131.0.0.0 Safari/537.36',
  platform: 'Win32',
});

await page.goto('https://example.com');
await browser.close();

位置参数重载(setUserAgent('...'))虽仍可调用,但已被标记 deprecated,新代码应统一使用 options 形式。若选择使用已废弃重载并传入第二个参数 userAgentMetadata,请务必让其中的字段与第一个 UA 字符串保持自洽,否则可能出现 UA 与 Client Hints 相互矛盾。

深入参数一:userAgentMetadata 与 Client Hints

userAgentMetadata 的类型为 Protocol.Emulation.UserAgentMetadata,即 CDP Emulation 域定义的 User-Agent 元数据结构。它负责将浏览器渲染出的 navigator.userAgentData(以及 getHighEntropyValues() 可读取的 High-Entropy 字段)一并覆盖。

仓库中的 集成测试(测试名 should work with additional userAgentMetdata)验证了该参数的生效范围:

await page.setUserAgent('MockBrowser', {
  architecture: 'Mock1',
  mobile: false,
  model: 'Mockbook',
  platform: 'MockOS',
  platformVersion: '3.1',
});
// 页面内:
// navigator.userAgentData.mobile === false
// navigator.userAgentData.getHighEntropyValues(['architecture','model','platform','platformVersion'])
//   → { architecture: 'Mock1', model: 'Mockbook', platform: 'MockOS', platformVersion: '3.1' }
// 且请求头 User-Agent === 'MockBrowser'

可见该参数会直接影响页面 JS 侧可观察到的 Client Hints(含 High-Entropy 部分),而不只是网络层信息。字段通常在结构上涵盖品牌列表(brands)、architecturebitnessfullVersionListmobilemodelplatformplatformVersionwow64 等——仓库测试实际断言了 architecturemodelplatformplatformVersionmobile 五个字段的读写行为,是可靠的参照。

深入参数二:platform 与"只改平台不动 UA"

options.platform 用于单独模拟操作系统平台。测试 should work with platform option without userAgent(见 page.test.ts)证明:只传 platform、不传 userAgent 时,UA 字符串保持不变,但 navigator.platform 会返回模拟值,且实际发出的请求头 User-Agent 也仍为原始 UA:

await page.setUserAgent({platform: 'MockPlatform'});

// navigator.platform === 'MockPlatform'
// navigator.userAgent === 原 UA(未改变)
// 请求头 user-agent === 原 UA

这适用于仅需伪装操作系统、又不想整套更换浏览器标识的场景。

底层原理一:CDP 后端(Chromium)的生效链路

在 Chromium 后端,方法实现位于 cdp/Page.ts。其核心逻辑是按入参形态分派:

if (typeof userAgentOrOptions === 'string') {
  // 已废弃位置参数重载 → 仍兼容转发
  return await this.#frameManager.networkManager.setUserAgent(
    userAgentOrOptions,
    userAgentMetadata,
  );
} else {
  // options 形式:未显式给 userAgent 时回落到浏览器默认 UA
  const userAgent = userAgentOrOptions.userAgent ?? (await this.browser().userAgent());
  return await this.#frameManager.networkManager.setUserAgent(
    userAgent,
    userAgentOrOptions.userAgentMetadata,
    userAgentOrOptions.platform,
  );
}

可以看到,对象形式在 userAgent 缺省时会自动取 Browser.userAgent() 作为兜底,这正是"只覆盖 platform 不影响 UA"的来源。

随后请求落到 NetworkManager.setUserAgent,将三份数据暂存后通过 #applyToAllClients 广播到所有已连接的 CDP 会话(#applyUserAgent,见 NetworkManager.ts),最终执行:

await client.send('Network.setUserAgentOverride', {
  userAgent,
  acceptLanguage: this.#acceptLanguage,
  userAgentMetadata: this.#userAgentMetadata,
  platform: this.#platform,
});

几个值得注意的实现细节:

  • 对全部目标生效#applyToAllClients 会把 override 应用到页面关联的所有目标上,因此子框架(iframe)里的请求同样携带自定义 UA——对应测试 should work for subframespage.test.ts)。
  • 与 Accept-Language 协同:该方法与 Page.setExtraHTTPHeaders 一样经由 NetworkManager 统一管理,且 #applyUserAgent 内还会带上 acceptLanguage,说明 UA 与语言等网络层 override 由同一套机制维护。
  • 幂等与清理:代码中维护了 nothingToEmulate#userAgentOverrideApplied 标记——当所有 override 字段都未设置时仍需发送一次协议指令以重置先前已应用的覆盖(注释原文:"Still need to send once to reset a previously-applied override"),这保证了重复设置/清空之间的一致性。

底层原理二:WebDriver BiDi 后端(Firefox)的差异

Firefox 走 WebDriver BiDi 协议,实现见 bidi/Page.ts,语义上做了与 CDP 分支对齐的归一化,但存在明显的协议差异:

  • 空字符串即"恢复默认":代码先将 userAgent 归一化——若为 '' 则转换为 null,注释指出 "In WebDriver BiDi null is used to restore the original user agent",再调用 browsingContext.setUserAgent()
  • platform 通过 Client Hints 变通实现:由于 WebDriver BiDi 规范暂未提供独立的平台覆盖(源码注释注明其受 w3c/webdriver-bidi#1065 待办项影响),实现会把 platform 合并进 client hints 元数据,通过 browsingContext.setClientHintsOverride() 下发;同时 '' 平台被解释为"无覆盖"。

这也解释了仓库为何需要区分两条实现路径:同一份 API,在两种协议下的落地方式与还原语义并不相同

实战:恢复默认 UA

如果你在同一页面先后执行了多次导航或加载了多个站点,希望清除先前设置的覆盖、恢复浏览器出厂默认 UA,可按测试 should restore originalpage.test.ts)中的方式传入空字符串:

// 先覆盖为自定义值
await page.setUserAgent('foobar');

// 之后清除覆盖,恢复原始 UA
await page.setUserAgent('');

// 页面内 navigator.userAgent 与后续请求头 user-agent
// 均恢复为浏览器默认值(与页面初始 UA 一致)

该测试同时断言了页面侧 navigator.userAgent 与网络侧请求头的双重复原。注意还原的是"进入覆盖前"的原始 UA,而非上一次覆盖值。

实战:配合设备描述符模拟移动端

Puppeteer 内置了常用设备的描述符集合(KnownDevices,如 iPhone 6),其中即包含对应设备的 UA。测试 should emulate device user-agentpage.test.ts)展示了直接取用设备 UA 的写法:

import puppeteer, {KnownDevices} from 'puppeteer';

const page = await browser.newPage();
await page.setUserAgent(KnownDevices['iPhone 6'].userAgent);
// 此时页面内 navigator.userAgent 包含 'iPhone'

更常见的做法是直接使用 Page.emulate() 一次模拟设备(viewport + UA + Client Hints),其内部实现正是调用了 setUserAgent({userAgent: device.userAgent, ...})(相关调用见 Page.ts)。若只需修改标识而保留桌面视口,则单独调用 setUserAgent 更合适。

使用建议与注意事项

综合文档声明、两套后端实现与 测试套件 中约 8 个用例,实践中的要点如下:

  1. 优先使用 options 对象形式:位置参数重载已被官方标记废弃,虽向后兼容,但新代码应迁移到 setUserAgent({userAgent, userAgentMetadata, platform})
  2. 调用时机:设置对所有由该页面发起的后续请求生效(含子框架);对已经加载完成的资源,UA 不会追溯改写,通常应在 page.goto() 之前调用。但若用于服务端检测验证,改完后再发起的请求(含 reload 或后续导航)都会携带新值。
  3. 字符串与 Client Hints 必须自洽:若站点通过 navigator.userAgentData 或 High-Entropy API 校验设备,仅改 UA 字符串可能暴露不一致;应同步提供匹配的 userAgentMetadata
  4. 恢复语义按后端区分:空字符串在 BiDi 后端明确转换为 null 以恢复原 UA;CDP 后端则依赖协议层 override 的重置机制保证最终一致。跨浏览器(Chromium/Firefox)场景建议以"设置 → 清空 → 校验恢复"的测试行为为准。
  5. 范围是"当前页面":该方法针对单个 Page 生效,不影响 Browser.userAgent() 或浏览器级别的默认值;新建页面默认仍使用浏览器原始标识。
登录后查看全文
热门项目推荐
相关项目推荐