
Puppeteer Target.createCDPSession 深度解析在 Target 层面建立 Chrome DevTools Protocol 会话【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteerTarget.createCDPSession()是 Puppeteer 中用于在任意 CDP Target页面、Worker、浏览器等上建立一条原始 Chrome DevTools Protocol 会话的底层 API。本文基于仓库中的官方 API 文档 docs/api/puppeteer.target.createcdpsession.md结合 CDP 协议实现、BiDi 协议实现 与 CDPSession 测试用例完整讲清该方法的签名、返回类型、底层调用链、BiDi 环境下的行为边界以及可直接运行的实战用法帮助你越过Page抽象直接驱动 CDP 协议。一、API 定义签名、返回类型与方法定位docs/api/puppeteer.target.createcdpsession.md 对该方法的官方描述只有一句话Creates a Chrome Devtools Protocol session attached to the target.为该 Target 创建一个附着的 Chrome DevTools Protocol 会话其 TypeScript 签名与返回类型如下class Target { abstract createCDPSession(): PromiseCDPSession; }Returns:PromiseCDPSession几个要点抽象方法Target是一个抽象类见 packages/puppeteer-core/src/api/Target.tscreateCDPSession声明为abstract由 CDP 与 BiDi 两个协议的具体实现各自落实。源码中的 JSDoc 与文档描述一致/** * Creates a Chrome Devtools Protocol session attached to the target. */ abstract createCDPSession(): PromiseCDPSession;对应 Target.ts。会话附着于 Target返回的CDPSession与特定 Target 绑定所有经它发出的协议命令和接收到的协议事件都作用在该 Target 上而不是其他页面或 Worker。与Page.createCDPSession()的关系Page抽象类同样声明了createCDPSession()见 packages/puppeteer-core/src/api/Page.ts。日常多数场景用page.createCDPSession()即可而target.createCDPSession()是更底层、更通用的入口——它不要求 Target 是页面类型任何被浏览器暴露的 CDP Target如service_worker、shared_worker或other都可以拿到会话。二、CDP 实现从 sessionFactory 到 Target.attachToTarget在 ChromeCDP 协议实现中Target的具体子类是CdpTargetpackages/puppeteer-core/src/cdp/Target.ts其createCDPSession()的实现非常精炼// packages/puppeteer-core/src/cdp/Target.ts (L125-L133) override createCDPSession(): PromiseCdpCDPSession { if (!this.#sessionFactory) { throw new Error(sessionFactory is not initialized); } return this.#sessionFactory(false).then(session { session.setTarget(this); return session; }); }对应 Target.ts。可以把它拆成三步理解检查 sessionFactory每个CdpTarget构造时被注入一个sessionFactory构造函数参数见 Target.ts。若未初始化例如 Target 对象脱离了浏览器连接调用会直接抛出sessionFactory is not initialized错误。创建会话不模拟自动附着sessionFactory(false)中传参isAutoAttachEmulated false。这个标志的含义是由你手动创建的会话不走 Puppeteer 内部自动附着auto-attach那套初始化流程——Puppeteer 不会为你这个自定义会话启用它自己需要的协议域也不会把该会话当作内部会话管理。这正是测试 test/src/cdp/CDPSession.test.ts 中should not report created targets for custom CDP sessions用例验证的行为监听targetcreated事件的 handler 里再调用target.createCDPSession()不会触发新的 Target 上报。回绑 Target 对象session.setTarget(this)将 JS 侧的Target实例挂到会话上便于后续从会话反查 Target例如CDPSession的事件处理与调试场景。再往下一层sessionFactory最终落到 Connection 上真正向浏览器发出的 CDP 命令是Target.attachToTarget// packages/puppeteer-core/src/cdp/Connection.ts (L300-L326 附近) async _createSession(...) { ... const {sessionId} await this.send(Target.attachToTarget, {...}); ... } async createSession(...) { return await this._createSession(targetInfo, false); }对应 Connection.ts。也就是说target.createCDPSession()的本质是通过浏览器级 WebSocket 连接向浏览器发送Target.attachToTarget拿到浏览器分配的sessionId再包装成一个CDPSession实例返回。另外一个值得注意的细节Page.createCDPSession()在 CDP 实现里是直接委托给 Target 的——// packages/puppeteer-core/src/cdp/Page.ts (L1036-L1038) override async createCDPSession(): PromiseCDPSession { return await this.target().createCDPSession(); }对应 Page.ts。这印证了文档中的定位Page 层面的会话创建最终走的都是Target.createCDPSession()这条路径。三、BiDi 实现功能边界与 UnsupportedOperationPuppeteer 当前同时支持 CDP 与 WebDriver BiDi 两种协议通道。在 BiDi 实现中packages/puppeteer-core/src/bidi/Target.tscreateCDPSession的行为按 Target 类型分化BiDi Target 子类createCDPSession 行为源码位置BidiBrowserTarget抛出UnsupportedOperationbidi/Target.ts#L35-L37BidiPageTarget委托给page.createCDPSession()bidi/Target.ts#L72-L74BidiFrameTarget委托给frame.createCDPSession()bidi/Target.ts#L117-L119BidiWorkerTarget抛出UnsupportedOperationbidi/Target.ts#L154-L156也就是说在 BiDi 模式下该方法只对页面/帧级 Target 可用通过底层 CDP 桥接方式建立会话浏览器级与 Worker 级 Target 则明确不支持。仓库的 docs/webdriver-bidi.md 也在 BiDi 支持矩阵中单独列出了Page.createCDPSession()这一条目。如果你的代码需要依赖target.createCDPSession()建议以 CDPChrome为适用前提或对 BiDi 环境做能力探测与降级。四、拿到 CDPSession 之后能做什么createCDPSession()返回的 CDPSession 继承自EventEmitter核心能力是client.send(method, params)发送任意 CDP 命令并等待响应client.on(eventName, handler)/client.on(*, ...)订阅该会话上的协议事件client.detach()手动分离会话detached只读属性反映分离状态client.connection()/client.id()访问底层连接与会话 ID。官方 API 文档中给出的标准用法示例来自 docs/api/puppeteer.cdpsession.mdconst client await page.createCDPSession(); await client.send(Animation.enable); client.on(Animation.animationCreated, () console.log(Animation created!), ); const response await client.send(Animation.getPlaybackRate); console.log(playback rate is response.playbackRate); await client.send(Animation.setPlaybackRate, { playbackRate: response.playbackRate / 2, });把入口换成target.createCDPSession()完全同理先send对应协议域的enable命令再on该域的事件即可在任意 Target 上监听原始协议事件流。五、实战用法在 Target 层面打开会话1. 常规页面 Targetimport puppeteer from puppeteer; const browser await puppeteer.launch(); const page await browser.newPage(); // page.target() 拿到 CDP Target 后建立会话 const target page.target(); const client await target.createCDPSession(); await client.send(Runtime.enable); const result await client.send(Runtime.evaluate, { expression: 1 2, returnByValue: true, }); console.log(result.result.value); // 3 await client.detach();这个enable 域 → evaluate → 得到 3的流程正是官方测试 test/src/cdp/CDPSession.test.ts 中 should work 用例的验证方式。2. 监听 Target 创建并为其建会话BrowserContext提供waitForTarget/targetcreated事件可以捕获任意新 Target包括 Worker 等非页面类型再为其打开会话const context browser.defaultBrowserContext(); const workerTarget await context.waitForTarget(t t.type() service_worker ); const client await workerTarget.createCDPSession(); await client.send(Runtime.enable);TargetType的取值枚举page、background_page、service_worker、shared_worker、browser、webview、other等定义在 packages/puppeteer-core/src/api/Target.tstype()方法返回其中一种便于按类型筛选后再决定是否为该 Target 建会话。3. 自定义会话的行为边界来自测试用例的验证结论test/src/cdp/CDPSession.test.ts 的describe(Target.createCDPSession)套件给出了几条可依赖的行为事实事件独立路由会话能收到自己enable的域的事件should send events 用例用Network.requestWillBeSent验证且不会把其他域的事件漏进来should not send extra events 用例断言事件域集合恰好是[Network]域开关互不干扰你手动Runtime.enable/Debugger.enable的域与 Puppeteer 内部如 JS coverage对Debugger域的启停相互独立should enable and disable domains independently 用例手动 detach 安全会话可以随时detach()之后不再收发消息should be able to detach session 用例。六、错误处理与注意事项sessionFactory 未初始化CDP 实现中若 Target 未绑定 sessionFactory例如连接已销毁调用会同步抛出Error(sessionFactory is not initialized)packages/puppeteer-core/src/cdp/Target.ts。在长生命周期脚本里建议对目标已关闭场景做 catch 并检查client.detached。BiDi 环境的能力差异如第三小节所述浏览器级/Worker 级 Target 在 BiDi 下会抛UnsupportedOperation编写跨协议代码时应在文档中声明适用前提。不要与自动附着混淆Puppeteer 内部为 Page/Worker 建立的会话走sessionFactory(true)自动附着初始化而createCDPSession()固定传false即自定义会话。这意味着你手动建的会话不享受 Puppeteer 内部域的自动启用需要自行send(Domain.enable)。七、小结Target.createCDPSession()的抽象签名很简单——无参、返回PromiseCDPSession但其背后贯穿了 Puppeteer 的核心通信链路API 层packages/puppeteer-core/src/api/Target.ts 声明抽象方法Page的会话创建最终委托至此packages/puppeteer-core/src/cdp/Page.tsCDP 实现CdpTarget.createCDPSession()经 sessionFactory 调Connection.createSession()底层发送Target.attachToTarget取得sessionIdpackages/puppeteer-core/src/cdp/Connection.tsBiDi 实现仅页面/帧级 Target 可用其余抛UnsupportedOperationpackages/puppeteer-core/src/bidi/Target.ts行为契约由 test/src/cdp/CDPSession.test.ts 的事件独立路由、域开关隔离、detach 等用例固化。当你需要监听原始 CDP 事件、调用 Puppeteer 尚未封装的协议域命令、或为 Worker/other类型 Target 建立通信通道时target.createCDPSession()就是那个正确的底层入口。关键文件索引文件说明docs/api/puppeteer.target.createcdpsession.md本方法的官方 API 文档签名与返回类型docs/api/puppeteer.target.mdTarget类总览page/worker/asPage/type 等成员docs/api/puppeteer.cdpsession.mdCDPSession用法示例与 send/on/detach 说明packages/puppeteer-core/src/api/Target.tsTarget抽象类与TargetType枚举packages/puppeteer-core/src/cdp/Target.tsCDP 协议下的CdpTarget实现packages/puppeteer-core/src/cdp/Connection.tsTarget.attachToTarget发送与会话创建packages/puppeteer-core/src/bidi/Target.tsBiDi 协议下各 Target 的行为边界test/src/cdp/CDPSession.test.tsTarget.createCDPSession行为测试套件【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考