Skip to content

第1章 迎接AI浏览器自动化的新范式

当浏览器不再只是"打开网页"的工具,而成为AI Agent的"手"与"眼",一切才刚刚开始。

如果你在过去一年里关注过AI领域的动态,一定对这样的场景不陌生:一个AI助手自主打开浏览器,搜索信息、填写表单、提交订单,全程无需人类干预。这不再是科幻——AutoGPT、Browser Use、WebVoyager等项目已经在GitHub上斩获数万Star,而它们背后都指向同一个核心能力:对浏览器的程序化控制

然而,当大多数教程还在教你用Selenium find_element_by_id 的时候,真正的底层玩家已经在用一种更强大、更精细、更贴近浏览器本质的方式操控一切——Chrome DevTools Protocol(CDP)

本章将带你从零理解CDP的来龙去脉,搭建开发环境,并写出你的第一个CDP程序。系好安全带,我们出发。


1.1 从Selenium到CDP:AI时代浏览器操控的进化

浏览器自动化的三代演进

浏览器自动化并非新概念。回溯其发展脉络,大致经历了三个阶段:

世代代表技术核心机制优势局限
第一代Selenium RC注入JS沙箱 + 代理服务器跨浏览器支持速度慢,沙箱限制多
第二代WebDriver (Selenium 2+)浏览器扩展/Native事件W3C标准,生态成熟仍需中间驱动层
第三代CDP / Playwright / Puppeteer直接WebSocket通信零中间层,完全控制仅限Chromium系

第一代:Selenium RC(Remote Control) 诞生于2004年,它的原理是在浏览器中注入一段JavaScript沙箱,通过代理服务器转发命令。这意味着你的操作实际上是在一个"被劫持"的JS环境中运行——任何同源策略、跨域限制都可能成为绊脚石。

第二代:WebDriver 是Selenium 2引入的革命性改进,它成为了W3C推荐标准(WebDriver Spec)。每个浏览器厂商提供自己的Driver(如ChromeDriver、geckodriver),通过HTTP协议接收指令,再转化为浏览器的原生操作。这解决了JS沙箱的问题,但引入了新的复杂性:每条命令都要经过 Client → HTTP → Driver → Browser 的漫长链路。

第三代:CDP直接通信 则是另一个维度的飞跃。它跳过所有中间层,直接通过WebSocket与浏览器的调试端口通信,命令延迟从百毫秒级降到毫秒级,同时获得了对浏览器内部状态的完全访问权——网络请求、渲染帧、内存堆栈,一切尽在掌握。

📊 图示位置:浏览器自动化三代架构对比图

  • 左侧:Selenium RC架构(Client → Proxy Server → JS Sandbox → Browser)
  • 中间:WebDriver架构(Client → HTTP → ChromeDriver → Browser)
  • 右侧:CDP架构(Client → WebSocket → Browser DevTools Port)
  • 箭头越少,延迟越低

为什么AI时代需要CDP?

Selenium够用吗?对于传统的"登录→点击→验证"测试流程,或许足够。但AI时代的浏览器自动化,需求已经发生了根本性变化:

  1. 实时感知:AI Agent需要实时获取页面状态(DOM结构、网络请求、渲染结果),而不仅仅是"找到某个元素"
  2. 精细控制:模拟真实用户行为需要控制鼠标轨迹、键盘事件时序,而非简单的click()
  3. 性能监控:AI决策需要页面性能数据作为输入,而Selenium无法直接获取渲染指标
  4. 多上下文管理:AI Agent可能同时操控多个标签页、多个浏览器实例,需要轻量的会话管理
  5. 反检测需求:AI自动化需要隐藏自身痕迹,CDP提供了修改浏览器指纹的底层能力

来看一个直观的对比——同样是获取页面的所有网络请求:

javascript
// Selenium方式:无法直接获取,需要借助第三方代理(如BrowserMob Proxy)
const proxy = new BrowserMobProxy();
proxy.start(8080);
proxy.newHar("network");
// ... 执行操作 ...
const har = proxy.getHar(); // 只能拿到HAR格式的摘要数据
javascript
// CDP方式:直接监听网络事件
const client = await CDP();
const { Network } = client;
await Network.enable();
Network.requestWillBeSent((params) => {
  console.log(`${params.request.method} ${params.request.url}`);
});
// 每个请求的详细信息实时获取,包括headers、timing、body

差距不言而喻。

AI Agent的浏览器控制范式

当前主流的AI浏览器自动化项目,几乎都建立在CDP之上:

  • Puppeteer:Google官方的Node.js CDP封装,AI项目的事实标准
  • Playwright:微软出品的跨浏览器自动化框架,底层同样使用CDP(Chromium通道)
  • Browser Use:直接基于Playwright的AI Agent框架,让LLM自主决策浏览器操作
  • LaVague:结合CDP与RAG技术的Web Agent,支持自然语言驱动浏览器

这种范式可以抽象为一个简单模型:

感知(CDP Event)→ 推理(LLM)→ 行动(CDP Method)→ 感知(CDP Event)→ ...

CDP既是AI的"眼睛"(通过Event获取页面状态),也是AI的"手"(通过Method执行操作)。理解CDP,就是理解AI操控浏览器的底层逻辑。


1.2 Chrome DevTools Protocol的核心价值与架构解析

什么是Chrome DevTools Protocol?

Chrome DevTools Protocol(CDP)是Chrome浏览器提供的调试协议,它允许外部程序通过WebSocket连接与浏览器通信,控制浏览器的行为并获取其内部状态。

这个名字可能会让人误解——"DevTools"是不是只能用于开发者工具?实际上,CDP的功能远超DevTools界面所展示的范畴。你平时在Chrome DevTools中看到的一切功能(Elements面板、Console、Network面板、Performance面板……),都是通过CDP实现的。反过来说,CDP的能力也远超DevTools界面,因为很多底层API并没有在UI中暴露。

🔗 官方文档Chrome DevTools Protocol Viewer

CDP架构全景

CDP的架构可以用一个三层模型来描述:

┌─────────────────────────────────────────┐
│           Application Layer             │
│   (Puppeteer / Playwright / 自定义客户端)  │
├─────────────────────────────────────────┤
│          Transport Layer                │
│        (WebSocket / JSON-RPC)           │
├─────────────────────────────────────────┤
│          Browser Layer                  │
│   (Chrome DevTools Backend / CDP Server)│
└─────────────────────────────────────────┘

传输层:CDP基于WebSocket传输,消息格式采用JSON-RPC 2.0。每条消息要么是请求(Request),要么是响应(Response),要么是事件通知(Event)。

浏览器层:Chrome在启动时可以通过--remote-debugging-port参数开放调试端口。连接建立后,浏览器内部有一个CDP Server,负责将协议命令分发给对应的处理模块。

应用层:你可以直接使用WebSocket客户端与CDP通信,也可以使用高级封装库(如Puppeteer、Playwright)。后者隐藏了WebSocket和JSON-RPC的细节,提供更友好的API。

Domain:CDP的功能组织单元

CDP将所有功能组织为若干Domain(域),每个Domain负责一类相关功能。以下是核心Domain的概览:

Domain职责典型用途
Target管理浏览器中的调试目标(标签页、iframe等)发现和附加到指定页面
Page页面级操作导航、截图、打印PDF
RuntimeJavaScript执行环境执行JS表达式、监听Console
DOMDOM树操作查询节点、修改属性
CSS样式操作获取/修改计算样式
Network网络监控与拦截监听请求、修改响应
Emulation设备模拟模拟移动设备、地理位置
Performance性能数据采集获取指标、录制Trace
Security安全状态处理证书错误
DOMDebuggerDOM断点设置事件监听断点
DebuggerJS调试设置断点、单步执行
Profiler性能分析CPU Profile、堆快照
IO输入输出流式读取大数据
Log日志监听获取控制台日志
Fetch网络请求拦截修改请求/响应内容

每个Domain下包含三类成员:

  • Method(方法):可调用的命令,如Page.navigate
  • Event(事件):浏览器主动推送的通知,如Network.requestWillBeSent
  • Type(类型):结构化的数据类型定义,如Network.Request

Method、Event与Type的协作

以一个完整的"导航到URL并等待加载完成"为例,展示三者的协作:

javascript
// 1. 启用Page域(必须先enable才能接收事件)
await Page.enable();

// 2. 监听加载完成事件(Event)
Page.loadEventFired(() => {
  console.log('页面加载完成!');
});

// 3. 调用导航方法(Method)
await Page.navigate({ url: 'https://www.baidu.com' });

这个过程体现了CDP的典型模式:先enable → 再监听event → 然后调用method。几乎所有Domain都遵循这一模式。

CDP消息格式

所有CDP消息都遵循JSON-RPC 2.0格式。来看实际的请求与响应:

请求(调用Method)

json
{
  "id": 1,
  "method": "Page.navigate",
  "params": {
    "url": "https://www.baidu.com"
  }
}

响应

json
{
  "id": 1,
  "result": {
    "frameId": "main",
    "loaderId": "1234567890"
  }
}

事件通知

json
{
  "method": "Page.loadEventFired",
  "params": {
    "timestamp": 1718294400000
  }
}

注意关键区别:请求和响应用id字段关联,而事件通知没有id(因为它们不是对某个请求的响应,而是浏览器主动推送的)。

🔗 参考JSON-RPC 2.0 Specification

Session:多目标通信的关键

当浏览器中有多个标签页时,CDP需要一种机制来区分命令应该发送到哪个目标。这就是Session的概念。

javascript
// 1. 获取所有目标
const { targetInfos } = await Target.getTargets();

// 2. 附加到特定目标,创建Session
const { sessionId } = await Target.attachToTarget({
  targetId: targetInfos[0].targetId,
  flatten: true  // 使用"扁平化"Session(推荐)
});

// 3. 在该Session下发送命令
await cdp.send('Page.navigate', { url: 'https://example.com' }, sessionId);

扁平化Session(flatten: true)是CDP 1.3引入的重要特性,它允许在同一条WebSocket连接上通过sessionId区分不同目标的消息,避免了为每个目标创建独立WebSocket连接的开销。


1.3 CDP与Chrome浏览器版本的兼容性矩阵

版本对应关系

CDP与Chrome版本是强绑定的——每个Chrome大版本对应一组CDP API。Chrome每6周发布一个大版本,CDP也会随之更新。

CDP采用语义化版本号,格式为major.minor

Chrome版本CDP版本关键新增特性
901.3Fetch域、扁平化Session
1001.3FedCm域(联邦登录管理)
1101.3Preload域、Storage域扩展
1201.3Extensions域(扩展管理)
125+1.3AuctionWorklet域(Privacy Sandbox)

🔗 完整版本列表CDP Version History

实验性API与稳定API

CDP中的Method和Event分为两类:

  • 稳定(Stable):已正式发布,向后兼容,可以安全用于生产环境
  • 实验性(Experimental):标记为"experimental": true,可能在后续版本中变更或移除

在官方文档中,实验性API会以特殊标识显示:

json
{
  "name": "Page.adScriptStarted",
  "experimental": true,
  "description": "..."
}

实践建议

  1. 生产环境优先使用稳定API,实验性API需要做降级处理
  2. 定期查看CDP变更日志,关注Breaking Changes
  3. 在Puppeteer/Playwright中,实验性API通常会被封装并标注为实验性

查询特定版本的CDP信息

你可以通过以下方式查询特定Chrome版本支持的CDP API:

bash
# 方法1:通过Chrome的JSON端点
curl http://localhost:9222/json/version
# 返回包含webSocketDebuggerUrl和Browser版本信息
javascript
// 方法2:通过CDP协议查询
const { Browser } = client;
const version = await Browser.getVersion();
console.log(version.product);     // Chrome/126.0.6478.114
console.log(version.userAgent);   // 完整UA字符串

兼容性实战策略

在AI自动化项目中,兼容性问题通常出现在以下场景:

  1. Docker部署:Docker镜像中的Chrome版本可能与本地不同
  2. CI/CD环境:GitHub Actions等CI环境中的Chrome版本会自动更新
  3. 远程浏览器:BrowserPool等远程浏览器服务的版本可能滞后

推荐的做法是锁定Chrome版本,而非追逐最新版:

dockerfile
# Dockerfile示例:锁定Chrome版本
FROM node:20-slim

# 安装指定版本的Chrome
RUN wget -q https://dl.google.com/linux/direct/google-chrome-stable_126.0.6478.114-1_amd64.deb \
    && dpkg -i google-chrome-stable_126.0.6478.114-1_amd64.deb \
    && rm google-chrome-stable_126.0.6478.114-1_amd64.deb

对于无法锁定版本的场景,可以使用Feature Detection模式:

javascript
// 检查某个Method是否可用
async function isMethodSupported(client, domain, method) {
  try {
    // 获取域的描述信息
    const { domains } = await client.send('Schema.getDomains');
    const targetDomain = domains.find(d => d.name === domain);
    return !!targetDomain;
  } catch {
    return false;
  }
}

1.4 开发环境搭建:Node.js/Python环境与调试配置

Node.js环境搭建

Node.js是CDP开发的首选环境——毕竟Puppeteer是Google官方的Node.js库,生态最完善。

1. 安装Node.js

推荐使用nvm管理Node.js版本:

bash
# 安装nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# 安装LTS版本
nvm install --lts
nvm use --lts

# 验证
node --version  # v20.x 或更高
npm --version

🔗 nvm官方仓库https://github.com/nvm-sh/nvm

2. 创建项目

bash
mkdir cdp-demo && cd cdp-demo
npm init -y

# 安装核心依赖
npm install chrome-remote-interface  # CDP底层客户端
npm install puppeteer                # 高级封装(自带Chrome)

chrome-remote-interface是最接近CDP协议原生的Node.js客户端,适合学习协议细节;puppeteer是高级封装,适合实际项目开发。本章后续会同时使用两者。

3. TypeScript配置(可选但推荐)

bash
npm install -D typescript @types/node tsx
npx tsc --init

修改tsconfig.json关键配置:

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "./dist"
  }
}

Python环境搭建

Python生态中,CDP开发主要通过pychromeplaywright进行。

1. 创建虚拟环境

bash
# 使用venv
python3 -m venv cdp-env
source cdp-env/bin/activate  # macOS/Linux
# cdp-env\Scripts\activate   # Windows

# 安装依赖
pip install pychrome           # CDP底层客户端
pip install playwright         # 高级封装
python -m playwright install chromium  # 安装浏览器

🔗 pychrome仓库https://github.com/nicholasgasior/pychrome
🔗 Playwright Python文档https://playwright.dev/python/

Chrome调试模式启动

无论使用哪种语言,都需要以调试模式启动Chrome:

bash
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chrome-debug

# Linux
google-chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chrome-debug \
  --no-first-run

# Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
  --remote-debugging-port=9222 ^
  --user-data-dir=%TEMP%\chrome-debug

关键参数说明:

参数作用说明
--remote-debugging-port=9222开启调试端口默认9222,可自定义
--user-data-dir指定用户数据目录避免与日常浏览器的Profile冲突
--no-first-run跳过首次运行向导自动化场景必加
--headless=new无头模式服务器部署必备(新版Headless)
--disable-gpu禁用GPU加速部分Linux环境需要
--window-size=1920,1080设置窗口大小影响视口和截图尺寸

验证调试端口

启动Chrome后,访问调试端点验证是否成功:

bash
# 获取浏览器版本信息
curl http://localhost:9222/json/version

# 获取所有可调试的页面
curl http://localhost:9222/json/list

成功的响应应该类似:

json
{
  "Browser": "Chrome/126.0.6478.114",
  "Protocol-Version": "1.3",
  "User-Agent": "Mozilla/5.0 ...",
  "V8-Version": "12.6.228.9",
  "WebKit-Version": "537.36",
  "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/xxx"
}

📊 图示位置:CDP开发环境架构图

  • 上方:开发者IDE / 终端
  • 中间:Node.js / Python 进程(CDP Client)
  • 下方:Chrome进程(调试端口9222)
  • 连接线:WebSocket通信

调试技巧:Chrome DevTools Inspector

Chrome本身提供了一个Web界面的DevTools Inspector,可以直接在浏览器中调试CDP连接:

  1. 启动调试模式的Chrome
  2. 在另一个浏览器窗口中访问 http://localhost:9222
  3. 你会看到所有可调试的页面列表
  4. 点击任意页面,会打开一个完整的DevTools窗口

这在开发过程中非常实用——你可以同时看到CDP命令的执行效果和DevTools中的状态变化。


1.5 第一个CDP程序:远程控制浏览器打开百度

理论够多了,现在让我们动手写代码。我们将用三种方式实现同一个目标:远程控制Chrome浏览器打开百度首页

方式一:使用chrome-remote-interface(底层方式)

这是最接近CDP协议原生的实现,适合理解协议细节。

javascript
// hello-cdp.js
const CDP = require('chrome-remote-interface');

async function main() {
  // 1. 建立CDP连接
  const client = await CDP({
    host: 'localhost',
    port: 9222
  });

  // 2. 解构获取需要的Domain
  const { Page, Runtime, Network } = client;

  // 3. 启用必要的Domain
  await Promise.all([
    Page.enable(),
    Network.enable()
  ]);

  // 4. 监听页面加载事件
  Page.loadEventFired(() => {
    console.log('✅ 页面加载完成!');

    // 获取页面标题
    Runtime.evaluate({ expression: 'document.title' }).then(result => {
      console.log(`📄 页面标题: ${result.result.value}`);
    });
  });

  // 5. 监听网络请求
  Network.requestWillBeSent((params) => {
    console.log(`🌐 ${params.request.method} ${params.request.url}`);
  });

  // 6. 导航到百度
  await Page.navigate({ url: 'https://www.baidu.com' });
  console.log('🚀 正在导航到百度...');
}

main().catch(console.error);

运行:

bash
# 先确保Chrome已在调试模式下运行
node hello-cdp.js

输出示例:

🚀 正在导航到百度...
🌐 GET https://www.baidu.com
🌐 GET https://www.baidu.com/favicon.ico
✅ 页面加载完成!
📄 页面标题: 百度一下,你就知道

方式二:使用Puppeteer(高级封装方式)

Puppeteer隐藏了CDP的底层细节,同时暴露了page.createCDPSession()方法,允许你在需要时直接访问CDP。

javascript
// hello-puppeteer.js
const puppeteer = require('puppeteer');

async function main() {
  // 1. 连接到已有的Chrome实例
  const browser = await puppeteer.connect({
    browserURL: 'http://localhost:9222'
  });

  // 2. 创建新页面
  const page = await browser.newPage();

  // 3. 创建CDP Session(可选,用于高级操作)
  const cdpSession = await page.createCDPSession();

  // 4. 监听网络请求(通过CDP Session)
  cdpSession.on('Network.requestWillBeSent', (params) => {
    console.log(`🌐 ${params.request.method} ${params.request.url}`);
  });
  await cdpSession.send('Network.enable');

  // 5. 导航到百度
  await page.goto('https://www.baidu.com', {
    waitUntil: 'networkidle2'  // 等待网络空闲
  });

  // 6. 获取页面标题
  const title = await page.title();
  console.log(`📄 页面标题: ${title}`);

  // 7. 截图保存
  await page.screenshot({ path: 'baidu.png' });
  console.log('📸 截图已保存为 baidu.png');
}

main().catch(console.error);

🔗 Puppeteer官方文档https://pptr.dev/

方式三:使用Python + pychrome

python
# hello_cdp.py
import pychrome

def on_request(params):
    request = params.get('request', {})
    print(f"🌐 {request.get('method', '?')} {request.get('url', '?')}")

def main():
    # 1. 创建浏览器连接
    browser = pychrome.Browser(url="http://localhost:9222")

    # 2. 获取或创建标签页
    tab = browser.list_tab()[0]
    tab.start()

    # 3. 启用Network域并监听事件
    tab.call_method("Network.enable")
    tab.on("Network.requestWillBeSent", on_request)

    # 4. 导航到百度
    tab.call_method("Page.navigate", url="https://www.baidu.com")
    tab.wait(3)  # 等待页面加载

    # 5. 获取页面标题
    result = tab.call_method("Runtime.evaluate", expression="document.title")
    print(f"📄 页面标题: {result.get('result', {}).get('value', '未知')}")

    tab.stop()

if __name__ == "__main__":
    main()

三种方式对比

维度chrome-remote-interfacePuppeteerpychrome
协议透明度⭐⭐⭐ 直接操作CDP⭐⭐ 封装后可降级⭐⭐⭐ 直接操作CDP
学习曲线中等中等
文档完善度一般优秀较少
生态与社区小众庞大小众
适合场景学习CDP协议、定制化开发生产项目、AI AgentPython生态集成
自动管理Chrome❌ 需手动启动✅ 可自动启动❌ 需手动启动

初学者建议:先从Puppeteer入手快速上手,遇到封装不足的场景时降级到cdpSession.send()直接调用CDP Method。等对协议理解深入后,再尝试chrome-remote-interface做更精细的控制。

一个更完整的示例:搜索百度

让我们在第一个CDP程序的基础上,增加一个实际操作——在百度搜索框中输入内容并搜索:

javascript
// baidu-search.js
const CDP = require('chrome-remote-interface');

async function main() {
  const client = await CDP({ port: 9222 });
  const { Page, Runtime, DOM, Input } = client;

  await Promise.all([Page.enable(), DOM.enable()]);

  // 导航到百度
  Page.loadEventFired(async () => {
    console.log('✅ 百度页面加载完成');

    // 1. 找到搜索框并聚焦
    const { root } = await DOM.getDocument();
    const { nodeId } = await DOM.querySelector({
      nodeId: root.nodeId,
      selector: '#kw'
    });

    // 2. 设置搜索框的值
    await DOM.setAttributeValue({
      nodeId,
      name: 'value',
      value: 'Chrome DevTools Protocol'
    });

    // 3. 模拟输入事件(让百度感知到输入)
    await Runtime.evaluate({
      expression: `
        const input = document.querySelector('#kw');
        input.dispatchEvent(new Event('input', { bubbles: true }));
      `
    });

    // 4. 点击搜索按钮
    const { nodeId: btnNodeId } = await DOM.querySelector({
      nodeId: root.nodeId,
      selector: '#su'
    });

    // 获取按钮的位置坐标
    const { model } = await DOM.getBoxModel({ nodeId: btnNodeId });
    const x = (model.border[0] + model.border[2]) / 2;
    const y = (model.border[1] + model.border[5]) / 2;

    // 5. 模拟鼠标点击
    await Input.dispatchMouseEvent({
      type: 'mousePressed', x, y, button: 'left', clickCount: 1
    });
    await Input.dispatchMouseEvent({
      type: 'mouseReleased', x, y, button: 'left', clickCount: 1
    });

    console.log('🔍 已搜索: Chrome DevTools Protocol');
  });

  await Page.navigate({ url: 'https://www.baidu.com' });
}

main().catch(console.error);

这个示例展示了CDP的核心操作流程:查找DOM节点 → 获取位置信息 → 模拟用户输入。虽然比Puppeteer的page.type()page.click()繁琐,但你获得了对每一步的完全控制——这正是CDP的价值所在。

常见问题与排查

问题原因解决方案
ECONNREFUSEDChrome未启动或端口错误检查Chrome是否带--remote-debugging-port启动
TypeError: Cannot read property 'nodeId'元素未找到确保在loadEventFired后再操作DOM
连接成功但无响应Domain未enable先调用Domain.enable()再操作
多个Chrome实例冲突调试端口被占用使用不同的--user-data-dir和端口
截图空白页面未渲染完成使用waitUntil: 'networkidle0'等待

本章小结

这一章我们从浏览器自动化的演进史出发,理解了CDP为何成为AI时代浏览器控制的核心技术。让我们回顾关键要点:

  1. CDP是第三代浏览器自动化技术,通过WebSocket直接通信,延迟更低、控制更精细
  2. CDP以Domain为功能组织单元,核心Domain包括Target、Page、DOM、Runtime、Network等
  3. CDP消息遵循JSON-RPC 2.0格式,分为Method调用、Event通知和Type定义三类
  4. 开发环境搭建需要Node.js/Python + 调试模式Chrome,Puppeteer是最推荐的上手工具
  5. 第一个CDP程序只需5步:连接 → enable → 监听 → 调用 → 处理结果

下一章,我们将深入CDP的协议细节,理解Domain、Method与Event的协作机制,掌握构建稳定CDP应用的核心技能。

💡 动手练习:尝试修改第一个CDP程序,让它打开GitHub并获取页面中所有仓库链接。提示:使用DOM.querySelectorAllRuntime.evaluate两种方式分别实现,体会它们的差异。

热爱生活,喜好美食,追求未来!