在 Web 自动化与反爬虫的对抗中,很多开发者会遇到一个困惑:明明已经改了 User-Agent、隐藏了无头模式,Playwright 刚启动访问目标网站,还是会被立刻识别拦截。其中最核心、也最容易被忽略的原因,就是CDP 检测—— 一种直接命中自动化工具底层通信协议的检测技术。
一、什么是 CDP 检测
1. CDP 是什么
CDP 全称Chrome DevTools Protocol(Chrome 开发者工具协议),是 Chromium 内核浏览器原生提供的一套调试与控制协议。它原本的设计目的是让开发者工具(F12)能够通过 WebSocket 连接,实现对浏览器的底层控制:比如操作 DOM、执行 JavaScript、捕获网络请求、分析性能等。
Playwright、Puppeteer、Selenium 4 等主流自动化工具,本质上都是通过 CDP 协议向浏览器发送指令,完成页面导航、元素点击、数据抓取等操作。
2. CDP 检测的定义
CDP 检测是反爬虫与反欺诈系统使用的一类技术手段:通过探测浏览器运行环境中是否存在 CDP 连接留下的特征与痕迹,判断当前浏览器是否被自动化程序远程控制,而非真人操作。
和传统的 Canvas 指纹、WebGL 指纹不同,CDP 检测不关心 “你用的是什么设备”,而是直接回答 “有没有脚本在背后操纵浏览器” 这个核心问题,因此检测权重更高,也更难绕过。
二、为什么 Playwright 一启动就会被识别
Playwright 对 Chromium 的控制完全建立在 CDP 之上,浏览器启动的瞬间,CDP 会话就已经建立,并在 JavaScript 运行时中留下了多处可被探测的痕迹。这就是为什么很多时候页面刚加载,甚至还没执行任何操作,就已经被识别。
1.navigator.webdriver属性标记
这是最基础也最广为人知的检测点。当浏览器通过 CDP 被自动化工具控制时,navigator.webdriver属性会被自动设置为true,而正常用户的浏览器中该值为false。
网站只需要一行 JavaScript 代码就能完成检测:
if (navigator.webdriver) { // 判定为自动化工具 }虽然 Playwright 后续版本尝试隐藏这个属性,但修改属性描述符本身也会产生新的特征,依然可以被高级检测脚本识别。
2.Runtime.enable运行时痕迹
这是 CDP 检测最核心、最隐蔽的检测点,也是 “一启动就被识别” 的主要原因。
Playwright 在建立 CDP 连接后,会默认调用Runtime.enable命令来接收执行上下文通知。这个调用会在 V8 引擎内部设置一个调试标志,产生一个副作用:当页面向控制台输出一个带有 getter 的对象时,这个 getter 会被自动触发执行。
正常浏览器中,控制台不会主动读取对象的 getter;而 CDP 开启后,为了展示对象属性,会自动调用所有 getter。反爬脚本正是利用这个差异,通过构造一个带 getter 的对象并输出到控制台,观察 getter 是否被自动触发,就能精准判断 CDP 是否处于激活状态。
3. CDP 专属全局属性泄露
CDP 连接和自动化工具会在 window 对象上注入一些正常浏览器不存在的属性:
- ChromeDriver 遗留的
window.cdc_adoQpoasnfa76pfcZLmcfl_*系列属性 - Playwright 注入的
__playwright_binding__等内部绑定对象 window.__cdp相关的内部状态属性
这些属性名通常是随机或特征化的字符串,真人浏览器环境中绝不会出现,扫描到即可直接判定为自动化。
4. 执行上下文隔离特征
通过 CDP 的Runtime.evaluate执行的 JavaScript 代码,会运行在一个独立的隔离世界(Isolated World)中,和页面自身的 JavaScript 不在同一个上下文。
反爬脚本可以通过 Symbol 属性、函数调用栈、定时器执行顺序等方式,探测到是否存在外来的隔离执行上下文,从而识别 CDP 控制的存在。
5. 其他 CDP 侧信道特征
- 控制台 API 行为差异:CDP 连接后,
console.debug、console.dir等方法的内部行为与正常浏览器存在细微差别 - 事件可信标志缺失:CDP 模拟的点击、键盘事件,缺少真人输入事件特有的
isTrusted = true标志 - WebSocket 连接状态:浏览器本地会开放调试端口并维持 WebSocket 连接,高级检测可以通过网络侧特征识别
三、CDP 检测 vs 普通浏览器指纹检测
很多开发者容易把 CDP 检测和普通的浏览器指纹检测混为一谈,实际上两者是完全不同的检测维度:
表格
| 检测类型 | 检测目标 | 检测层面 | 绕过难度 |
|---|---|---|---|
| 浏览器指纹检测 | 设备型号、显卡、字体、插件等环境特征 | 应用层 | 中等,可通过伪装参数绕过 |
| CDP 检测 | 浏览器是否被远程调试 / 控制 | 运行时底层 | 高,触及协议本身的设计 |
简单来说:指纹检测回答 “你是谁”,CDP 检测回答 “你是不是真人在操作”。即使你把所有指纹都伪装得和真人一模一样,只要 CDP 连接存在,依然会被精准识别。
四、常见的 CDP 检测绕过思路
1. Stealth 插件补丁
社区最常用的方案是使用playwright-stealth(Python)或playwright-extra+puppeteer-extra-plugin-stealth(Node.js)。这类插件会在页面加载前注入 JavaScript 补丁,覆盖navigator.webdriver、伪造window.chrome对象、修复控制台行为差异,隐藏大部分表层 CDP 特征。
但这类方案属于JavaScript 层面的修补,无法从根本上消除Runtime.enable带来的底层特征,面对 DataDome、Cloudflare Turnstile 等高级反爬系统时效果有限。
2. 改造 CDP 调用的增强版本
以 Patchright、rebrowser-patches 为代表的方案,直接修改了 Playwright 内部的 CDP 调用逻辑:
- 避免不必要的
Runtime.enable调用 - 移除 CDP 绑定注入模式
- 优化脚本执行上下文的隔离方式
这类方案从协议层面减少了 CDP 痕迹,绕过能力比普通 stealth 插件更强,但依然无法完全消除 CDP 连接本身的存在。
3. 附着到真实浏览器
不使用 Playwright 自带的 Chromium,而是启动一个普通用户安装的 Chrome 浏览器,开启远程调试端口,再让 Playwright 通过 CDP 附着上去。这种方式使用真实的浏览器二进制文件,TLS 指纹、启动参数、内置插件都和真人浏览器一致,能大幅降低被识别概率。
4. 注意事项
- CDP 检测只是反爬体系的一环,即使绕过了 CDP 检测,TLS 指纹(JA3/JA4)、IP 信誉、行为模式等依然可能导致被拦截
- 尽量使用
headless=false有头模式,无头模式会叠加更多可疑特征 - 绕过 CDP 检测是一个持续对抗的过程,没有一劳永逸的方案
总结
CDP 检测之所以能精准识别 Playwright,本质上是因为 “控制浏览器” 这个行为本身就会留下痕迹 —— 只要你通过 CDP 协议去操纵浏览器,就必然会在运行时中产生特征。
理解 CDP 检测的原理,能帮助我们跳出 “改 UA、换指纹” 的表层对抗思路,从协议层面去理解自动化工具的暴露面。在实际场景中,往往需要结合协议层补丁、真实浏览器、住宅 IP、行为模拟等多种手段,才能达到较好的绕过效果。