首页
/ Puppeteer `BrowserContext.pages()` 方法全解析:页面枚举、`includeAll` 参数与可见性边界

Puppeteer `BrowserContext.pages()` 方法全解析:页面枚举、`includeAll` 参数与可见性边界

2026-09-08 19:52:10作者:范靓好Udolf

BrowserContext.pages() 是 Puppeteer 中用于枚举某个浏览器上下文(Browser Context)内所有已打开页面的核心 API。本篇技术指南基于官方 API 文档并结合 Puppeteer 当前仓库源码,深入讲解该方法的签名、参数语义、返回行为、"非可见页面"过滤规则,以及它与 Browser.pages()Target.page() 等相邻 API 的配合方式。读者读完后将能准确、可靠地统计与遍历任意上下文中的标签页,并能解释诸如 background_page 等页面为何不会出现在结果中。

BrowserContext 与 pages() 的定位

在 Puppeteer 中,BrowserContext(浏览器上下文)代表浏览器内相互隔离的用户会话。源码注释给出了清晰定义:

BrowserContext represents individual user contexts within a browser. When a browser is launched, it has at least one default browser context. Others can be created using Browser.createBrowserContext. Each context has isolated storage (cookies/localStorage/etc.)。(见 packages/puppeteer-core/src/api/BrowserContext.ts

也就是说:

  • 浏览器启动后天然存在默认浏览器上下文,可通过 browser.defaultBrowserContext() 获取;
  • 可通过 browser.createBrowserContext() 创建隔离上下文(各上下文之间 Cookie、localStorage 等存储互不相通),随后用 context.newPage() 在其中开页;
  • 若某页面通过 window.open() 打开了另一个页面,弹窗页会归属于其父页所在的浏览器上下文(源码见 packages/puppeteer-core/src/api/BrowserContext.ts)。

pages() 正是挂在这样一个上下文对象上的实例方法,用于获取"该上下文内所有打开的页面"列表,是配合多上下文隔离自动化场景时做页面盘点与状态校验的常用入口。

方法签名与参数说明

依据 API 文档(见 docs/api/puppeteer.browsercontext.pages.md),方法的类型签名如下:

class BrowserContext {
  abstract pages(includeAll?: boolean): Promise<Page[]>;
}

参数

参数 类型 说明
includeAll boolean (可选) 实验性参数,设为 true 时将包含所有类型的页面(all kinds of pages)。

返回值

Promise<Page[]>:由 Page 对象组成的数组,元素类型为 Page 实例。

需要说明的是,这一签名定义在抽象基类上(packages/puppeteer-core/src/api/BrowserContext.ts),意味着不同协议后端(CDP、WebDriver BiDi)必须各自实现该方法。

核心语义:默认过滤"非可见页面"

文档中的 Remarks 给出了最容易被忽略的行为边界:

非可见页面(non-visible pages),例如 "background_page"不会出现在该列表中。若需要查找它们,请使用 Target.page()

这里有几个含义值得展开:

  1. pages() 返回的是页面级对象Page),而不是 target(目标)对象;
  2. 普通标签页、window.open() 弹出的窗口、新开的空标签都属于"可见页面",会被列入;
  3. 浏览器扩展等创建的 background_page、Service Worker、DevTools 等非页面型 target 默认被排除在列表之外——因为它们没有对应的"常规页面"语义;
  4. 当这些后台实体确实对应某个 Page 时,需要绕道 Target.page() 逐个取得。

深入源码:pages() 在 CDP 后端的真实过滤逻辑

pages() 的默认参数值为 includeAll = false(源码默认值),它在 Chrome/Chromium 的 CDP 协议实现位于 packages/puppeteer-core/src/cdp/BrowserContext.ts

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 => {
        return target.page();
      }),
  );
  return pages.filter(page => {
    return !!page;
  });
}

从源码结构可以精确拆解出它的枚举策略:

  • 先通过 this.targets() 取得属于当前上下文的所有 targettargets() 本身也是 BrowserContext 的抽象方法,CDP 实现按 target.browserContext() === this 过滤,见 packages/puppeteer-core/src/cdp/BrowserContext.ts);
  • 过滤条件分两条路径:
    1. target.type() === 'page':所有类型为 page 的 target 必然入选,覆盖普通标签页与弹窗;
    2. (target.type() === 'other' || includeAll) && _getIsPageTargetCallback()?.(target):对于 typeother 的 target(默认不传 includeAll 时即可命中),或传入 includeAll = true 时的其他类型 target,还需通过浏览器级的 _getIsPageTargetCallback() 回调判定它是否属于"页面类 target",通过才纳入;
  • 每个入选的 target 调用 target.page() 异步换取 Page 对象;
  • 最后过滤掉 page() 返回为空(如已销毁但尚未清理的 target)的结果。

由此可以得出一个容易被误解的细节:includeAll: true 并不是"无脑返回全部 target 的 Page"——从实现看,即使开启该实验性参数,非 page 类型的 target 仍需满足 _getIsPageTargetCallback 对"页面类 target"的判定才会被包含,普通 worker、devtools 等仍会被排除;同时,真正的过滤产物仍然以 Page 实例为单位返回。

建议:由于 includeAll 在文档与源码注释中均标注为 experimental,生产代码中应优先依赖默认行为 + Target 事件体系(waitForTarget/targetcreated)来精确追踪目标,而不是把 includeAll 当作稳定契约。

浏览器级 pages():跨上下文聚合

值得对比的是,pages() 同样存在于 Browser 级别。文档中 Browser.pages() 的实现会遍历全部浏览器上下文并拼接结果(见 packages/puppeteer-core/src/api/Browser.ts):

async pages(includeAll = false): Promise<Page[]> {
  const contextPages = await Promise.all(
    this.browserContexts().map(context => {
      return context.pages(includeAll);
    }),
  );
  // Flatten array.
  return contextPages.reduce((acc, x) => {
    return acc.concat(x);
  }, []);
}

因此两者关系可以概括为:

API 作用范围 典型用途
browserContext.pages() 仅当前上下文 单会话内页面统计、隔离环境页面盘点
browser.pages() 所有上下文之和(扁平化) 全局页面快照(例如连接远程浏览器后做总览)

includeAll 参数会原样透传到每个上下文各自的 pages() 调用上,说明两层 API 的过滤语义保持一致。

非 CDP 浏览器:Firefox / WebDriver BiDi 的独立实现

从源码结构看,pages() 并非 CDP 专属能力。针对通过 WebDriver BiDi 协议驱动的 Firefox,抽象基类的另一套实现 BidiBrowserContext 同样重写了该方法,签名返回的是 BidiPage[](见 packages/puppeteer-core/src/bidi/BrowserContext.ts)。这意味着:

  • 跨浏览器脚本中,context.pages() 的调用方式一致,上层业务无需感知底层协议差异;
  • 但"哪些 target 算页面、如何枚举"由各自后端决定,跨浏览器运行时仍建议以简单场景(普通标签页)为准,避免依赖实验性 includeAll 的具体展开细节。

实战示例:多上下文场景下的页面枚举

基础用法:列出某个上下文中的所有页面

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({headless: true});

// 默认上下文(浏览器启动即存在)
const defaultContext = browser.defaultBrowserContext();

// 打开一个标签页
const pageA = await defaultContext.newPage();
await pageA.goto('https://example.com');

// 再次调用 newPage 会产生第二个标签页
const pageB = await defaultContext.newPage();

// pages() 返回该上下文内全部可见页面
const pages = await defaultContext.pages();
console.log(pages.length); // 2(不含约:blank 以外的后台/非页面目标时)
console.log(pages.includes(pageA)); // true

await browser.close();

在隔离上下文中做页面统计

const browser = await puppeteer.launch({headless: true});

// 创建隔离上下文(Chrome 中即"隐身"会话,存储相互隔离)
const context = await browser.createBrowserContext();
const page = await context.newPage();

// 上下文内只有 1 个页面
console.log(await context.pages()); // [page]

// 浏览器全局视角则包含所有上下文中的页面
console.log((await browser.pages()).length);

// 用完即弃:关闭该上下文会级联销毁其全部页面
await context.close();

关于"关闭上下文会清空其下所有页面",仓库测试也给出了直接印证:创建新上下文并 newPage()context.pages() 长度为 1,context.close() 之后 browser.pages() 恢复为关闭前的数量(见 test/src/browsercontext.test.ts)。

结合 waitForTarget 处理 window.open 弹窗

由于 pages() 是"快照式"枚举,若要捕捉某个 window.open() 即将产生的新页面,更稳妥的做法是配合 waitForTarget 或上下文上的 targetcreated 事件:

const context = browser.defaultBrowserContext();

const [popupTarget] = await Promise.all([
  context.waitForTarget(target => target.url() === 'https://example.com/popup'),
  pageA.evaluate(url => {
    return window.open(url);
  }, 'https://example.com/popup'),
]);

const popupPage = await popupTarget.page();
// 此时新弹窗页已经归属父页所在上下文,刷新快照即可看到它
const after = await context.pages();
console.log(after.includes(popupPage)); // true

仓库测试 window.open should use parent tab context 验证了"弹窗会进入父页上下文"这一归属规则(test/src/browsercontext.test.ts);Browser.pages should return all of the pagestest/src/target.test.ts)与多标签计数断言(test/src/cdp/TargetManager.test.ts)则共同锁定了 pages() 的计数与包含关系语义。

常见误区与边界场景小结

  1. pages() 不等于 targets():前者返回可直接操作的 Page[](不含纯后台 target),后者返回上下文内所有 target,元素类型不同,使用目的也不同。
  2. 扩展后台页面默认查不到background_page 这类非可见页面不会出现在默认 pages() 结果中;文档明确建议改用 Target.page() 从 target 侧获取。从 CDP 实现看,这是因为默认只放行 type === 'page' 的 target,其余类型需同时通过 _isPageTargetCallback 判定。
  3. includeAll 是实验性开关:源码中 includeAll = false 为默认值(packages/puppeteer-core/src/cdp/BrowserContext.ts),且开启后仍有二次判定,不应假设它会把所有 target 都变成 Page 返回。
  4. 快照是异步的且存在瞬时窗口:实现会并发对多个 target 调用 target.page() 并过滤空值,靠近页销毁/创建瞬间调用时结果可能滞后于真实状态,需要精确追踪时应使用事件与 waitForTarget

相关 API 导航

上述源码实现可分别在 packages/puppeteer-core/src/api/BrowserContext.tspackages/puppeteer-core/src/cdp/BrowserContext.tspackages/puppeteer-core/src/api/Browser.ts 中找到对应证据,测试用例则集中在 test/src/browsercontext.test.tstest/src/target.test.tstest/src/cdp/TargetManager.test.ts

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391