Comparisons

用于验证码解决的 Headless 与 Headed Chrome

先说结论:只看识别成功率,headless 和有头 Chrome 对 CaptchaAI 没区别;但脚本依赖 reCAPTCHA v3 分数时,选错模式会直接拉低分数。Headless 启动快、占用低,天生适合跑服务器,代价是验证码系统专门盯着它的检测信号。下面用实测数据说清楚怎么选。

三句话看懂:

  • 识别成功率跟浏览器模式无关。
  • 差异在 v3 分数和出现频率。
  • 两头兼顾就上 Xvfb + 有头。

Headless 与有头 Chrome 怎么选:核心指标一览

对比项 headless 模式 有头模式
速度 快(无渲染开销) 较慢(要渲染 UI)
内存占用 ~100–200 MB ~300–500 MB
服务器部署 原生支持 需要虚拟显示
验证码检测
reCAPTCHA v3 分数 0.1–0.3 0.7–0.9
调试难度 较高 较低
横向扩展 优秀 较差

一句话总结:追并发选 headless,追分数选有头,两头都要就上 Xvfb。


该怎么选:三种典型场景

什么时候用 headless

  • 部署在服务器 / CI,没有显示器
  • 要扩展到 50+ 并发会话
  • 内存预算有限
  • 验证码全部交给 CaptchaAI 处理(浏览器模式不影响识别结果)

什么时候用有头模式

  • 需要肉眼调试验证码流程
  • reCAPTCHA v3 分数很关键(有头模式的分数明显更高)
  • 目标站点专门针对 headless 做了拦截
  • 本来就跑在带显示器的机器上

什么时候用有头 + 虚拟显示

  • 服务器上也需要接近有头模式的检测抗性
  • headless 下 reCAPTCHA v3 分数一直偏低
  • 预算能承受额外的内存开销(每个会话大约翻倍)

实际案例

某跨境电商团队在阿里云 ECS 上跑 headless 集群做 QA,v3 分数偏低的任务切到 Xvfb + 有头。


实测性能对比:headless / 有头 / 有头 + Xvfb

场景 headless 有头 有头 + Xvfb
启动耗时 ~1 秒 ~2 秒 ~2.5 秒
单标签页内存 100–200 MB 300–500 MB 300–500 MB
页面加载(平均) 1.5 秒 2.5 秒 2.5 秒
reCAPTCHA v3 分数 0.1–0.3 0.7–0.9 0.5–0.8
最大并发数(8GB 内存) 30–40 10–15 10–15
验证码识别成功率 相同(API 决定) 相同(API 决定) 相同(API 决定)

关键结论: 浏览器模式不影响 CaptchaAI 的识别结果,只影响验证码出现的频率和 reCAPTCHA v3 的分数区间。


常见故障排查

问题 原因 处理方式
headless 被识别 navigator.webdriver 标志暴露 加标准配置补丁或做 CDP 层面的修补
headless 下 v3 分数低 缺少渲染相关信号 切换到有头 + Xvfb
Xvfb 崩溃 显示编号冲突 --auto-servernum 参数
有头模式内存高 GPU 进程占用 --disable-gpu 参数
headless 截图空白 viewport 设置错误 设置 --window-size=1920,1080

验证码怎么识别出 headless 浏览器

常见检测信号

// Tests that CAPTCHA systems run

// 1. WebDriver flag (biggest tell)
navigator.userAgent  // true in headless, undefined in real browser

// 2. Chrome object completeness
window.chrome        // Missing or incomplete in headless

// 3. Plugins
navigator.plugins.length  // 0 in headless, 3+ in headed

// 4. WebGL renderer
// Headless: "SwiftShader Renderer"
// Headed:   "ANGLE (NVIDIA GeForce GTX 1080...)"

// 5. User-Agent contains "Headless"
navigator.userAgent.includes("Headless")  // Old Chrome versions

// 6. Screen dimensions
screen.width === 0   // Some headless configs

// 7. Notification permission
Notification.permission  // "denied" in headless without override

实测检测结果对比

Signal               Headed    Headless   New Headless (v112+)
─────────────────────────────────────────────────────────────
webdriver            false     true       true
chrome object        full      partial    improved
plugins              5         0          0
WebGL renderer       ANGLE     Swift      Swift
screen.width         1920      800        800
UA "HeadlessChrome"  no        yes        no (removed)

Chrome 的“新 headless”模式能躲过检测吗?(v112+)

Chrome 112 加入 --headless=new,和有头模式共用渲染代码:

from selenium import webdriver

# Old headless (more detectable)
options = webdriver.ChromeOptions()
options.add_argument("--headless")  # Legacy mode

# New headless (less detectable)
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")  # Shares headed rendering

driver = webdriver.Chrome(options=options)

新模式确实堵上了一部分检测点,但 navigator.webdriver 依旧会暴露,插件列表也还是空的——只检查这两项的站点照样能把它识别出来。


headless 模式下用 CaptchaAI 识别验证码(Python 示例)

CaptchaAI 服务端识别验证码,浏览器模式不影响结果。完整流程:起 headless 会话、抓 sitekey、调 in.php / res.php 拿 token、写回页面提交表单:

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"


def create_headless_driver():
    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--no-sandbox")
    options.add_argument("--window-size=1920,1080")
    options.add_argument(
        "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
    )
    return webdriver.Chrome(options=options)


def solve_captcha(site_url, sitekey):
    resp = requests.post(f"{API_URL}/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": site_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":
            return data["request"]

    raise TimeoutError("Solve timeout")


# Full headless workflow
driver = create_headless_driver()

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

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

    if sitekey:
        token = solve_captcha(driver.current_url, sitekey)
        driver.execute_script(f"""
            document.querySelector('#g-recaptcha-response').value = '{token}';
        """)
        driver.find_element(By.CSS_SELECTOR, "form").submit()

finally:
    driver.quit()

无头服务器怎么跑有头 Chrome:Xvfb 虚拟显示方案

用 Xvfb 建一块虚拟屏幕,就能在没显示器的服务器上跑“真正”的有头 Chrome。

Linux + Xvfb

# Install
sudo apt-get install xvfb

# Run with virtual display
xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" python script.py

Python + PyVirtualDisplay

from pyvirtualdisplay import Display
from selenium import webdriver

display = Display(visible=0, size=(1920, 1080))
display.start()

options = webdriver.ChromeOptions()
# No --headless flag = headed mode in virtual display
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")

driver = webdriver.Chrome(options=options)
# Now runs headed Chrome with real rendering, but no physical display

Docker + Xvfb

FROM python:3.12-slim

RUN apt-get update && apt-get install -y \
    xvfb \
    chromium \
    chromium-driver \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . /app
WORKDIR /app

CMD ["xvfb-run", "--auto-servernum", "python", "main.py"]

Node.js 标准配置补丁:减少验证码出现频率

标准配置补丁能降低验证码出现频率:

const puppeteer = require("puppeteer-extra");
const StealthPlugin = require("puppeteer-extra-plugin-stealth");

puppeteer.use(StealthPlugin());

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

  const page = await browser.newPage();
  await page.setViewport({ width: 1920, height: 1080 });

  // Additional QA patches
  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, "webdriver", {
      get: () => undefined,
    });

    Object.defineProperty(navigator, "plugins", {
      get: () => [1, 2, 3, 4, 5],
    });
  });

  await page.goto("https://example.com/form");

  // Detect and solve CAPTCHA with CaptchaAI
  const sitekey = await page.evaluate(() =>
    document.querySelector("[data-sitekey]")?.getAttribute("data-sitekey")
  );

  if (sitekey) {
    // Call CaptchaAI API (see other guides for full implementation)
    const token = await solveCaptchaAI(page.url(), sitekey);

    await page.evaluate((t) => {
      document.querySelector("#g-recaptcha-response").value = t;
    }, token);
  }

  await browser.close();
}

验证码仍交给 CaptchaAI 处理。


常见问题

验证码检测的到底是“headless”本身,还是行为特征?

两者都看:navigator.webdriver、插件列表是浏览器信号,鼠标轨迹、停留时间是行为信号。

CaptchaAI 在 headless 模式下识别效果会打折扣吗?

不会,浏览器模式不影响识别结果,只影响验证码出现频率和 v3 分数。

只用 GeeTest v3 或 Turnstile,还需要在意 headless 检测吗?

需要但没那么敏感——这两种更看重滑动、点击信号,识别成功率不受影响。

标准配置补丁和 CaptchaAI,该选哪个?

不是二选一,补丁减少验证码出现次数,出现的仍交给 CaptchaAI 处理。


相关指南


给你的验证码工作流选对浏览器模式——获取 CaptchaAI API Key,headless 或有头模式都能稳定识别验证码。

该文章已禁用评论。