谷歌CDP(Chrome DevTools Protocol)命令详解
一、核心定义与作用
1. 什么是CDP?
- 全称:Chrome DevTools Protocol(谷歌开发者工具协议)
- 本质:一套基于 HTTP/JSON-RPC 2.0 的协议,用于与 Chromium 内核浏览器(Chrome、Edge、Brave 等)进行程序化交互
- 核心作用:替代手动操作 DevTools 界面,通过代码控制浏览器的调试、性能分析、页面自动化、数据采集等功能
- 应用场景:
- 前端自动化测试(如 Puppeteer、Playwright 底层依赖)
- 页面性能监控与分析(采集 FPS、加载时间、资源耗时)
- 网络爬虫与数据采集(绕开部分前端反爬、模拟真实用户行为)
- 浏览器扩展开发、调试工具开发
- TLS 指纹模拟、浏览器环境定制(逆向工程常用)
2. 核心优势
- 跨语言兼容:支持所有能发送 HTTP/JSON-RPC 请求的语言(JavaScript、Python、Java 等)
- 功能全面:覆盖 DevTools 所有可视化功能,且提供更底层的控制能力
- 无侵入性:无需修改目标页面代码,通过浏览器调试端口通信
- 高性能:比 Selenium 等基于 WebDriver 的工具更轻量、响应更快
二、CDP 基础工作原理
1. 通信流程
- 启动浏览器并开启调试端口:
# 命令行启动 Chrome(Windows 示例)
chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\ChromeDebug" - 获取调试目标列表:访问
http://localhost:9222/json,返回所有打开的页面/标签页信息(包含webSocketDebuggerUrl) - 建立 WebSocket 连接:通过
webSocketDebuggerUrl与目标页面建立长连接 - 发送 CDP 命令:通过 WebSocket 发送 JSON-RPC 格式的命令,浏览器执行后返回结果
2. 命令格式(JSON-RPC)
// 请求示例(启用网络监控)
{
"id": 1, // 命令唯一标识(用于匹配响应)
"method": "Network.enable", // CDP 方法名(域+方法)
"params": {} // 方法参数(可选)
}
// 响应示例(成功)
{
"id": 1,
"result": {}
}
// 响应示例(失败)
{
"id": 1,
"error": {
"code": -32601,
"message": "Method not found"
}
}
// 事件示例(网络请求完成)
{
"method": "Network.responseReceived",
"params": {
"requestId": "123",
"response": {...}
}
}
三、常用 CDP 命令分类与核心方法
CDP 命令按功能分为多个域(Domain),以下是 Web 开发/爬虫/逆向中最常用的域和方法:
1. 网络相关(Network 域)
核心用于监控、拦截、修改网络请求/响应(爬虫/逆向高频使用)
| 方法名 | 功能 | 参数示例 |
|---|---|---|
Network.enable | 启用网络监控 | {} |
Network.disable | 禁用网络监控 | {} |
Network.requestIntercepted | 拦截网络请求(需配合 setRequestInterception) | - |
Network.continueRequest | 继续被拦截的请求 | {"interceptionId": "xxx"} |
Network.modifyRequestHeaders | 修改请求头 | {"requestId": "xxx", "headers": {"User-Agent": "Chrome/120"}} |
Network.getResponseBody | 获取响应体(需 requestId) | {"requestId": "xxx"} |
Network.setExtraHTTPHeaders | 设置全局额外请求头 | {"headers": {"Referer": "https://example.com"}} |
2. 页面操作相关(Page 域)
控制页面加载、导航、截图、DOM 操作等(自动化测试常用)
| 方法名 | 功能 | 参数示例 |
|---|---|---|
Page.navigate | 导航到指定 URL | {"url": "https://example.com"} |
Page.reload | 刷新页面 | {"ignoreCache": true}(忽略缓存) |
Page.captureScreenshot | 截取页面截图 | {"format": "png", "quality": 80} |
Page.setDocumentContent | 设置页面 HTML 内容 | {"html": "<h1>Hello CDP</h1>"} |
Page.loadEventFired | 页面加载完成事件(被动接收) | - |
3. DOM 与 CSS 相关(DOM 域 + CSS 域)
操作 DOM 元素、获取节点信息、修改样式(前端调试/自动化)
| 方法名 | 功能 | 参数示例 |
|---|---|---|
DOM.getDocument | 获取根 DOM 节点 | {"depth": -1}(获取所有层级) |
DOM.querySelector | 查找单个 DOM 节点 | {"nodeId": 1, "selector": ".content"} |
DOM.getOuterHTML | 获取节点 outerHTML | {"nodeId": 123} |
CSS.setStyleText | 修改元素样式 | {"styleId": "xxx", "text": "color: red"} |
4. JavaScript 执行相关(Runtime 域)
在页面上下文执行 JS 代码、获取变量、监听异常(核心交互能力)
| 方法名 | 功能 | 参数示例 |
|---|---|---|
Runtime.evaluate | 执行 JS 表达式 | {"expression": "document.title", "returnByValue": true} |
Runtime.callFunctionOn | 调用指定函数 | {"functionDeclaration": "() => 1+2", "returnByValue": true} |
Runtime.getProperties | 获取对象属性 | {"objectId": "xxx"} |
Runtime.exceptionThrown | JS 异常事件(被动接收) | - |
5. 浏览器环境定制(Emulation 域)
模拟设备、屏幕尺寸、网络速度、TLS 指纹等(逆向/反爬常用)
| 方法名 | 功能 | 参数示例 |
|---|---|---|
Emulation.setDeviceMetricsOverride | 模拟设备尺寸 | {"width": 375, "height": 667, "deviceScaleFactor": 2} |
Emulation.setUserAgentOverride | 模拟 User-Agent | {"userAgent": "iPhone/15 Safari"} |
Emulation.setNetworkConditions | 模拟网络速度 | {"offline": false, "latency": 100, "downloadThroughput": 102400} |
Emulation.setPageScaleFactor | 设置页面缩放比例 | {"pageScaleFactor": 1.5} |
6. 调试与性能分析(Debugger 域 + Performance 域)
断点调试 JS 代码、采集性能数据(前端性能优化/逆向调试)
| 方法名 | 功能 | 参数示例 |
|---|---|---|
Debugger.enable | 启用调试器 | {} |
Debugger.setBreakpoint | 设置 JS 断点 | {"location": {"scriptId": "xxx", "lineNumber": 10}} |
Performance.enable | 启用性能监控 | {} |
Performance.getMetrics | 获取性能指标 | {}(返回 FPS、加载时间等) |
四、实战示例(Python + WebSocket 直接调用)
以下示例通过 Python 直接使用 WebSocket 调用 CDP 命令,实现「导航页面 + 截图 + 获取页面标题」:
1. 安装依赖
pip install websockets json5
2. 代码实现
import websockets
import json
import requests
# 1. 获取调试目标的 WebSocket 地址
def get_debugger_url():
response = requests.get("http://localhost:9222/json")
targets = response.json()
# 选择第一个打开的页面(可根据 title/url 筛选)
return targets[0]["webSocketDebuggerUrl"]
# 2. 发送 CDP 命令
async def send_cdp_command(ws, method, params=None, id=1):
params = params or {}
request = {
"id": id,
"method": method,
"params": params
}
await ws.send(json.dumps(request))
# 接收响应
response = await ws.recv()
return json.loads(response)
# 3. 核心逻辑
async def main():
debugger_url = get_debugger_url()
async with websockets.connect(debugger_url) as ws:
# 导航到目标页面
nav_response = await send_cdp_command(ws, "Page.navigate", {"url": "https://example.com"})
print("导航响应:", nav_response)
# 等待页面加载完成(监听 Page.loadEventFired 事件)
while True:
message = await ws.recv()
data = json.loads(message)
if data.get("method") == "Page.loadEventFired":
print("页面加载完成")
break
# 执行 JS 获取页面标题
title_response = await send_cdp_command(
ws, "Runtime.evaluate",
{"expression": "document.title", "returnByValue": True}
)
print("页面标题:", title_response["result"]["value"])
# 截取页面截图
screenshot_response = await send_cdp_command(
ws, "Page.captureScreenshot", {"format": "png"}
)
# 保存截图(base64 解码)
import base64
with open("screenshot.png", "wb") as f:
f.write(base64.b64decode(screenshot_response["result"]["data"]))
print("截图已保存")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
五、高级应用场景
1. 绕开前端反爬(如签名、加密参数)
- 通过
Runtime.evaluate执行页面内置加密函数,获取加密后参数 - 通过
Network.requestIntercepted拦截请求,修改关键参数后放行
2. TLS 指纹模拟(逆向工程常用)
- 使用
Network.setUserAgentOverride模拟浏览器 UA - 通过
Emulation.setDeviceMetricsOverride模拟设备信息 - 高级场景:使用
Security.setOverrideCertificateErrors忽略证书错误,或定制 TLS 握手参数(需配合底层工具)
3. 前端性能监控
- 启用
Performance.enable和Network.enable - 监听
Performance.metrics事件获取 FPS、布局偏移(CLS)等指标 - 结合
Network.responseReceived统计资源加载时间
六、工具与库推荐
1. 高层封装库(无需直接写 CDP 命令)
- JavaScript:Puppeteer(Chrome 官方)、Playwright(支持多浏览器)
- Python:Pyppeteer(Puppeteer Python 移植)、Playwright-Python
- Java:Selenium 4+(内置 CDP 支持)、ChromeDevTools
2. 调试与学习工具
- Chrome DevTools:
More tools > Protocol Monitor(实时查看 CDP 命令交互) - CDP 官方文档:Chrome DevTools Protocol Docs(完整命令列表)
- 在线调试工具:CDP Tester(快速测试 CDP 命令)
七、注意事项
- 浏览器版本兼容性:部分 CDP 命令可能随 Chrome 版本更新而变化,需查看文档确认兼容性
- 调试端口安全:
--remote-debugging-port开启后,本地端口可被任意程序访问,避免在公共网络环境使用 - 性能考量:高频发送 CDP 命令可能影响浏览器性能,建议按需启用/禁用监控
- 反爬风险:部分网站会检测 CDP 特征(如
navigator.webdriver),需通过Emulation.setUserAgentOverride或 JS 注入绕过
通过 CDP,你可以获得对浏览器的底层控制能力,无论是前端开发、自动化测试还是爬虫/逆向工程,都能大幅提升效率。建议从高层库(如 Puppeteer)入手,熟悉后再深入学习原生 CDP 命令,灵活应对复杂场景。