横向对比

用于验证码自动化的 WebDriver 与 Chrome DevTools 协议

有两种协议控制 Chrome 的自动化:WebDriver(W3C 标准,由 Selenium 使用)和 Chrome DevTools 协议(CDP,由 Puppeteer/Playwright 使用)。每个都有不同的检测配置文件、功能和验证码处理特性。


协议架构

WebDriver (Selenium):
  Script ──HTTP──▶ ChromeDriver ──DevTools──▶ Chrome
  (JSON Wire)        (bridge)      (internal)

CDP (Puppeteer/Playwright):
  Script ──WebSocket──▶ Chrome
  (direct)               (no middle layer)

头对头比较

特征 网络驱动程序 CDP
协议 HTTP+JSON WebSocket
标准 W3C标准 Chrome 专用
检测表面 高 (navigator.userAgent = true) 较低(可修补)
网络拦截 否(需要Selenium Wire) 内置(FetchNetwork
JavaScript 评估 executeScript(包装) Runtime.evaluate(直接)
请求修改 是的
响应修改 是的
连接开销 每个命令的 HTTP 持久WebSocket
跨浏览器 Chrome、火狐、Safari、Edge 仅限 Chrome/Chromium
隐身潜力 低(始终设置 webdriver 标志) 高(可修补标志)

检测差异

WebDriver 检测(Selenium)

// What sites check for Selenium
navigator.userAgent                    // true (set by ChromeDriver)
document.$cdc_asdjflasutopfhvcZLmcfl  // ChromeDriver internal variable
window.callSelenium                    // Older Selenium versions
window._selenium                      // Selenium internals
document.__webdriver_evaluate          // WebDriver namespace
# Even with flag removal, ChromeDriver leaves traces
from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--no-sandbox")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)

driver = webdriver.Chrome(options=options)

# Still detectable via $cdc variables in document

CDP 检测(Puppeteer)

// Puppeteer sets webdriver flag too, but it's fully patchable
await page.evaluateOnNewDocument(() => {
  Object.defineProperty(navigator, 'webdriver', {
    get: () => undefined,
  });
  // No $cdc variables to worry about
  // No Selenium-specific traces
});

验证码解决:WebDriver 方法

# Selenium (WebDriver) + CaptchaAI
import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

API_KEY = "YOUR_API_KEY"
API_URL = "https://ocr.captchaai.com"

options = webdriver.ChromeOptions()
options.add_argument("--no-sandbox")
driver = webdriver.Chrome(options=options)

driver.get("https://example.com/form")
time.sleep(2)

# Extract sitekey
sitekey = driver.execute_script(
    "return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
)

# Solve via CaptchaAI
resp = requests.post(f"{API_URL}/in.php", data={
    "key": API_KEY, "method": "userrecaptcha",
    "googlekey": sitekey, "pageurl": driver.current_url, "json": 1,
})
task_id = resp.json()["request"]

for _ in range(60):
    time.sleep(5)
    resp = requests.get(f"{API_URL}/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": 1,
    })
    data = resp.json()
    if data["request"] != "CAPCHA_NOT_READY":
        break

token = data["request"]

# Inject token
driver.execute_script(f"""
    document.querySelector('#g-recaptcha-response').value = '{token}';
    document.querySelectorAll('[name="g-recaptcha-response"]')
        .forEach(el => {{ el.value = '{token}'; }});
""")

driver.find_element(By.CSS_SELECTOR, "form").submit()
driver.quit()

验证码解决:CDP 方法

// Puppeteer (CDP) + CaptchaAI
const puppeteer = require("puppeteer-extra");
const StealthPlugin = require("puppeteer (默认配置)");
const https = require("https");

puppeteer.use(StealthPlugin());

const API_KEY = "YOUR_API_KEY";
const API_URL = "https://ocr.captchaai.com";

function solveCaptcha(siteUrl, sitekey) {
  // Submit + poll (simplified)
  return new Promise(async (resolve, reject) => {
    const submitResp = await httpPost(`${API_URL}/in.php`, {
      key: API_KEY, method: "userrecaptcha",
      googlekey: sitekey, pageurl: siteUrl, json: "1",
    });

    const taskId = submitResp.request;

    for (let i = 0; i < 60; i++) {
      await new Promise((r) => setTimeout(r, 5000));
      const result = await httpGet(
        `${API_URL}/res.php?key=${API_KEY}&action=get&id=${taskId}&json=1`
      );
      if (result.request !== "CAPCHA_NOT_READY") {
        return resolve(result.request);
      }
    }
    reject(new Error("Timeout"));
  });
}

(async () => {
  const browser = await puppeteer.launch({
    headless: "new",
    args: ["--no-sandbox", "--window-size=1920,1080"],
  });

  const page = await browser.newPage();

  // CDP: Enable network interception
  const cdp = await page.createCDPSession();
  await cdp.send("Network.enable");

  // Monitor CAPTCHA-related requests
  cdp.on("Network.requestWillBeSent", (params) => {
    if (params.request.url.includes("recaptcha")) {
      console.log(`CAPTCHA request: ${params.request.url}`);
    }
  });

  await page.goto("https://example.com/form", { waitUntil: "networkidle0" });

  const sitekey = await page.evaluate(() =>
    document.querySelector("[data-sitekey]")?.getAttribute("data-sitekey")
  );

  if (sitekey) {
    const token = await solveCaptcha(page.url(), sitekey);

    // CDP: Inject via Runtime.evaluate (lower-level than page.evaluate)
    await cdp.send("Runtime.evaluate", {
      expression: `
        document.querySelector('#g-recaptcha-response').value = '${token}';
        document.querySelector('form').submit();
      `,
    });
  }

  await browser.close();
})();

验证码工作流程的功能比较

网络拦截

# WebDriver: Cannot intercept requests natively
# Requires selenium-wire (third-party)
from seleniumwire import webdriver

driver = webdriver.Chrome()
# selenium-wire proxies all traffic through a local proxy
// CDP: Built-in request interception
await cdp.send("Fetch.enable", {
  patterns: [{ urlPattern: "*recaptcha*" }],
});

cdp.on("Fetch.requestPaused", async ({ requestId, request }) => {
  console.log(`Intercepted: ${request.url}`);
  await cdp.send("Fetch.continueRequest", { requestId });
});

响应修改

// CDP: Can modify responses (WebDriver cannot)
cdp.on("Fetch.requestPaused", async ({ requestId, request }) => {
  if (request.url.includes("captcha-config")) {
    // Return modified response
    await cdp.send("Fetch.fulfillRequest", {
      requestId,
      responseCode: 200,
      body: btoa(JSON.stringify({ enabled: false })),
    });
  } else {
    await cdp.send("Fetch.continueRequest", { requestId });
  }
});
# WebDriver: Standard cookie API
driver.add_cookie({"name": "session", "value": "abc123"})
cookies = driver.get_cookies()
// CDP: Full cookie control including httpOnly
await cdp.send("Network.setCookie", {
  name: "session",
  value: "abc123",
  domain: "example.com",
  httpOnly: true,
  secure: true,
});

// Get all cookies including httpOnly (WebDriver can't access these)
const { cookies } = await cdp.send("Network.getAllCookies");

决策矩阵

设想 受到推崇的 为什么
多浏览器测试 网络驱动程序 跨浏览器支持
最大隐身性 CDP 下检测面
网络调试 CDP 内置拦截
团队不熟悉 CDP 网络驱动程序 更简单的 API,更多文档
高规模刮削 CDP 降低每个会话的开销
CI/CD 集成 网络驱动程序 更好的工具支持
验证码较多的网站 CDP + CaptchaAI 潜行+解决组合
简单的表格填写 WebDriver + CaptchaAI 更容易实施

混合方法:WebDriver + CDP

Selenium 4 通过 BiDi(双向)支持 CDP 命令:

from selenium import webdriver

driver = webdriver.Chrome()

# Use WebDriver for navigation
driver.get("https://example.com")

# Use CDP for QA patches
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
    """
})

# Use CDP for network events
driver.execute_cdp_cmd("Network.enable", {})

性能基准

公制 网络驱动程序(硒) CDP(Puppeteer) CDP(剧作家)
会话开始 ~2.5秒 ~1.5秒 ~1.2秒
页面导航 ~500ms 开销 ~100ms 开销 ~80ms 开销
脚本执行 每次调用约 200 毫秒 每次调用约 50 毫秒 每次调用约 40 毫秒
每个会话的内存 〜350MB 〜280MB 约260MB
验证码解决(CaptchaAI) 15-30秒 15-30秒 15-30秒

CaptchaAI 求解时间是相同的 — 这是独立于浏览器协议的 API 调用。


故障排除

问题 网络驱动修复 CDP修复
检测为机器人 excludeSwitches + 选项 隐形插件+补丁
无法拦截请求 使用Selenium Wire 使用Fetch.enable
执行缓慢 减少 execute_script 调用 批量CDP命令
token 提交失败 检查 iframe 上下文 在上下文中使用 Runtime.evaluate
会话不稳定 更新 Chrome 驱动程序 检查 WebSocket 连接

常问问题

哪种协议的验证码成功率更高?

成功率是相同的——CaptchaAI 解决服务器端问题。该协议仅影响检测频率(验证码出现的频率)。

我可以混合使用 WebDriver 和 CDP 吗?

是的。 Selenium 4 公开了 execute_cdp_cmd()。使用 WebDriver 进行导航,使用 CDP 进行标准补丁。

剧作家比两者都好吗?

Playwright 在内部使用 CDP,但提供了具有自动等待和更好的错误处理功能的更高级别的 API。这是一个很好的中间立场。

我应该从 Selenium 切换到 Puppeteer 来进行验证码工作吗?

如果检测是您的主要问题,那么是的。如果您需要跨浏览器支持,请继续使用 Selenium + CaptchaAI。


相关指南


为您的验证码工作流程选择正确的自动化协议 —获取您的 CaptchaAI 密钥并解决验证码,无论协议如何。

该文章已禁用评论。