puppeteer Browser 类详解:从启动、窗口与屏幕管理到断开重连的浏览器实例控制
发布时间:2026/9/5 22:33:53来源:尧图网络
puppeteer Browser 类详解从启动、窗口与屏幕管理到断开重连的浏览器实例控制【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer本篇技术文章基于 puppeteer 官方 API 文档 Browser 类 展开系统讲解Browser抽象类的完整 API 面页面与浏览器上下文的创建、Cookie 与权限的快捷方法、窗口/屏幕管理、扩展与 PWA 管理以及close/disconnect/wsEndpoint构成的连接生命周期体系。读完后你将能够基于Browser实例完成多上下文隔离、远程重连、多屏仿真等典型场景并理解每个方法在 packages/puppeteer-core/src/api/Browser.ts 与 CDP 实现层 packages/puppeteer-core/src/cdp/Browser.ts 中的真实调用链。类的定位抽象基类与两种获取途径Browser表示一个浏览器实例该实例要么通过Puppeteer.connect()连接而来要么由PuppeteerNode.launch()启动。官方类签名如下export declare abstract class Browser extends EventEmitterBrowserEvents它继承自EventEmitterBrowserEvents是一个抽象类。文档中明确要求该类的构造函数被标记为内部internal第三方代码不应直接调用其构造函数也不应创建继承自Browser的子类。在源码中可以印证这一约束。packages/puppeteer-core/src/api/Browser.ts#L478 中声明了抽象类构造函数同样标注为internal// packages/puppeteer-core/src/api/Browser.ts export abstract class Browser extends EventEmitterBrowserEvents { #logger: Logger; /** internal */ constructor(logger: Logger) { super(undefined, logger); this.#logger logger; } ... }从源码结构看仓库中存在多个具体实现CDP 协议的 CdpBrowserpackages/puppeteer-core/src/cdp/Browser.ts、WebDriver BiDi 协议的packages/puppeteer-core/src/bidi/Browser.ts与packages/puppeteer-core/src/bidi/core/Browser.ts。用户通过launch()/connect()得到的Browser实例由对应协议的实现类填充。官方示例一用 Browser 创建 Page文档给出的第一个核心用法是启动浏览器后创建页面并关闭import puppeteer from puppeteer; const browser await puppeteer.launch(); const page await browser.newPage(); await page.goto(https://example.com); await browser.close();在 CDP 实现中newPage 本身只是一层转发——它把调用委托给默认浏览器上下文// packages/puppeteer-core/src/cdp/Browser.ts override async newPage(options?: CreatePageOptions): PromisePage { return await this.#defaultContext.newPage(options); }真正的建页逻辑在_createPageInContextL414-L455先发送 CDP 命令Target.createTargeturl: about:blank再调用waitForTarget等待目标完成初始化最后经target.page()得到Page实例。注意CreatePageOptions支持三种形态见 api/Browser.ts#L255-L270省略type或type: tab在当前窗口新建标签页type: window并可附带windowBoundsleft/top/width/height/windowState新建独立窗口background?: boolean在后台创建页面默认false。测试文件 test/src/browser.test.ts#L168-L196 演示了窗口形态通过context.newPage({type: window, windowBounds})建窗后用page.windowId()拿到窗口 id再配合browser.getWindowBounds回读校验。官方示例二断开连接与重连第二个官方示例展示wsEndpoint()、disconnect()与Puppeteer.connect()的协作——先保存 WebSocket 端点断开后再凭端点重建连接import puppeteer from puppeteer; const browser await puppeteer.launch(); // Store the endpoint to be able to reconnect to the browser. const browserWSEndpoint browser.wsEndpoint(); // Disconnect puppeteer from the browser. await browser.disconnect(); // Use the endpoint to reestablish a connection const browser2 await puppeteer.connect({browserWSEndpoint}); // Close the browser. await browser2.close();文档同时说明wsEndpoint()返回用于连接本浏览器的 WebSocket URL通常配合Puppeteer.connect()使用你也可以从http://HOST:PORT/json/version中的webSocketDebuggerUrl字段找到调试器地址且该地址格式固定为ws://HOST:PORT/devtools/browser/id。在 CDP 实现里它直接返回连接 URLcdp/Browser.ts#L406-L408override wsEndpoint(): string { return this.#connection.url(); }有一个重要的适用前提pipe 连接下没有 WebSocket 端点。启动时设置pipe: true后wsEndpoint()返回空字符串这一点在测试 test/src/cdp/pipe.test.ts#L17 中被断言expect(browser.wsEndpoint()).toBe()。重连场景在 test/src/launcher.test.ts#L774-L809 中有完整验证先browser.disconnect()再用puppeteer.connect({browserWSEndpoint, protocol})重连随后通过remoteBrowser.pages()找回断开前已导航到nested-frames.html的页面并确认其 frame 树与可执行evaluate返回7 * 8 56——证明浏览器进程与页面状态在断开期间被完整保留。实例属性connected 与 debugInfo文档中Browser暴露两个只读属性属性修饰符类型说明connectedreadonlybooleanPuppeteer 是否已连接到该浏览器debugInforeadonlyDebugInfo实验性获取 Puppeteer 的调试信息目前包含未完成的协议调用pending protocol callsconnected在 CDP 实现中就是对底层连接关闭状态的取反cdp/Browser.ts#L709-L711override get connected(): boolean { return !this.#connection._closed; }debugInfo的返回结构由接口 DebugInfo 定义仅含pendingProtocolErrors: Error[]CDP 实现直接取自连接层{pendingProtocolErrors: this.#connection.getPendingProtocolErrors()}cdp/Browser.ts#L727-L731。测试 test/src/browser.test.ts#L77-L93 验证了connected的一个关键行为关闭所有页面后浏览器连接依然保持expect(browser.connected).toBe(true)且此时仍可继续newPage()——即“页面全部关闭”不会导致浏览器断开。页面与目标Target管理Browser上围绕页面/目标的常用方法如下均引自文档的 Methods 表newPage(options?)在默认浏览器上下文中创建新页面pages(includeAll?)获取本浏览器内所有打开的页面存在多个浏览器上下文时返回所有上下文中的页面。注意不可见页面如background_page类型不会列出可通过Target.page()找到它们target()获取与默认浏览器上下文关联的目标targets()获取所有活动目标多个上下文时返回全部上下文中的目标waitForTarget(predicate, options)等待匹配predicate的目标出现并返回它会遍历所有打开的浏览器上下文。从源码结构看pages()并非查询浏览器而是对每个上下文逐页聚合api/Browser.ts#L637-L647async pages(includeAll false): PromisePage[] { const contextPages await Promise.all( this.browserContexts().map(context { return context.pages(includeAll); }), ); // Flatten array. return contextPages.reduce((acc, x) acc.concat(x), []); }waitForTarget则是基类中用 RxJS 组合的事件流实现api/Browser.ts#L608-L623对已存在目标做一次快照再 mergetargetcreated与targetchanged事件流经predicate异步过滤后与AbortSignal和timeout(ms)竞速async waitForTarget( predicate: (x: Target) boolean | Promiseboolean, options: WaitForTargetOptions {}, ): PromiseTarget { const {timeout: ms 30000, signal} options; return await firstValueFrom( merge( fromEmitterEvent(this, BrowserEvent.TargetCreated), fromEmitterEvent(this, BrowserEvent.TargetChanged), from(this.targets()), ).pipe( filterAsync(predicate), raceWith(fromAbortSignal(signal), timeout(ms)), ), ); }对应的WaitForTargetOptionsapi/Browser.ts#L148-L160中timeout默认30000毫秒、传0可禁用signal支持用AbortSignal取消等待。文档中给出的典型用法——捕获window.open打开的新窗口await page.evaluate(() window.open(https://www.example.com/)); const newWindowTarget await browser.waitForTarget( target target.url() https://www.example.com/, );targets()在 CDP 实现中返回的是“已暴露且初始化成功”的目标cdp/Browser.ts#L659-L668而target()从中查找type() browser的那个若找不到会抛出Browser target is not found。浏览器上下文BrowserContext与 Cookie 快捷方法上下文管理createBrowserContext(options?)创建一个不与其他上下文共享 Cookie/缓存的新浏览器上下文browserContexts()获取所有打开的上下文列表新建的浏览器中只有一个默认上下文defaultBrowserContext()获取默认上下文且默认上下文无法被关闭。createBrowserContext的选项BrowserContextOptions定义在 api/Browser.ts#L41-L58proxyServer可选端口的代理服务器账密通过Page.authenticate设置、proxyBypassList绕过代理的主机列表、downloadBehavior下载行为定义未设置则用默认。CDP 实现将前两者直接传给Target.createBrowserContextcdp/Browser.ts#L267-L290downloadBehavior则在上下文创建后单独调用setDownloadBehavior。文档中的示例import puppeteer from puppeteer; const browser await puppeteer.launch(); // Create a new browser context. const context await browser.createBrowserContext(); // Create a new page in a pristine context. const page await context.newPage(); // Do stuff await page.goto(https://example.com);Cookie 与权限快捷方法。文档中明确这四个方法都是对默认上下文的快捷方式cookies()返回默认BrowserContext中的所有 Cookie等价browser.defaultBrowserContext().cookies()setCookie(cookies)在默认上下文设置 Cookie等价browser.defaultBrowserContext().setCookie()deleteCookie(cookies)从默认上下文删除 Cookie等价browser.defaultBrowserContext().deleteCookie()deleteMatchingCookies(filters)按过滤条件从默认上下文删除 Cookie等价browser.defaultBrowserContext().deleteMatchingCookies()setPermission(origin, permissions)为指定源设置默认上下文中的权限等价browser.defaultBrowserContext().setPermission()。源码印证了这种“转发”关系例如 api/Browser.ts#L691-L705async cookies(): PromiseCookie[] { return await this.defaultBrowserContext().cookies(); } async setCookie(...cookies: CookieData[]): Promisevoid { return await this.defaultBrowserContext().setCookie(...cookies); }权限值支持PermissionDescriptorname、userVisibleOnly、sysex、panTiltZoom、allowWithoutSanitization与状态granted | denied | promptapi/Browser.ts#L132-L143旧的字符串联合类型Permissioncamera、geolocation、notifications等 18 种已被标记为deprecated建议改用PermissionDescriptor。连接生命周期process、close、disconnect 与资源释放process()获取关联的 NodeChildProcess。若该实例是通过Puppeteer.connect连接而来则返回null。测试 test/src/browser.test.ts#L58-L76 双向验证了这一点本地 launch 的浏览器process().pid 0而通过wsEndpoint远程重连得到的remoteBrowser.process()为null。close()关闭该浏览器及所有关联页面。CDP 实现中它是“关闭回调 断开连接”的组合cdp/Browser.ts#L690-L700override async close(): Promisevoid { await this.#closeCallback.call(null); await this.disconnect(); } override disconnect(): Promisevoid { this.#targetManager.dispose(); this.#connection.dispose(); this._detach(); return Promise.resolve(); }disconnect()把 Puppeteer 从浏览器上断开但进程继续运行——这正是前面重连示例的基础。此外Browser实现了显式资源管理Symbol.dispose/Symbol.asyncDispose这解释了为什么文档方法表中列出这两个方法也是测试代码中using remoteBrowser await puppeteer.connect(...)语法的底层支撑。基类实现api/Browser.ts#L858-L871的决策逻辑非常简洁override [disposeSymbol](): void { return void this[asyncDisposeSymbol]().catch(error { this.#logger?.(DEBUG_PREFIXES.error)?.(error); }); } override async [asyncDisposeSymbol](): Promisevoid { if (this.process()) { await this.close(); } else { await this.disconnect(); } await super[asyncDisposeSymbol](); }即自己启动的浏览器有进程执行close()仅连接的浏览器执行disconnect()——using声明可以按获取方式自动选择正确的清理策略。浏览器窗口与屏幕管理这一组方法主要用于窗口级控制与 headless 多屏仿真getWindowBounds(windowId)/setWindowBounds(windowId, windowBounds)获取/设置指定窗口的 boundsscreens()获取屏幕信息对象列表addScreen(params)新增一块屏幕并返回其ScreenInfo仅 headless 模式支持removeScreen(screenId)移除一块屏幕仅 headless 模式支持且不能移除主屏幕指定主屏幕 id 会失败。类型定义方面WindowBounds含left/top/width/height/windowStateWindowState为normal | minimized | maximized | fullscreenapi/Browser.ts#L234-L250ScreenInfo包含id、label、isPrimary、isExtended、devicePixelRatio、orientation、workArea相关尺寸等完整字段api/Browser.ts#L283-L326AddScreenParams支持left/top/width/height必填项及workAreaInsets、devicePixelRatio、rotation、colorDepth、label、isInternal可选项。CDP 层对应关系为Browser.getWindowBounds/Browser.setWindowBounds注意 windowId 会被转成Number发送cdp/Browser.ts#L642-L657以及Emulation.getScreenInfos/Emulation.addScreen/Emulation.removeScreencdp/Browser.ts#L623-L640。测试给出了可直接参考的断言样例test/src/browser.test.ts#L96-L166headless 下默认单块 800×600 主屏addScreen({left: 800, top: 0, width: 1600, height: 1200, colorDepth: 32, workAreaInsets: {bottom: 80}, label: secondary})后screens()返回 2 块且新屏availHeight因底部 80 像素内缩变为 1120removeScreen(screenInfo.id)后回到 1 块。窗口最大化场景L198-L231则展示了先addScreen建副屏、在该屏上开窗、再setWindowBounds(windowId, {windowState: maximized})的完整流程。扩展与 PWA 管理扩展ExtensioninstallExtension(path, options?)安装扩展并返回扩展 IDuninstallExtension(id)卸载指定扩展extensions()获取当前已安装扩展的 Map键为扩展 ID值为Extension实例。CDP 实现中安装走Extensions.loadUnpackedenableInIncognito默认false返回idcdp/Browser.ts#L500-L510卸载走Extensions.uninstall并有一段针对 service worker 目标销毁事件的补偿逻辑L512-L540——当前 CDP 的Extensions.uninstall不会触发对应 service worker 的Target.targetDestroyed事件实现中通过手动补发事件避免测试抖动注释中标记为待上游修复后移除。extensions()则通过Extensions.getExtensions拉取清单并与本地缓存的CdpExtension实例合并。PWA渐进式 Web 应用共 4 个方法且有统一的前置限制installPWA(options)安装 PWA返回其 manifest id。参数InstallPWAOptions含必填的manifestIdWeb App 清单中的 id通常为站点 URL、必填的installUrlOrBundleUrl因为浏览器级 CDP 会话没有可推导安装 URL 的页面、可选displayMode: standalone | browserlaunchPWA(options)启动已安装的 PWA解析为承载该应用窗口的Page。参数含manifestId、可选url应用作用域内要打开的 URL、可选timeout默认 30 秒0禁用getPWAState(options)返回已安装 PWA 的操作系统集成状态如角标计数与已注册的文件处理器uninstallPWA(options)卸载之前安装的 PWA。文档对该组方法的限定必须牢记仅通过 pipe 连接可用——需在puppeteer.launch中设置pipe: true该启动选项默认false底层的PWACDP 域不会通过 WebSocket 连接暴露。此外installPWA返回的 manifest id 就是传入的InstallPWAOptions.manifestId的回显可直接传给launchPWA/getPWAState/uninstallPWAlaunchPWA在 Chromium 聚焦已有应用窗口时返回该窗口的既有 page查询未安装应用的getPWAState会 reject。从源码结构看四个实现都先做网络限制检查配置了 blocklist/allowlist 时直接抛错PWA APIs are not supported when network restrictions are configured.cdp/Browser.ts#L542-L621launchPWA的实现值得注意PWA.launch解析出的是tab目标的 id而 tab 目标位于 page 目标之上的层级、不会暴露在browser.targets()中因此实现会waitForTarget等待该 tab 目标的子 page 目标出现再取target.page()返回L572-L608。getPWAState则调用PWA.getOsAppState返回{badgeCount, fileHandlers}。版本、User-Agent 与事件version()返回表示浏览器名称与版本的字符串。headless 浏览器形如HeadlessChrome/61.0.3153.0非 headless / new-headless 形如Chrome/61.0.3153.0Firefox 形如Firefox/116.0a1文档提醒该格式可能随浏览器版本变化。CDP 实现通过一次Browser.getVersion拿到并缓存Deferred保证只发一次协议请求cdp/Browser.ts#L680-L725userAgent()返回该浏览器的原始User-Agent各Page可用Page.setUserAgent()覆盖。测试对两者均有覆盖test/src/browser.test.ts#L14-L46version()非空且包含chrome或firefoxuserAgent()在 Chrome 下包含WebKit、Firefox 下包含Gecko。事件。Browser会发出文档 BrowserEvent 枚举所列的事件源码定义在 api/Browser.ts#L167-L207事件值触发时机DisconnecteddisconnectedPuppeteer 与浏览器断开可能是浏览器关闭/崩溃或调用了Browser.disconnectTargetChangedtargetchanged目标 URL 变化负载为Target实例含所有上下文TargetCreatedtargetcreated目标被创建如window.open或browser.newPage打开新页面TargetDestroyedtargetdestroyed目标被销毁如页面关闭TargetDiscoveredtargetdiscoveredinternal内部事件CDP 实现中这些事件由TargetManager驱动TargetAvailable→emit(BrowserEvent.TargetCreated, target)TargetGone→TargetDestroyedTargetChanged→TargetChanged且会同时向所属BrowserContext再发一次同名字事件cdp/Browser.ts#L373-L404连接层的CDPSessionEvent.Disconnected触发Disconnected。这也解释了waitForTarget为何要监听TargetCreated与TargetChanged两个事件——新建目标与 URL 变化都可能让predicate首次成立。小结与延伸阅读Browser是 puppeteer 的浏览器级入口抽象页面/上下文/窗口/屏幕/扩展/PWA 的资源管理都汇聚于此具体行为由 CDP 或 BiDi 实现类填充连接模型是理解它的钥匙process()区分“自启动”与“纯连接”close与disconnect语义不同wsEndpoint()是跨进程重连的凭证[Symbol.asyncDispose]则按前者自动选择清理方式注意适用前提屏幕管理仅限 headlessPWA API 仅限pipe: true的 pipe 连接wsEndpoint()在 pipe 连接下为空字符串。可进一步深入的材料api/Browser.ts 抽象基类、cdp/Browser.ts CDP 实现、test/src/browser.test.ts、test/src/launcher.test.ts、test/src/cdp/pipe.test.ts以及 BrowserContext、Target、Page 等关联 API 文档。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网