Puppeteer 中 Browser.deleteMatchingCookies() 详解:按 name/domain/path/url/partitionKey 过滤器批量清除浏览器上下文 Cookie
导读
在自动化测试、爬虫与会话清理场景中,经常需要按条件批量删除 Cookie,而逐个调用 deleteCookie() 传入完整 Cookie 对象既繁琐又容易遗漏。本文基于 Puppeteer 官方 API 文档与源码,深入讲解 Browser.deleteMatchingCookies() 的定位、签名与 DeleteCookiesRequest 过滤器各字段(name、url、domain、path、partitionKey)的确切语义,并结合 BrowserContext.ts 的匹配实现与 browsercontext-cookies.test.ts 的真实测试用例,让你能写出可验证、可复现的批量清理代码。
方法定位与签名
Browser.deleteMatchingCookies() 从默认的 BrowserContext 中删除符合给定过滤条件的 Cookie。它本质上是 browser.defaultBrowserContext().deleteMatchingCookies() 的快捷方式(Shortcut),这一点在 Browser.deleteMatchingCookies() 文档 的 Remarks 部分有明确说明,源码也印证了这一委托关系(Browser.ts):
// packages/puppeteer-core/src/api/Browser.ts
async deleteMatchingCookies(
...filters: DeleteCookiesRequest[]
): Promise<void> {
return await this.defaultBrowserContext().deleteMatchingCookies(...filters);
}
Signature
class Browser {
deleteMatchingCookies(...filters: DeleteCookiesRequest[]): Promise<void>;
}
要点:
- 变长参数(rest arguments):可以一次传入多个
DeleteCookiesRequest过滤器,任一过滤器匹配上的 Cookie 都会被删除,过滤器之间是"或"关系; - 返回值为
Promise<void>,不返回被删除的 Cookie 列表; - 真正的筛选逻辑不在
Browser上,而是在 BrowserContext.deleteMatchingCookies() 中实现(源码见 BrowserContext.ts)。如果你的脚本使用了隔离的BrowserContext(如隐身上下文browser.createBrowserContext()),调用browser.deleteMatchingCookies()不会清理隔离上下文中的 Cookie,应直接调用该上下文上的同名方法。
DeleteCookiesRequest 过滤器字段详解
过滤条件的数据结构是 DeleteCookiesRequest 接口,定义于 Cookie.ts:
// packages/puppeteer-core/src/common/Cookie.ts
export interface DeleteCookiesRequest {
/** Name of the cookies to remove.(必选:要删除的 Cookie 名称) */
name: string;
/** 指定后,仅删除该 URL 的域名与路径都匹配的 Cookie;否则只删当前页面域相关的 Cookie */
url?: string;
/** 指定后,仅删除 domain 完全相等的 Cookie */
domain?: string;
/** 指定后,仅删除 path 完全相等的 Cookie */
path?: string;
/** 指定后,仅删除处于给定分区键下的 Cookie(见下文 partitionKey 跨浏览器差异) */
partitionKey?: CookiePartitionKey | string;
}
官方文档(puppeteer.deletecookiesrequest.md)给出的完整参数表如下:
| 属性 | 修饰符 | 类型 | 说明 |
|---|---|---|---|
domain |
optional |
string |
If specified, deletes only cookies with the exact domain.(只删除 domain 精确匹配的 Cookie) |
name |
(必选) | string |
Name of the cookies to remove.(要删除的 Cookie 名称) |
partitionKey |
optional |
CookiePartitionKey | string |
指定后只删除该分区键下的 Cookie。在 Chrome 中,partitionKey 匹配分区 Cookie 所处的顶级站点;在 Firefox 中,它匹配 WebDriver BiDi PartitionKey 中的 source origin |
path |
optional |
string |
If specified, deletes only cookies with the exact path.(只删除 path 精确匹配的 Cookie) |
url |
optional |
string |
指定后,删除 name 相同且 domain 与 path 均与该 URL 匹配的所有 Cookie;否则只删除与当前页面域名相关的 Cookie |
几个使用要点:
name是唯一必选字段,所有匹配都先以 Cookie 名称相等为前提;domain/path都是精确匹配(源码中为===比较),不做子域或路径前缀推断。删除example.com的 Cookie 时,应传完整域名,而非前缀;partitionKey支持字符串或对象两种形式:字符串会被与 Cookie 分区键的sourceOrigin比较;对象形式则直接比较sourceOrigin。此外 Chrome 与 Firefox 对分区键的解释不同(Chrome 对应分区 Cookie 可用的顶级站点,Firefox 对应 BiDi 的 source origin),跨浏览器脚本需要留意这一差异,测试用例中也有区分处理(见下文"实战示例")。
源码级实现:匹配与删除是如何发生的
BrowserContext.deleteMatchingCookies() 的完整实现在 BrowserContext.ts,其工作流程可以拆为两步:"筛选出目标 Cookie" + "调用 deleteCookie 逐一删除"。
第一步:在内存中筛选匹配的 Cookie
// packages/puppeteer-core/src/api/BrowserContext.ts
async deleteMatchingCookies(
...filters: DeleteCookiesRequest[]
): Promise<void> {
const cookies = await this.cookies();
const cookiesToDelete = cookies.filter(cookie => {
return filters.some(filter => {
if (filter.name === cookie.name) {
if (filter.domain !== undefined && filter.domain === cookie.domain) {
return true;
}
if (filter.path !== undefined && filter.path === cookie.path) {
return true;
}
if (
filter.partitionKey !== undefined &&
cookie.partitionKey !== undefined
) {
if (typeof cookie.partitionKey !== 'object') {
throw new Error('Unexpected string partition key');
}
if (typeof filter.partitionKey === 'string') {
if (filter.partitionKey === cookie.partitionKey?.sourceOrigin) {
return true;
}
} else {
if (
filter.partitionKey.sourceOrigin ===
cookie.partitionKey?.sourceOrigin
) {
return true;
}
}
}
if (filter.url !== undefined) {
const url = new URL(filter.url);
if (
url.hostname === cookie.domain &&
url.pathname === cookie.path
) {
return true;
}
}
return true;
}
return false;
});
});
await this.deleteCookie(...cookiesToDelete);
}
从源码结构看,有四个关键行为值得注意:
- 匹配的前提是
filter.name === cookie.name:名称不相等的 Cookie 永远不会被该过滤器命中,多个过滤器之间为some()的"或"语义。 domain、path、partitionKey、url是四个并列的独立判断:只要其中一个条件(在"指定了该字段"的前提下)成立,Cookie 即被选中;url的判断方式是用new URL(filter.url)解析后比较hostname === cookie.domain && pathname === cookie.path。partitionKey匹配要求 Cookie 自身带有分区键:若 Cookie 的partitionKey为undefined,该分支直接跳过,即分区键过滤器只能命中分区 Cookie(CHIPS 分区场景);同时若 Cookie 侧的分区键是字符串形态,会抛出Unexpected string partition key错误,可以推断该实现预期 Cookie 侧始终返回对象形态的分区键。- 末尾的
return true意味着"仅指定 name 的过滤器会删除该名称下的所有 Cookie"(在默认上下文中即所有域下同名 Cookie)。文档中url字段"否则只删当前页面域相关的 Cookie"的表述,在源码层面实际体现为:不带url时不做域限定。编写脚本时建议显式带上domain/path/url之一,让删除范围更可控。
第二步:deleteCookie 的底层删除机制
筛选出的 Cookie 最终交给 deleteCookie() 完成删除,其实现相当简洁——把每个 Cookie 的 expires 改写为 1(一个已过期的时间戳),再走 setCookie 写入:
// packages/puppeteer-core/src/api/BrowserContext.ts
async deleteCookie(...cookies: Cookie[]): Promise<void> {
return await this.setCookie(
...cookies.map(cookie => {
return {
...cookie,
expires: 1,
};
}),
);
}
也就是说,Puppeteer 的 Cookie 删除并不是向浏览器发送"删除"命令,而是重新设置一个已过期版本的 Cookie,由浏览器自行清理过期项。这也解释了为什么 deleteMatchingCookies 返回 void 而不会报告删除数量——删除结果需要通过 context.cookies() 再次查询来验证。
实战示例与测试用例佐证
仓库测试套件 browsercontext-cookies.test.ts 中的 BrowserContext.deleteMatchingCookies describe 块,覆盖了 5 种过滤器形态的端到端验证,是最直接的参考用法。测试先写入 cookie1、cookie2 两条同名域 Cookie,然后按不同过滤器删除,断言只剩 cookie2:
// test/src/browsercontext-cookies.test.ts(节选)
const filters: DeleteCookiesRequest[] = [
{ name: 'cookie1' },
{ url: 'https://example.test/test', name: 'cookie1' },
{ domain: 'example.test', name: 'cookie1' },
{ path: '/test', name: 'cookie1' },
{ name: 'cookie1', partitionKey: { sourceOrigin: 'https://example.test' } },
];
for (const filter of filters) {
it(`should delete cookies matching ${JSON.stringify(filter)}`, async () => {
// ...
await context.deleteMatchingCookies(filter);
const cookies = await context.cookies();
expect(cookies).toHaveLength(1);
expect(cookies[0]!.name).toBe('cookie2');
});
}
其中分区 Cookie 的写入方式体现了 Chrome/Firefox 的差异处理(测试节选自 browsercontext-cookies.test.ts):
const topLevelSite = 'https://example.test';
await context.setCookie({
name: 'cookie1',
value: 'secret',
domain: new URL(topLevelSite).hostname,
path: '/test',
expires: -1,
httpOnly: false,
secure: true,
partitionKey: isChrome
? { sourceOrigin: topLevelSite, hasCrossSiteAncestor: false }
: undefined,
// ...
});
一个可运行的清理脚本
结合 Browser 侧快捷方式,一个典型的会话清理脚本如下(适用于 launch() 启动的浏览器,默认操作 defaultBrowserContext):
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
// 删除当前上下文里 example.com 上名为 session_token 的 Cookie
await browser.deleteMatchingCookies({
name: 'session_token',
domain: 'example.com',
});
// 同时按多个过滤器清理:另一个名字 + 特定路径下的 Cookie
await browser.deleteMatchingCookies(
{ name: 'csrf_token', domain: 'example.com' },
{ name: 'cart_items', path: '/cart' },
);
// 验证:重新拉取 Cookie 列表确认目标项已消失
const remaining = await browser.cookies();
console.log(remaining.map(c => c.name));
await browser.close();
注意事项(均有源码/测试依据):
- 变长参数传入的多个过滤器是"或"关系,命中任一即删;
domain、path为精确匹配,url会被解析为hostname与pathname两段做相等比较;- 操作对象是默认上下文;若你在
browser.createBrowserContext()返回的上下文中建立了会话,请在该上下文上调用deleteMatchingCookies()(BrowserContext.deleteMatchingCookies 文档); - 删除后建议像测试用例那样调用
context.cookies()复查,确认目标 Cookie 已不存在。
与相关 API 的关系
Puppeteer 在 Cookie 清理上提供三层入口,选择依据是"你手里有什么":
| API | 适用场景 | 实现要点 |
|---|---|---|
| browser.deleteCookie(...cookies: Cookie[]) | 已持有完整的 Cookie 对象(例如刚从 browser.cookies() 里取出) |
直接委托默认上下文,将 expires 改写为 1 后 setCookie(BrowserContext.ts) |
| browser.deleteMatchingCookies(...filters) | 只知道名称,可能附带 domain/path/url/partitionKey 条件,想批量清理 | 先 cookies() 全量拉取,内存过滤后逐一走 deleteCookie |
| page.deleteCookie()(页面级) | 仅关心当前页面域相关的 Cookie | Page.ts 源码注释中明确推荐在需要跨页面/跨域清理时改用 Browser.deleteMatchingCookies / BrowserContext.deleteMatchingCookies |
小结
Browser.deleteMatchingCookies() 是 Puppeteer 中按条件批量清理 Cookie 的快捷入口:它以 name 为必选锚点,叠加 domain/path/url 的精确匹配与 partitionKey 的分区匹配,在默认 BrowserContext 内完成"拉取—过滤—过期改写"三步删除。理解 BrowserContext.ts 中的并列匹配逻辑与 测试用例 的 5 种过滤器形态,就能在测试会话隔离、爬虫登录态清理等场景下精准地控制删除范围,避免误删或漏删。
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 StartedRust0623
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