Puppeteer BrowserContext.targets():获取并处理浏览器上下文中所有活跃 Target
2026/9/7 3:12:32 网站建设 项目流程

Puppeteer BrowserContext.targets():获取并处理浏览器上下文中所有活跃 Target

【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer

BrowserContext.targets()是 Puppeteer 中浏览上下文(BrowserContext)上的一个同步方法,用于一次性拿到当前上下文内所有活跃的Target(页面、Web Worker、背景页等可调试对象)。本篇以 Puppeteer 仓库中的 API 文档为骨架,深入其 CDP 与 WebDriver BiDi 两套协议实现,讲清targets()的签名语义、返回值构成、与pages()/waitForTarget()的协作关系,帮助你在自动化脚本中正确枚举、过滤和等待上下文内的目标对象。

方法签名与返回值

根据 API 文档,该方法的定义如下:

class BrowserContext { abstract targets(): Target[]; }
  • 功能:获取当前浏览器上下文内所有活跃的 Target;
  • 参数:无;
  • 返回值Target[],一个包含该上下文中全部活跃 Target 实例的数组(同步返回,不产生 Promise);
  • 声明位置:抽象声明 位于BrowserContext基类中,具体行为由各协议后端(CDP / BiDi)实现类覆写。

BrowserContext本身表示浏览器中相互隔离的用户环境:启动浏览器后至少存在一个默认上下文,其余可通过browser.createBrowserContext()创建(在 Chrome 中所有非默认上下文均为 incognito 模式)。每个上下文拥有独立的存储(cookies/localStorage 等),targets()正是以这个隔离边界为范围进行枚举的。

什么是 Target

理解targets()的返回值,先要看 Target 抽象类:它代表一个 CDP target,即“任何可以被调试的对象”,例如页面、frame 或 worker。Target 类型由TargetType枚举定义(定义位置):

枚举值字面量含义
PAGE'page'普通页面
BACKGROUND_PAGE'background_page'扩展/后台背景页
SERVICE_WORKER'service_worker'Service Worker
SHARED_WORKER'shared_worker'Shared Worker
BROWSER'browser'浏览器自身
WEBVIEW'webview'WebView
OTHER'other'其他类型
TAB'tab'内部使用的 tab 目标

每个 Target 实例提供了若干关键方法(见 Target 类 与 实现源码):

  • type():返回TargetType,是过滤targets()结果的首要依据;
  • url():返回目标当前 URL;
  • page():当类型为'page''webview''background_page'时返回Page,否则返回null
  • worker():当类型为'service_worker''shared_worker'时返回WebWorker,否则返回null
  • asPage():强制将任意类型的 target 当作 page 处理(适合处理other类型目标);
  • opener():返回打开当前 target 的上级 target,顶层目标返回null
  • browserContext()/browser():返回所属上下文与浏览器,可用于反向归属判断。

典型用法是遍历并做类型分派:

const context = browser.defaultBrowserContext(); for (const target of context.targets()) { if (target.type() === 'page') { console.log(target.url()); } else if (target.type() === 'service_worker') { const worker = await target.worker(); // 处理 worker ... } }

源码实现:CDP 后端的过滤链

在 CDP 实现中,CdpBrowserContext.targets()的实现只有一行过滤逻辑:

override targets(): CdpTarget[] { return this.#browser.targets().filter(target => { return target.browserContext() === this; }); }

即:先从所属CdpBrowser拿到浏览器级全量 target,再按“目标所属上下文 === 当前上下文”做引用相等过滤。这就解释了Browser.targets()BrowserContext.targets()的边界关系:前者覆盖所有上下文,后者是前者的子集。

而浏览器层的 CdpBrowser.targets() 还叠加了两道筛选条件:

override targets(): CdpTarget[] { return Array.from(this.#targetManager.getAvailableTargets().values()) .filter(target => target._isTargetExposed() && target._initializedDeferred.value() === InitializationStatus.SUCCESS ); }

从源码结构看,只有同时满足“已被暴露(_isTargetExposed)”和“初始化成功(InitializationStatus.SUCCESS)”的目标才会出现在结果中。因此targets()返回的是一个稳定、已就绪的快照:正在初始化中或尚未暴露的目标不会混入,调用方无需再做初始化状态判断。

源码实现:BiDi 后端的目标集合

在 WebDriver BiDi 后端中,targets()的数据来源完全不同。BidiBrowserContext 内部维护了一张#targets映射表(结构定义):

readonly #targets = new Map< BidiPage, [ BidiPageTarget, Map<BidiFrame | BidiWebWorker, BidiFrameTarget | BidiWorkerTarget>, ] >(); override targets(): Target[] { return [...this.#targets.values()].flatMap(([target, frames]) => { return [target, ...frames.values()]; }); }

即“页面 → [页面 Target, {frame/worker → frame/worker Target}]”的结构,targets()将其拍平为一维数组返回。这张表是随着页面事件动态维护的:在#createPage中,每当新浏览上下文出现时创建BidiPageTarget,帧挂载(FrameAttached)时注册BidiFrameTarget,worker 创建(WorkerCreated)时注册BidiWorkerTarget;对应地,TargetCreatedTargetChangedTargetDestroyed事件会在创建、导航和销毁时通过trustedEmitter发出。BiDi 后端因此返回的 target 集合粒度更细——不仅包含页面目标,还包含每个 frame 与 worker 对应的 target 对象。

两种实现的差异提示了一点:targets()的具体元素构成与协议后端相关,跨后端编写脚本时尽量依赖type()url()等抽象方法做判断,而不是假设元素数量或类型分布。

与 pages() 的关系:pages 是 targets 的投影

BrowserContext.pages()的文档说明“非可见页面(如background_page)不会出现在列表中,可用Target.page自行查找”。在 CDP 实现中可以看到pages()正是对targets()的投影(实现位置):

override async pages(includeAll = false): Promise<Page[]> { const pages = await Promise.all( this.targets() .filter(target => { return ( target.type() === 'page' || ((target.type() === 'other' || includeAll) && this.#browser._getIsPageTargetCallback()?.(target)) ); }) .map(target => target.page()), ); return pages.filter(page => !!page); }

其规则是:只保留type() === 'page'的目标,外加经_getIsPageTargetCallback判定为页面性质的'other'目标(includeAlltrue时放宽);随后调用target.page()并把返回null的条目剔除。因此当你需要拿到pages()遗漏的目标(例如后台页、无法直接映射为Page的对象)时,回退到targets()+asPage()是文档推荐的思路。

配合 waitForTarget() 等待新目标出现

targets()是同步快照,对于“目标稍后才出现”的场景(典型如window.open打开弹窗),应使用同类的waitForTarget()

async waitForTarget( predicate: (x: Target) => boolean | Promise<boolean>, options: WaitForTargetOptions = {}, ): Promise<Target> { const {timeout: ms = 30000} = options; return await firstValueFrom( merge( fromEmitterEvent(this, BrowserContextEvent.TargetCreated), fromEmitterEvent(this, BrowserContextEvent.TargetChanged), from(this.targets()), ).pipe(filterAsync(predicate), raceWith(timeout(ms))), ); }

从实现可以看到三个关键细节:

  1. 默认超时 30 秒timeout: ms = 30000),可通过options.timeout覆盖;
  2. 它把“TargetCreated事件流 +TargetChanged事件流 +当前targets()快照”合并为一个数据流,也就是说已经存在且满足条件的目标会立即命中,无需等待新事件;
  3. 支持同步或异步谓词(filterAsync),因此可以在谓词内调用target.page()等异步方法做条件判断。

官方文档给出的示例是捕获window.open产生的新窗口 target:

await page.evaluate(() => window.open('https://www.example.com/')); const newWindowTarget = await context.waitForTarget( target => target.url() === 'https://www.example.com/', );

仓库测试 test/src/target.test.ts 中有一个更完整的异步版本,用Promise.all同时发起等待与打开动作,谓词内通过target.page().then(...)比对 URL,并在{timeout: 3000}下断言新 page 与otherPage不是同一实例——这正是waitForTarget的推荐写法。而 枚举场景的用例 则验证了browser.targets()中应同时存在about:blank的 page 目标与browser类型目标,可作为targets()行为的对照基准。

使用建议与边界

  • 作用域context.targets()只返回该上下文内的目标;跨上下文枚举请使用browser.targets()(Browser.targets 文档)。
  • 快照语义:方法同步返回数组快照,不随后续页面打开/关闭而变化;实时追踪请订阅 BrowserContextEvent 中的TargetCreated/TargetChanged/TargetDestroyed事件(BiDi 与 CDP 后端均会发出)。
  • 就绪保证:在 CDP 后端中,结果已过滤掉未暴露或未初始化成功的目标(CdpBrowser.targets);BiDi 后端则随#targets映射实时增删。
  • 与页面列表的取舍:拿Page对象用await context.pages();需要 worker、后台页或自定义过滤逻辑时,用targets()遍历后按type()分派page()/worker()/asPage()

相关文件索引

内容路径
方法 API 文档docs/api/puppeteer.browsercontext.targets.md
Target 类文档docs/api/puppeteer.target.md
抽象声明与 waitForTargetpackages/puppeteer-core/src/api/BrowserContext.ts
Target 抽象类packages/puppeteer-core/src/api/Target.ts
CDP 上下文实现packages/puppeteer-core/src/cdp/BrowserContext.ts
CDP 浏览器实现packages/puppeteer-core/src/cdp/Browser.ts
BiDi 上下文实现packages/puppeteer-core/src/bidi/BrowserContext.ts
行为测试test/src/target.test.ts

【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询