Puppeteer 扩展管理 API 详解browser.extensions() 如何枚举并操控已安装的浏览器扩展【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteerBrowser.extensions()是 Puppeteer 扩展Extension管理 API 中的核心枚举入口它返回一个Mapstring, Extension键为扩展 ID值为对应的Extension实例让你可以在自动化脚本中列出当前浏览器里所有已安装的扩展并对它们执行安装、卸载、查看页面/Service Worker、模拟点击工具栏图标等操作。本文基于 Puppeteer 仓库的 API 文档与源码实现讲解该方法的签名、协议层原理、与installExtension()/uninstallExtension()的协作方式以及 CDP 与 WebDriver BiDi 两种协议下的可用性差异。读完本文你将能够正确使用browser.extensions()枚举扩展并遍历Extension的属性id、name、version、path、enabled理解 CDP 实现中Extensions.getExtensions协议调用与实例缓存机制明确该方法仅适用于 CDPChrome/Chromium协议、在 BiDi 连接下会抛出UnsupportedOperation这一关键限制结合官方测试与示例搭建安装扩展 → 枚举扩展 → 定位其 Service Worker/页面 → 触发 action的完整自动化链路。方法签名与返回值官方 API 文档 docs/api/puppeteer.browser.extensions.md 对该方法的定义如下Retrieves a map of all extensions installed in the browser, where the keys are extension IDs and the values are the corresponding Extension instances.class Browser { abstract extensions(): PromiseMapstring, Extension; }Returns:PromiseMapstring, Extension三个要点值得注意键是扩展 ID不是扩展名。Chrome 扩展 ID 是由 manifest 决定的唯一字符串未签名的加载扩展会得到类似pfnemomllfl_objggckjbffopkcbhkk形式的 ID。用 ID 作为 Map 的键意味着你可以把installExtension()返回的 ID 直接拿来从结果中取回对应实例。值是Extension实例且为只读属性集合。对应文档见 docs/api/puppeteer.extension.mdExtension暴露五个只读属性属性类型说明enabledboolean扩展是否处于启用状态idstring扩展的唯一标识符namestringmanifest 中声明的扩展名称pathstring扩展在文件系统中的路径versionstringmanifest 中声明的扩展版本该 API 处于实验阶段。从 packages/puppeteer-core/src/api/Browser.ts 中public声明与 packages/puppeteer-core/src/api/Extension.ts 中experimental标注可以确认extensions()和Extension类均已公开但标记为实验性行为在未来版本中可能有调整。Extension构造函数为内部实现第三方代码不应直接new Extension()或继承它——你只能通过browser.extensions()等公开入口获得其实例。文档给出的标准用法与 docs/api/puppeteer.extension.md 中的示例一致const extensions await browser.extensions(); for (const [id, extension] of extensions) { console.log(extension.name, id); }CDP 实现原理一次协议调用 实例缓存在 Chrome 侧extensions()的具体实现位于 packages/puppeteer-core/src/cdp/Browser.ts其核心逻辑只有四步override async extensions(): PromiseMapstring, Extension { const response await this.#connection.send(Extensions.getExtensions); const extensionsMap new Mapstring, Extension(); for (const currExtension of response.extensions) { if (this.#extensions.has(currExtension.id)) { // 命中缓存复用已有 CdpExtension 实例 extensionsMap.set(currExtension.id, this.#extensions.get(currExtension.id)!); } else { // 新扩展构造 CdpExtension(id, version, name, path, enabled, browser, logger) const newExtension new CdpExtension( currExtension.id, currExtension.version, currExtension.name, currExtension.path, currExtension.enabled, this, this.logger, ); extensionsMap.set(currExtension.id, newExtension); } } this.#extensions extensionsMap; return this.#extensions; }从源码结构看这里有两个值得关注的设计底层依赖Extensions.getExtensionsCDP 命令。每次调用都会向浏览器发送一次Extensions.getExtensions浏览器返回全部已安装扩展的id、version、name、path、enabled字段Puppeteer 将其逐一映射为CdpExtension实例。这也说明该方法反映的是浏览器当前真实安装状态而不是 Puppeteer 自身记录了什么。#extensions缓存保证实例稳定。CdpBrowser用一个私有 Map 缓存已见过的扩展实例同一扩展 ID 再次出现时直接复用旧实例避免反复构造对象。对上层代码而言这意味着同一浏览器生命周期内针对同一扩展 ID 拿到的是同一个Extension对象引用可以安全地作为 Map 键或做相等性比较。CdpExtension的构造还额外持有CdpBrowser引用和 logger见 packages/puppeteer-core/src/cdp/Extension.ts这是它能够进一步操控扩展的前提——下一节会展开。Extension 实例能做什么workers、pages 与 triggerAction拿到Extension实例后除了读取五个属性还可以调用三个方法定义见 docs/api/puppeteer.extension.mdworkers()列出扩展的 Service WorkerCdpExtension.workers()packages/puppeteer-core/src/cdp/Extension.ts的实现思路是遍历browser.targets()筛选出type() service_worker且 URL 以chrome-extension://id开头的 target再将其转换为WebWorker。这样你就可以直接在扩展后台 Service Worker 里执行脚本const extensions await browser.extensions(); const extension extensions.get(someExtensionId)!; const workers await extension.workers(); for (const worker of workers) { const result await worker.evaluate(() globalThis.MAGIC); }pages()列出扩展的活动页面pages()的筛选条件是type()为page或background_page且 URL 同样以chrome-extension://id开头packages/puppeteer-core/src/cdp/Extension.ts。这覆盖了两类典型场景popup 弹出的选项页以及使用旧式 background page 的扩展。两个方法内部都用#canIgnoreError忽略target 已关闭/不存在类错误isTargetClosedError或No target with given id found只把真正的异常抛出——这符合扩展 target 生命周期短、随时可能被浏览器回收的现实。triggerAction(page)模拟点击扩展图标await this.#browser._connection.send(Extensions.triggerAction, { id: this.id, targetId: page._tabId, });triggerAction(page)会在指定页面上触发扩展的默认 action效果相当于用户点击工具栏上的扩展图标——可能打开 popup也可能执行 action script。注意它需要传入一个Page并依赖该页面对应的tabId。与 installExtension / uninstallExtension 的协作extensions()只是查询入口完整的扩展生命周期管理由三个 API 组成Browser.installExtension()abstract installExtension(path: string, options?: ExtensionInstallOptions): Promisestring参数为扩展目录解压后的目录或.zip/.crx路径返回安装后的扩展 IDBrowser.uninstallExtension()按 ID 卸载Browser.extensions()按 ID 枚举。三者串起来的典型工作流参数用法与 test/src/cdp/extensions.test.ts 中的真实测试一致import puppeteer from puppeteer; // 1. 启动浏览器并安装测试用的扩展 const browser await puppeteer.launch({ headless: true, enableExtensions: true, // 启用扩展支持测试用例通过 setupSeparateTestBrowserHooks({enableExtensions: true}) 传入 }); const extensionId await browser.installExtension(path/to/extension); // 2. 枚举并验证extensions() 的 Map 中应能按 ID 取回该扩展 const extensions await browser.extensions(); const extension extensions.get(extensionId); console.log(extension.name, extension.version, extension.path, extension.enabled); // 3. 等待扩展的 Service Worker 出现并交互 const target await browser.waitForTarget( target target.url().includes(extensionId) target.type() service_worker, ); const worker await target.worker(); console.log(await worker!.evaluate(() globalThis.MAGIC)); // 42 // 4. 清理 await browser.uninstallExtension(extensionId);仓库中有一个可直接参考的示例工程 examples/puppeteer-in-extension/包含manifest.json、background.js、playground.html等测试资产目录test/assets/下也提供了simple-extension与extension-with-page两个最小扩展用于 test/src/cdp/extensions.test.ts 中的验证。该测试文件覆盖的关键断言包括安装扩展后能等到service_workertarget卸载后targets()中不再出现该扩展的 Service Worker可以在扩展 Service Worker 中evaluateshould list extensions and their properties用例直接验证了安装 → 通过 URL 中包含扩展 ID 的 target 定位 → 断言扩展属性的完整链路。协议差异仅 CDP 可用BiDi 会抛异常一个容易被忽略但非常重要的事实extensions()不是跨协议通用的 API。CDP 侧如前所述packages/puppeteer-core/src/cdp/Browser.ts 通过Extensions.getExtensions完整实现BiDi 侧packages/puppeteer-core/src/bidi/Browser.ts 的实现是直接抛出异常override extensions(): PromiseMapstring, Extension { throw new UnsupportedOperation(); }也就是说当你通过puppeteer.connect()以 WebDriver BiDi 方式连接浏览器例如 Firefox 场景时调用browser.extensions()会立刻收到UnsupportedOperation对应 docs/api/puppeteer.unsupportedoperation.md 描述的异常类型。同理installExtension()/uninstallExtension()也属于 CDP 专属能力。如果你的自动化需要同时覆盖 ChromeCDP与 FirefoxBiDi必须在调用扩展管理 API 前做协议分支处理或提供降级逻辑。版本适用与文档说明本文讨论的实现以当前仓库源码为准方法在 packages/puppeteer-core/src/api/Browser.ts 中声明为abstract即各浏览器驱动类必须各自实现行为差异以各驱动实现为准任务中指定的website/versioned_docs/version-25.8.0/api/puppeteer.browser.extensions.md属于 Docusaurus 构建产物website/materialize-docs.ts 在构建文档站点时会从 git release tag如puppeteer-v25.8.0导出对应版本的docs/目录并执行docusaurus docs:version生成versioned_docs因此该文件在源码仓库中并不存在其内容源文件即 docs/api/puppeteer.browser.extensions.md两者一一对应Extension相关 API 标记为experimental在升级大版本时建议对照 packages/puppeteer/CHANGELOG.md 与 docs/api/puppeteer.browser.extensions.md 的更新说明确认行为是否有变化。小结browser.extensions()返回PromiseMapstring, Extension键为扩展 ID值为Extension实例是 Puppeteer 枚举浏览器内已安装扩展的标准入口CDP 实现通过Extensions.getExtensions协议命令拉取浏览器真实安装状态并用私有缓存保证同一扩展 ID 对应稳定的实例引用Extension提供enabled/id/name/path/version五个只读属性和workers()/pages()/triggerAction(page)三个方法可定位扩展的 Service Worker 与页面、模拟点击工具栏图标该 API 标记为experimental且仅 CDP 协议可用——BiDi 连接下调用会抛出UnsupportedOperation跨协议脚本务必提前做分支判断配合installExtension()与uninstallExtension()即可完成安装 → 枚举 → 交互 → 卸载的扩展全生命周期自动化仓库中的 test/src/cdp/extensions.test.ts 与 examples/puppeteer-in-extension/ 提供了可复制的完整参照。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考