Python-Chrome-CDP入门

举报
yd_214609086 发表于 2026/11/05 11:32:03 2026/11/05
【摘要】 CDP 是什么、怎么开调试端口、Python 最小示例、常用域和一点使用建议

用 Python 调用 Chrome DevTools Protocol,从打开页面到截图

Chrome DevTools Protocol(CDP)是 Chrome 暴露出来的一套调试接口。平时按 F12 打开开发者工具,看到的 DOM、网络请求、性能数据,底层大多走的这套协议。用 Python 直接连上去,可以控制浏览器、执行 JS、截屏,也能拿到比 Selenium 更细的运行时信息。

这篇用一个最小例子走一遍:启动无头 Chrome,连上 CDP,打开一个空白页,改点内容,再截张图。

先把 Chrome 的调试端口打开

Chrome 启动时加一个参数,就会在本地开一个调试端口:

chrome --headless=new --remote-debugging-port=9222 --remote-allow-origins=* --user-data-dir=./chrome-profile about:blank

几个参数值得说一下。--headless=new 是无头模式,Chrome 112 之后用这个写法;老版本直接写 --headless。--user-data-dir 指定一个临时用户目录,免得和你平时用的浏览器配置打架。--remote-allow-origins=* 是给非浏览器客户端用的,Python 的 websocket 库连接时会带 Origin 头,不加这个参数,Chrome 可能直接返回 403。

端口起来后,访问本地的 /json/version 能看到浏览器版本和 websocket 地址,/json/list 则列出当前打开的页面。每个页面都有一个 webSocketDebuggerUrl,后面的命令都发到这个地址。

Python 这边要装什么

只需要两个库:

pip install requests websocket-client

requests 用来查 HTTP 接口,websocket-client 用来建长连接。Playwright、Selenium 这些工具把 CDP 包了一层,用起来更省事;这篇直接用原生协议,目的是看清楚消息是怎么发的。

一个能跑的最小例子

import base64
import json
import subprocess
import time

import requests
import websocket

CHROME = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
HOST = "localhost"
PORT = 9222
SCHEME = "http"
BASE = f"{SCHEME}://{HOST}:{PORT}"

proc = subprocess.Popen([
    CHROME,
    "--headless=new",
    f"--remote-debugging-port={PORT}",
    "--remote-allow-origins=*",
    "--user-data-dir=./chrome-profile",
    "about:blank",
])

for _ in range(50):
    try:
        requests.get(f"{BASE}/json/version", timeout=0.5)
        break
    except requests.RequestException:
        time.sleep(0.1)
else:
    raise RuntimeError("Chrome 没有在预期时间内启动")

targets = requests.get(f"{BASE}/json/list").json()
page = next(t for t in targets if t["type"] == "page")
ws = websocket.create_connection(page["webSocketDebuggerUrl"])

msg_id = 0

def call(method, params=None):
    global msg_id
    msg_id += 1
    ws.send(json.dumps({"id": msg_id, "method": method, "params": params or {}}))
    while True:
        msg = json.loads(ws.recv())
        if msg.get("id") == msg_id:
            return msg

call("Page.enable")
call("Runtime.enable")
call("Page.navigate", {"url": "about:blank"})
time.sleep(0.2)
call("Runtime.evaluate", {
    "expression": "document.title = 'CDP Demo'; document.body.innerHTML = '<h1>Hello CDP</h1>'",
})
time.sleep(0.2)

shot = call("Page.captureScreenshot", {"format": "png"})
with open("shot.png", "wb") as f:
    f.write(base64.b64decode(shot["result"]["data"]))

print("截图已保存到 shot.png")
ws.close()
proc.terminate()

代码不长,关键在 call()。CDP 的消息是 JSON,发出去的时候带一个自增的 id、方法名和参数。浏览器返回时会把同样的 id 带回来,所以循环读消息、只处理 id 对得上的那条就行。中间可能夹杂事件消息,比如页面加载、控制台输出,这些没有 id,跳过。

Page.navigate 打开页面,Runtime.evaluate 执行一段 JS。这里没有真的去访问网站,而是把 about:blank 的标题和正文改掉,省得等网络。最后 Page.captureScreenshot 返回 base64 的 PNG,解码写文件。

几个常用的域

CDP 的命令按“域”分组,刚开始不用记太多,这三个够用一阵子:

  • Page:导航、加载事件、截图、打印 PDF
  • Runtime:执行 JS、取返回值、监听 console
  • Network:请求列表、响应头、拦截和修改请求

比如想抓某个接口的响应,先 Network.enable,再监听 Network.responseReceived 事件,拿到 requestId 后用 Network.getResponseBody 取内容。这个过程比 Selenium 直接,但事件处理要自己写,代码量也上去了。

值不值得用

如果你只是做常规的页面自动化,Playwright 更省心,选择器、等待、截图都封装好了。直接用 CDP 的优势在于控制细:性能指标、网络拦截、DOM 快照、跨页面会话,这些都能自己拼。调试自动化脚本时,也可以用它看浏览器到底收到了什么,比猜快。

我的建议是把它当成底层的调试工具,而不是日常首选。先跑通上面的例子,再挑一个你关心的域试两条命令,基本就有感觉了。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。