Puppeteer Browser.cookies() 详解:获取默认浏览器上下文完整 Cookie 列表的机制与实战
Browser.cookies() 是 Puppeteer 中在浏览器级别读取 Cookie 的核心 API:它以一次调用返回默认 BrowserContext 中所有页面的全部 Cookie,是登录态检查、请求调试、状态序列化等场景的常用入口。本文以 Browser.cookies() 文档为骨架,结合当前仓库的源码实现与测试用例,讲清它的签名、返回结构、底层 CDP 调用链以及它与 BrowserContext.cookies() 的等价关系,读完后可直接在生产脚本中正确使用并排查 Cookie 读取问题。
方法概述与签名
Browser.cookies() 方法用于返回默认 BrowserContext 中的所有 Cookie(Returns all cookies in the default BrowserContext)。原始 API 文档位于 docs/api/puppeteer.browser.cookies.md,其定义如下:
Signature
class Browser {
cookies(): Promise<Cookie[]>;
}
Returns: Promise<Cookie[]> —— 返回一个 Promise,解析结果为 Cookie 对象数组。
Remarks(文档原注)
官方文档对它的定位是一句话:"Shortcut for browser.defaultBrowserContext().cookies()",即它只是默认浏览器上下文 cookies() 方法的快捷方式。这意味着:
- 它只能读取默认上下文的 Cookie;通过
browser.createBrowserContext()创建的其它(隔离/无痕)上下文中的 Cookie,需要直接调用对应上下文实例的BrowserContext.cookies()(见 docs/api/puppeteer.browsercontext.cookies.md); - 默认上下文不能被关闭(源码中对
close()有assert(this.#id, 'Default BrowserContext cannot be closed!')的断言,见下文实现分析),因此browser.cookies()永远有稳定的读取入口。
这一"快捷方式"的定位在源码中得到逐字印证:
// packages/puppeteer-core/src/api/Browser.ts
async cookies(): Promise<Cookie[]> {
return await this.defaultBrowserContext().cookies();
}
参见 api/Browser.ts#L689-L692。调用链完全等价于 defaultBrowserContext() → BrowserContext.cookies(),没有任何额外的过滤或状态缓存逻辑。
返回值:Cookie 接口的完整字段
browser.cookies() 返回的每个元素都是 Cookie 接口对象,其定义为 interface Cookie extends CookieData(见 common/Cookie.ts#L59-L85)。CookieData 提供 name、value、domain、path、httpOnly、secure、sameSite、partitionKey 等写入与读取通用的基础字段;Cookie 在此基础上补充了五个由浏览器回传、只读性质的字段,完整字段表如下(继承自 docs/api/puppeteer.cookie.md):
| 属性 | 修饰符 | 类型 | 说明 |
|---|---|---|---|
path |
— | string |
Cookie path.(Cookie 路径) |
expires |
— | number |
Cookie 过期时间,UNIX 纪元起的秒数;会话 Cookie 为 -1 |
secure |
— | boolean |
是否为 Secure Cookie(仅 HTTPS 传输) |
session |
— | boolean |
是否为会话 Cookie |
size |
— | number |
Cookie 大小 |
partitionKeyOpaque |
optional |
boolean |
Cookie 分区键是否不透明。仅 Chrome 支持 |
几个字段在自动化实践中值得特别注意:
expires与session的对应关系:expires === -1的 Cookie 即会话 Cookie,session为true。这也是 Puppeteer 删除 Cookie 的原理——BrowserContext.deleteCookie()内部就是把目标 Cookie 的expires改写为1(一个过去的时间点)再写回,从而让浏览器立即将其过期(见 api/BrowserContext.ts#L299-L308)。因此browser.cookies()读到的过期时间可以直接用于判断"该 Cookie 是否随会话消失"。partitionKeyOpaque仅 Chrome 支持:它对应 Chrome 第三方 Cookie 分区(CHIPS)机制中的不透明分区键。同文件中的 CookiePartitionKey 定义了sourceOrigin与可选的hasCrossSiteAncestor两个字段,用于描述 Cookie 分区归属。sameSite/priority/sourceScheme等枚举类型:CookieSameSite('Strict' | 'Lax' | 'None' | 'Default')、CookiePriority('Low' | 'Medium' | 'High')、CookieSourceScheme('Unset' | 'NonSecure' | 'Secure')均在 common/Cookie.ts#L13-L30 中定义,可用于断言服务端下发的 Cookie 策略是否符合预期。
底层实现:CDP 调用链与数据转换
Browser.cookies() 本身只是一层转发,真正干活的是其所在实现的 BrowserContext 子类。CDP(Chrome DevTools Protocol)实现中,cookies() 直接下发 Storage.getCookies 命令,并把返回的 partitionKey 从 CDP 结构转换为 Puppeteer 结构:
// packages/puppeteer-core/src/cdp/BrowserContext.ts
override async cookies(): Promise<Cookie[]> {
const {cookies} = await this.#connection.send('Storage.getCookies', {
browserContextId: this.#id,
});
return cookies.map(cookie => {
return {
...cookie,
partitionKey: cookie.partitionKey
? {
sourceOrigin: cookie.partitionKey.topLevelSite,
hasCrossSiteAncestor: cookie.partitionKey.hasCrossSiteAncestor,
}
: undefined,
};
});
}
参见 cdp/BrowserContext.ts#L146-L161。从这段源码可以得到三个实现事实:
- 作用域由
browserContextId决定:默认上下文的#id为undefined,即Storage.getCookies不带该参数时查询的就是浏览器默认作用域的全部 Cookie——这正是browser.cookies()能"一次拿全"的原因。 partitionKey.topLevelSite被重命名为sourceOrigin:如果你对比过原始 CDP 返回结构与 Puppeteer 结果,会发现字段名不一致,这是映射层刻意对齐跨浏览器语义的结果(BiDi 规范中对应PartitionKey的 source origin)。- 同一接口在 BiDi 后端也有独立实现:
packages/puppeteer-core/src/bidi/BrowserContext.ts同样实现了cookies(),因此该方法在 Chrome(CDP)与 Firefox(WebDriver BiDi)上均可用;但由于partitionKey/partitionKeyOpaque等字段标注"Supported only in Chrome",跨浏览器时以文档字段说明为准。
典型使用示例
结合源码中 deleteMatchingCookies() 的依赖方式(它先调用 cookies() 拉全量、再按 name/domain/path/url/partitionKey 过滤删除,见 api/BrowserContext.ts#L315-L364),browser.cookies() 最常见的用法是"读取—断言—清理"三步:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// 1. 读取默认上下文的完整 Cookie 列表
const cookies = await browser.cookies();
console.log(cookies.map(c => `${c.name}=${c.value} (expires: ${c.expires})`));
// 2. 断言某个分区/属性特征(Chrome 特有字段,Firefox 可能为 undefined)
const partitioned = cookies.find(c => c.partitionKeyOpaque);
// 3. 精确清理某个域下的全部 Cookie
// 注意:deleteMatchingCookies 属于 BrowserContext 方法
await browser.defaultBrowserContext().deleteMatchingCookies({
domain: 'example.com',
});
await browser.close();
适用前提与限制:
browser.cookies()只覆盖默认上下文。若脚本使用了browser.createBrowserContext()创建的隔离上下文(Chrome 中即 incognito 上下文,各上下文 Cookie/localStorage 相互隔离,见 api/BrowserContext.ts#L61-L107 的类注释),必须对该上下文实例调用context.cookies()。- 该方法读取的是浏览器存储层的全量 Cookie,与当前页面 URL 无关;如果只需要某页面作用域的 Cookie,可对比使用页面级的
page.cookies(),再结合domain/path字段自行过滤。 partitionKey、partitionKeyOpaque、priority、sourceScheme等字段文档明确标注 Chrome-only,跨浏览器代码中建议做可选字段判断。
测试用例中的行为验证
仓库的集成测试对该 API 的行为提供了可验证依据:
- test/src/cookies.test.ts 覆盖了 Cookie 设置与读取的核心行为(会话 Cookie、过期时间、domain/path 匹配等);
- test/src/browsercontext-cookies.test.ts 验证了不同
BrowserContext之间 Cookie 相互隔离、以及上下文级cookies()的读写; - test/src/defaultbrowsercontext.test.ts 中涉及默认上下文的 Cookie 场景,与
browser.cookies()走默认上下文路径的语义一致。
这些测试文件可以在 test/ 目录配合 Mocha 运行器(见 tools/mocha-runner)执行,用于回归验证 Cookie API 在版本升级后的行为是否稳定。
小结
Browser.cookies() 的定位非常收敛:它等价于 browser.defaultBrowserContext().cookies(),最终在 CDP 后端翻译为一次 Storage.getCookies 调用,返回默认作用域内全部 Cookie[]。理解这一点后,实践中三个要点即可覆盖绝大多数场景:返回对象按 Cookie 接口解析(expires: -1 即会话 Cookie,Chrome 独有 partitionKeyOpaque);只读默认上下文,隔离上下文需走 BrowserContext.cookies();删除 Cookie 依赖"改写 expires 后写回"的机制,因此 deleteMatchingCookies() 这类操作本质上依赖 cookies() 提供的全量快照作为过滤输入。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00