【DrissionPage源码-1】dp和selenium的异同

一、selenium架构详解/为什么多了一层

核心问题:为什么说 Selenium "多了一层"?

让我们先对比 CDP 和 Selenium 的架构:

CDP 架构(2 层)

plain 复制代码
Python 代码
    ↓ WebSocket(直连)
Chrome 浏览器

Selenium 架构(3 层,Selenium 3)

plain 复制代码
Python 代码
    ↓ HTTP
chromedriver(中间层!)
    ↓ CDP
Chrome 浏览器

关键区别 :Selenium 多了 chromedriver 这一层!


Selenium WebDriver 完整架构(Selenium 3)

plain 复制代码
┌─────────────────────────────────────────────────────────┐
│                 第 1 层:Client Library                  │
│              (Python, Java, C#, etc.)                   │
│                                                          │
│  from selenium import webdriver                         │
│  driver = webdriver.Chrome()                            │
│  driver.get('https://example.com')                      │
└────────────────────┬────────────────────────────────────┘
                     │
                     │ JSON Wire Protocol over HTTP
                     │ (REST API 调用)
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│          第 2 层:Browser Driver(中间层)                │
│          (chromedriver, geckodriver, etc.)              │
│                                                          │
│  - 接收 HTTP 请求                                        │
│  - 解析 JSON 命令                                        │
│  - 转换为浏览器特定的命令                                 │
│  - 内部使用 CDP/DevTools 与浏览器通信                     │
└────────────────────┬────────────────────────────────────┘
                     │
                     │ CDP (Chrome DevTools Protocol)
                     │ 或其他浏览器原生协议
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              第 3 层:Real Browser                       │
│          (Chrome, Firefox, Safari, etc.)                │
│                                                          │
│  - 真实的浏览器实例                                       │
│  - 执行 DOM 操作、JS 代码等                               │
└─────────────────────────────────────────────────────────┘

详细解析每一层

第 1 层:Selenium Client Library

这是你写的 Python/Java 代码:

python 复制代码
from selenium import webdriver

# 创建 driver 实例
driver = webdriver.Chrome()

# 执行操作
driver.get('https://www.baidu.com')
element = driver.find_element('id', 'kw')
element.send_keys('hello')
element.submit()

功能

  • 提供面向对象的 API
  • 将你的命令转换为 HTTP 请求
  • 支持多种语言(Python, Java, C#, Ruby, JavaScript)

第 2 层:Browser Driver(关键的"中间层")

这是 chromedriver.exegeckodriver.exe 等独立的可执行文件。

为什么需要这一层?

历史原因

  • Selenium 最初设计时,浏览器还没有标准化的自动化协议
  • 每个浏览器的内部机制不同(Chrome 用 CDP,Firefox 用 Marionette)
  • Selenium 需要一个"翻译层"来统一这些差异
chromedriver 的工作流程
python 复制代码
# 当你执行这行代码
driver.get('https://www.baidu.com')

# 实际发生的事情:

1. Client Library → chromedriver(HTTP 请求)

http 复制代码
POST http://localhost:9515/session/xxx/url HTTP/1.1
Content-Type: application/json

{
  "url": "https://www.baidu.com"
}

2. chromedriver 内部(解析 + 转换)

javascript 复制代码
// chromedriver 收到 JSON 请求
// 解析命令:"navigate to URL"
// 转换为 CDP 命令

// 通过 CDP 发送到 Chrome:
{
  "id": 1,
  "method": "Page.navigate",
  "params": {"url": "https://www.baidu.com"}
}

3. chromedriver → Chrome(CDP WebSocket)

chromedriver 内部维护了一个到 Chrome 的 WebSocket 连接,使用 CDP 协议通信。

4. Chrome 执行 → 返回结果

Chrome 导航到页面后,通过 CDP 返回结果:

json 复制代码
{
  "id": 1,
  "result": {"frameId": "xxx", "loaderId": "yyy"}
}

5. chromedriver → Client(HTTP 响应)

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json

{
  "status": 0,
  "value": null
}

为什么 Selenium 比 CDP 慢?

正是因为这个"中间层"!

性能对比

操作 CDP (直连) Selenium (3 层)
通信路径 Python → Chrome Python → chromedriver → Chrome
协议转换 0 次 2 次(JSON Wire ↔ CDP)
网络开销 1 次 WebSocket 2 次(HTTP + WebSocket)
延迟 ~5ms ~15-30ms

实际测试

python 复制代码
import time

# CDP (Playwright)
from playwright.sync_api import sync_playwright
start = time.time()
with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto('https://www.baidu.com')
print(f'CDP: {time.time() - start:.2f}s')  # ~0.5s

# Selenium
from selenium import webdriver
start = time.time()
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
print(f'Selenium: {time.time() - start:.2f}s')  # ~1.2s

Selenium 4 的改进

Selenium 4 移除了 JSON Wire Protocol,直接使用 W3C WebDriver Protocol

plain 复制代码
Selenium 3:
Client → JSON Wire → chromedriver → CDP → Chrome
         (自定义协议)

Selenium 4:
Client → W3C WebDriver → chromedriver → CDP → Chrome
         (标准化协议,更快)

改进

  • 标准化通信协议(W3C 标准)
  • 更好的跨浏览器兼容性
  • 稍微提升了性能(但仍然慢于直接 CDP)

chromedriver 的内部实现(C++ 源码)

chromedriver 是开源的,位于 Chromium 仓库中:

cpp 复制代码
// chromium/src/chrome/test/chromedriver/server/http_handler.cc

void HttpHandler::HandleRequest(const HttpRequest& request, 
                                HttpResponse* response) {
  // 1. 解析 HTTP 请求
  std::string method = request.method;
  std::string path = request.path;
  std::string body = request.body;
  
  // 2. 根据 WebDriver 命令分发
  if (path == "/session") {
    CreateSession(body, response);
  } else if (path.find("/url") != std::string::npos) {
    Navigate(body, response);
  }
  // ... 其他命令
}

void HttpHandler::Navigate(const std::string& body, 
                           HttpResponse* response) {
  // 3. 解析 JSON
  base::Value parsed = base::JSONReader::Read(body);
  std::string url = parsed.FindKey("url")->GetString();
  
  // 4. 转换为 CDP 命令
  base::Value cdp_command;
  cdp_command.SetKey("method", "Page.navigate");
  cdp_command.SetKey("params", base::Value::Dict().Set("url", url));
  
  // 5. 通过 DevToolsClient 发送到 Chrome
  devtools_client_->SendCommand(cdp_command);
  
  // 6. 等待响应并返回
  // ...
}

关键文件

  • chrome/test/chromedriver/server/ - HTTP 服务器
  • chrome/test/chromedriver/chrome/ - CDP 客户端
  • chrome/test/chromedriver/commands.cc - 命令实现

为什么不能去掉 chromedriver?

你可能会问:既然 chromedriver 内部用的是 CDP,为什么不直接用 CDP?

Selenium 的优势(尽管慢)

  1. 跨浏览器统一接口
    • 一套代码支持 Chrome、Firefox、Safari、Edge
    • 不需要关心底层协议差异
  2. W3C 标准
    • WebDriver 是 W3C 官方标准
    • 大量工具和生态支持
  3. 历史兼容性
    • 海量现有代码和教程
    • 企业级稳定性

CDP 的优势(为什么更快)

  1. 直连
    • 没有中间层
    • 减少协议转换开销
  2. 完整功能
    • CDP 提供比 WebDriver 更多的功能
    • 网络拦截、性能监控、覆盖率等
  3. 原生性能
    • Chrome 官方协议
    • 最优化的通信

总结对比

CDP(DrissionPage, Playwright, Puppeteer)

plain 复制代码
┌──────────┐
│ Python   │
└────┬─────┘
     │ WebSocket (直连)
     ▼
┌──────────┐
│ Chrome   │
└──────────┘

Selenium

plain 复制代码
┌──────────┐
│ Python   │
└────┬─────┘
     │ HTTP
     ▼
┌──────────┐
│chromedriver│ ← 多了这一层!
└────┬─────┘
     │ CDP
     ▼
┌──────────┐
│ Chrome   │
└──────────┘

二、selenium和drissionpage 相比多了哪些风险点

Selenium 的检测风险主要源于它必须遵守 W3C WebDriver 标准 ,以及它为了实现跨浏览器控制而引入的中间层(ChromeDriver)特征

相比 DrissionPage,Selenium 多了以下几个致命的风险监测点:


1. 致命特征:navigator.webdriver

这是最基础也是最直接的检测点。

  • Selenium:
    根据 W3C 协议规定,凡是通过 WebDriver 控制的浏览器,必须 将 navigator.webdriver 属性设为 true。
    • 风险: 网站 JS 只需要一行代码 if (navigator.webdriver) { 杀无赦 }。虽然可以通过 JS 注入去修改它,但在 Selenium 中,页面加载和 JS 注入之间存在时间差(Race Condition),在页面初始化的瞬间(Script Execution Start),这个值为 true,极易被捕捉。
  • DrissionPage:
    由于它直接通过 CDP 的 Page.addScriptToEvaluateOnNewDocument 在页面加载注入 JS,它可以完美地将这个值设为 undefined,让网页完全感知不到。

2. 源代码特征:CDC 变量 (CDC_xxxxxxxx)

这是 Selenium(特指 ChromeDriver)独有的深层特征。

  • Selenium:
    ChromeDriver 为了在浏览器内部执行 JavaScript 并与外部通信,会在浏览器的 JS 上下文中注入特定的全局变量。最著名的就是以 cdc<fontstyle="color:rgb(26,28,30);"></font><fontstyle="color:rgb(26,28,30);">开头的变量(例如</font><fontstyle="color:rgb(26,28,30);"></font>cdc_<font style="color:rgb(26, 28, 30);"> </font><font style="color:rgb(26, 28, 30);">开头的变量(例如</font><font style="color:rgb(26, 28, 30);"> </font>cdc<fontstyle="color:rgb(26,28,30);"></font><fontstyle="color:rgb(26,28,30);">开头的变量(例如</font><fontstyle="color:rgb(26,28,30);"></font>cdc_asdjflasutopfhvcZLmcfl_)。
    • 风险 : 高级反爬虫(如 Akamai, Cloudflare)会扫描 window 对象下的属性。如果发现了 cdc<fontstyle="color:rgb(26,28,30);"></font><fontstyle="color:rgb(26,28,30);">或</font><fontstyle="color:rgb(26,28,30);"></font>cdc_<font style="color:rgb(26, 28, 30);"> </font><font style="color:rgb(26, 28, 30);">或</font><font style="color:rgb(26, 28, 30);"> </font>cdc<fontstyle="color:rgb(26,28,30);"></font><fontstyle="color:rgb(26,28,30);">或</font><fontstyle="color:rgb(26,28,30);"></font>wdc_ 开头的变量,直接判定为 Selenium。
    • 补救困难: 除非你用 16 进制编辑器修改 chromedriver.exe 二进制文件中的字符串,否则很难去除。
  • DrissionPage:
    完全没有这个问题。因为它不使用 ChromeDriver,是通过 WebSocket 发送纯 CDP 指令,不需要在 JS 上下文里塞这种"联络暗号"。

3. 命令执行差异:isTrusted 事件属性

这涉及到模拟点击和按键的真实性。

  • Selenium:
    Selenium 的 click() 或 send_keys() 生成的事件,虽然现在的 Driver 已经做得很好,但在某些复杂的 JS 验证下,生成的 Event 对象中的 isTrusted 属性可能表现异常,或者缺少某些物理硬件关联的属性(如 pointerId, pressure 等)。
  • DrissionPage:
    它提供了两种模式:
    • 浏览器模拟: 通过 CDP 的 Input.dispatchMouseEvent,这和 Selenium 类似,有一定风险。
    • 更底层的控制: 它更容易结合 Python 的系统级输入库,或者通过 CDP 构建更完美的事件链,甚至通过"接管模式"让用户手动操作过验证码,程序接管后续流程。

4. 浏览器的"纯净度"与启动参数

  • Selenium:
    默认情况下,Selenium 每次启动都是一个新的、干净的浏览器实例(临时 Profile)。
    • 风险: 没有历史记录、没有缓存、没有 Cookie、Local Storage 为空。这种"白板"浏览器在风控系统眼里非常可疑(像是刚出生的婴儿却在做复杂操作)。
    • 此外,Selenium 启动时默认带有大量特征参数,如 --enable-automation, --test-type。
  • DrissionPage:
    • 非常擅长接管模式。你可以先手动打开一个浏览器(积累了正常的用户行为、缓存、Cookie),然后 DrissionPage 通过端口连上去。
    • 优势 : 在这种情况下,你的浏览器指纹是完美的真实用户指纹,Selenium 很难做到如此丝滑的接管。

5. 通信时序(Timing Attack)

我们在上一节讨论过架构差异,这直接导致了时序特征。

  • Selenium:
    Python -> HTTP -> ChromeDriver -> CDP -> Chrome。
    • 风险 : 这种长链路导致命令执行有固定的延迟模式(RTT)。某些高阶反爬(如瑞数)会统计你的 JS 执行时间、鼠标移动轨迹的采样率。如果发现操作之间有极其稳定的"机器延迟",就会触发风控。(不确定真假,ai 给的 -
  • DrissionPage:
    Python -> WebSocket -> Chrome。
    • 优势: 通信延迟极低,并发能力强。你可以编写更复杂的鼠标轨迹算法,以极高的频率发送 CDP 包来模拟人类的抖动和变速,这在 Selenium 中因为 HTTP 瓶颈很难做到。

6. User-Agent 和 Headless 特征

虽然两者都可以改 UA,但 Selenium 在 Headless(无头)模式下的特征暴露得更彻底。

  • Selenium:
    早期版本在 Headless 模式下,User-Agent 会包含 HeadlessChrome 字样。虽然现在可以改,但配合 navigator.plugins(无插件)、navigator.languages(语言单一)等特征,极易被识别。
  • DrissionPage:
    它封装了很多便捷的配置(如 Stealth 模式),自动帮你处理了 Headless 模式下的很多特征(自动补全插件列表、语言、WebGL 指纹噪声等),而 Selenium 原生是不管这些的,需要你自己写大量的 execute_script 去补。

总结表格

检测点 Selenium (WebDriver) DrissionPage (CDP) 风险等级 (Selenium)
Navigator.webdriver 强制为 true (极难修改) 可设为 undefined 🔴 极高
JS 注入变量 ($cdc_) 存在 (需修改二进制去除) 不存在 🔴 极高
启动参数 默认含 --enable-automation 可完全自定义/无自动化Flag 🟠 高
时序特征 HTTP 延迟大,操作僵硬 WebSocket 低延迟,更拟人 🟠 高
接管现有浏览器 困难 (需特定配置) 原生支持,极简 🟡 中
浏览器指纹 默认"白板"环境 易继承真实环境 🟡 中

结论:

Selenium 的风险在于**"它太标准了"。它是为了测试而生的,所以它诚实地告诉浏览器"我是机器人"。而 DrissionPage 是为了自动化(和爬虫)而生的,它的设计初衷就是 "控制"而非"测试"**,所以它避开了那些自我暴露的协议规范。

更多文章,敬请关注gzh:零基础爬虫第一天

相关推荐
sugar椰子皮3 小时前
【DrissionPage源码-6】dp如何监听network和console
爬虫
sugar椰子皮4 小时前
【DrissionPage源码-0】了解CDP
爬虫
010不二5 小时前
基于Appium爬虫文本导出可话个人动态(环境准备篇)
爬虫·python·appium
硅星企鹅5 小时前
如何使用低代码爬虫工具采集复杂网页数据?
爬虫·python·低代码
010不二5 小时前
基于Appium爬虫文本导出可话个人动态
数据库·爬虫·python·appium
山峰哥6 小时前
Python爬虫实战:从零构建高效数据采集系统
开发语言·数据库·爬虫·python·性能优化·架构
小白学大数据16 小时前
Java 爬虫对百科词条分类信息的抓取与处理
java·开发语言·爬虫
sugar椰子皮18 小时前
【node源码-6】async-hook c层修改以及测试
爬虫
Data_agent1 天前
OOPBUY模式淘宝1688代购系统搭建指南
开发语言·爬虫·python