
本文详解如何在SeleniumBase中正确启用CDP事件监听(而非依赖已废弃的goog:loggingPrefs),通过RequestWillBeSent和ResponseReceived处理器实时捕获Fetch/XHR请求及响应元数据,解决log_cdp=True无法输出performance日志的根本原因。
本文详解如何在seleniumbase中正确启用cdp事件监听(而非依赖已废弃的`goog:loggingprefs`),通过`requestwillbesent`和`responsereceived`处理器实时捕获fetch/xhr请求及响应元数据,解决`log_cdp=true`无法输出performance日志的根本原因。
SeleniumBase 的 log_cdp=True 参数并不等价于 Chrome 的 goog:loggingPrefs={"performance": "ALL"}——这是一个常见误解。自 Chrome 110+ 及 Selenium 4.x 起,performance 日志类型已被官方弃用,其底层机制(基于 DevTools Protocol 的 Log.entryAdded 事件)在无头/UC 模式下默认不可用,且 SeleniumBase 未对此做兼容性封装。真正稳定、可控、面向开发者的网络监控方式,是直接使用 CDP Mode(Chrome DevTools Protocol 模式),它绕过日志层,直连浏览器协议栈,以事件驱动方式监听网络生命周期。
✅ 正确做法:使用 CDP Mode 注册原生事件处理器
CDP Mode 是 SeleniumBase 提供的高级能力,需显式激活并注册强类型事件处理器。以下为生产级可用示例(适配 seleniumbase>=4.33.7 + Chrome 131):
import colorama
import sys
from seleniumbase import SB
# 注意:mycdp 是 SeleniumBase 内置模块,无需额外安装
import mycdp
# 控制台颜色适配(Linux/WSL 下自动禁用)
c1 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX if "linux" not in sys.platform else ""
c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX if "linux" not in sys.platform else ""
cr = colorama.Style.RESET_ALL if "linux" not in sys.platform else ""
async def on_request_sent(event: mycdp.network.RequestWillBeSent):
"""捕获即将发出的请求(含 Fetch/XHR/导航)"""
req = event.request
print(f"{c1}[REQUEST] {req.method} {req.url}{cr}")
# 可选:打印请求头(注意 headers 是 dict[str, str])
# for k, v in req.headers.items():
# print(f" {k}: {v}")
async def on_response_received(event: mycdp.network.ResponseReceived):
"""捕获已接收的响应(含状态码、URL、headers)"""
res = event.response
print(f"{c2}[RESPONSE] {res.status} {res.url}{cr}")
# ⚠️ 注意:此处仅获取响应元数据;如需响应体,须额外调用 getResponseBody(见下文进阶技巧)
# 启动 UC 模式浏览器(自动规避 webdriver 检测)
with SB(
uc=True, # 启用 undetected-chromedriver
test=True, # 启用测试增强(自动截图、失败重试等)
headless=True, # 支持 headless 和 headed 模式
locale_code="en", # 避免地区相关指纹偏差
) as sb:
# 关键:必须先激活 CDP Mode,并指定初始页面(建议用 about:blank 避免干扰)
sb.activate_cdp_mode("about:blank")
# 注册事件处理器(类型提示确保 IDE 支持 & 运行时校验)
sb.cdp.add_handler(mycdp.network.RequestWillBeSent, on_request_sent)
sb.cdp.add_handler(mycdp.network.ResponseReceived, on_response_received)
# 使用 CDP.open() 替代 driver.get() —— 确保所有导航均被 CDP 监控
target_url = "https://httpbin.org/json"
sb.cdp.open(target_url)
# 等待关键响应完成(建议用 sb.wait_for_text() 或 sb.assert_element() 更健壮)
sb.sleep(2)
? 为什么 driver.get_log("performance") 在 SeleniumBase 中失效?
-
get_log("performance")依赖旧版Log.entryAdded事件,而现代 Chrome(尤其 UC 模式)为规避检测会屏蔽该日志源; - SeleniumBase 的
Driver(..., log_cdp=True)仅开启 CDP WebSocket 连接日志(用于调试驱动通信),不启用 Network 域事件监听; -
log_cdp=True是一个误导性命名,实际用途与网络抓包无关——请彻底放弃该参数用于请求捕获。
? 进阶技巧:获取完整响应体(Response Body)
ResponseReceived 事件仅提供响应头和状态,若需原始 JSON/HTML 文本,需在响应接收后主动调用 CDP 命令:
async def on_response_received(event: mycdp.network.ResponseReceived):
res = event.response
if res.status == 200 and "json" in res.headers.get("content-type", ""):
try:
# 注意:需传入 event.request_id(来自 RequestWillBeSent 事件)
body_data = await sb.cdp.command(
"Network.getResponseBody",
{"requestId": event.request_id}
)
print("[BODY]", body_data.get("body", "[empty]"))
except Exception as e:
print("[BODY ERROR]", str(e))
? 提示:
request_id必须从RequestWillBeSent事件中提取并跨处理器传递(可通过sb.shared或闭包变量暂存),这是 CDP 协议的设计约束。
✅ 最佳实践总结
| 项目 | 推荐方案 | 禁止方案 |
|---|---|---|
| 启动模式 | SB(uc=True, headless=True) |
Driver(..., headless=True)(缺少 UC 反检测) |
| 页面导航 | sb.cdp.open(url) |
sb.get(url)(可能漏监部分动态请求) |
| 事件监听 | sb.cdp.add_handler(mycdp.network.XXX, handler) |
driver.get_log("performance") |
| 响应体获取 | await sb.cdp.command("Network.getResponseBody", {...}) |
尝试解析 performance 日志中的 base64 字段(已不可靠) |
掌握 CDP Mode 不仅解决日志缺失问题,更为实现请求拦截、响应篡改、资源注入等高级自动化场景奠定基础。真正的稳定性,永远来自对协议本质的理解,而非对表层配置的盲目调参。










