Reference

使用 CaptchaAI 对跨类型验证码解决时间进行基准测试

验证码识别到底要等多久?答案因类型而异:图片验证码不到 1 秒出结果,reCAPTCHA v2 的复杂挑战却要等上大半分钟。这几十秒差距乘以几千次任务,就是排队与按时交付的区别。本文给出 CaptchaAI 各类型验证码的官方耗时上限,以及可在你自己账号复现的 Python / JavaScript 基准脚本。

各类型验证码识别耗时参考

以下数字基于官方 SLA 上限与内部观测样本,仅供参考,实际结果因环境、代理质量与任务量而异,请自行实测确认。

验证码类型 官方耗时上限 实测中位数 P90 P99 成功率
reCAPTCHA v2 <60 秒 12–18 秒 25 秒 40 秒 高(参考值)
reCAPTCHA v2 隐形版 <30 秒 10–15 秒 20 秒 28 秒 高(参考值)
reCAPTCHA v3 <4 秒 1–3 秒 3 秒 4 秒 高(参考值)
reCAPTCHA 企业版 <60 秒 15–25 秒 35 秒 50 秒 高(参考值)
Cloudflare Turnstile <10 秒 4–8 秒 9 秒 10 秒 高(参考值)
GeeTest(极验)v3 <12 秒 6–10 秒 11 秒 12 秒 高(参考值)
图片/OCR <0.5 秒 0.2–0.4 秒 0.5 秒 0.8 秒 高(参考值)
BLS CAPTCHA <1 秒 0.3–0.7 秒 0.9 秒 1 秒 高(参考值)

持续超出上限建议提交工单核实,而非偶发抖动。

如何解读这些数据

中位数、P90、P99 分别看什么

  • 中位数 —— 典型体验,用于容量规划。
  • P90 —— 十次里九次能达到的“最差情况”,用于日常超时。
  • P99 —— 真正的极端情况,超时上限要高于它,避免误杀正常任务。

轮询间隔怎么配置

根据上面的基准数据,为每种类型配置对应的轮询节奏:

验证码类型 首次等待 轮询间隔 最大尝试次数
图片/OCR 5 秒 3 秒 15
Cloudflare Turnstile 5 秒 3 秒 15
BLS CAPTCHA 5 秒 3 秒 15
reCAPTCHA v2 15 秒 5 秒 20
reCAPTCHA v3 10 秒 5 秒 20
GeeTest(极验)v3 12 秒 5 秒 20
reCAPTCHA 企业版 15 秒 5 秒 25

哪些因素在影响识别耗时

耗时不是固定值,下面几个变量决定实际数字:

  • 网络延迟 —— 物理距离体现为往返时间:200 毫秒 RTT,加一次提交和 3~4 次轮询,约拉长 1 秒总耗时。
  • 代理质量 —— 带代理请求时,自有服务器基础设施通常比数据中心代理触发更少二次验证,识别更快。
  • 验证码复杂度 —— reCAPTCHA v2 图片挑战难度不固定,带褪色或遮挡的“选出交通灯”任务比简单勾选框耗时更久。
  • 轮询策略 —— 频繁轮询(每 2 秒)更快拿结果但增加调用次数;保守轮询(每 10 秒)降低压力但拉长总耗时观测值。
  • 国内网络环境 —— reCAPTCHA 脚本走 Google 域名,中国大陆网络下连通性不稳定,实测耗时偏高,多是链路问题而非识别变慢。建议 Turnstile、GeeTest v3 在国内网络测,reCAPTCHA 系列放海外节点测,两组数据分开记录。

用脚本复现这些基准数据

参考值终归是参考值,真实数据要来自你自己的账号和目标页面。下面两段脚本跑 N 次识别,输出中位数、P90 等统计值。

Python 基准脚本

# benchmark_captchaai.py
import os
import time
import statistics
import requests

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

def benchmark_solve(method, params, runs=10):
    """Benchmark a CAPTCHA type over multiple runs."""
    times = []
    successes = 0

    for i in range(runs):
        start = time.time()

        # Submit
        submit_params = {"key": API_KEY, "json": "1", **params}
        resp = requests.get("https://ocr.captchaai.com/in.php",
                            params=submit_params)
        result = resp.json()

        if result.get("status") != 1:
            print(f"  Run {i+1}: Submit failed - {result.get('request')}")
            continue

        task_id = result["request"]

        # Poll
        time.sleep(10)
        solved = False
        for _ in range(30):
            poll = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get",
                "id": task_id, "json": "1",
            })
            poll_result = poll.json()

            if poll_result.get("status") == 1:
                elapsed = time.time() - start
                times.append(elapsed)
                successes += 1
                solved = True
                print(f"  Run {i+1}: {elapsed:.1f}s")
                break
            if poll_result.get("request") != "CAPCHA_NOT_READY":
                print(f"  Run {i+1}: Error - {poll_result.get('request')}")
                break

            time.sleep(5)

        if not solved and poll_result.get("request") == "CAPCHA_NOT_READY":
            print(f"  Run {i+1}: Timeout")

    if times:
        return {
            "runs": runs,
            "successes": successes,
            "success_rate": f"{successes/runs*100:.0f}%",
            "median": f"{statistics.median(times):.1f}s",
            "p90": f"{sorted(times)[int(len(times)*0.9)]:.1f}s" if len(times) >= 10 else "N/A",
            "min": f"{min(times):.1f}s",
            "max": f"{max(times):.1f}s",
        }
    return {"error": "No successful solves"}

# Benchmark reCAPTCHA v2
print("=== reCAPTCHA v2 ===")
recaptcha_v2 = benchmark_solve("userrecaptcha", {
    "method": "userrecaptcha",
    "googlekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    "pageurl": "https://www.google.com/recaptcha/api2/demo",
})
print(recaptcha_v2)

# Benchmark Image/OCR
print("\n=== Image CAPTCHA ===")
# Submit a base64-encoded test image
import base64
# Use a test image for benchmarking
image_result = benchmark_solve("post", {
    "method": "base64",
    "body": "BASE64_ENCODED_CAPTCHA_IMAGE",
})
print(image_result)

JavaScript(Node.js)基准脚本

Node.js 技术栈可以用下面这段 axios 脚本做同样的测试:

// benchmark_captchaai.js
const axios = require('axios');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';

async function benchmarkSolve(params, runs = 10) {
  const times = [];
  let successes = 0;

  for (let i = 0; i < runs; i++) {
    const start = Date.now();

    // Submit
    const submit = await axios.get('https://ocr.captchaai.com/in.php', {
      params: { key: API_KEY, json: '1', ...params },
    });

    if (submit.data.status !== 1) {
      console.log(`  Run ${i + 1}: Submit failed - ${submit.data.request}`);
      continue;
    }

    // Poll
    await new Promise(r => setTimeout(r, 10000));
    let solved = false;

    for (let j = 0; j < 30; j++) {
      const poll = await axios.get('https://ocr.captchaai.com/res.php', {
        params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
      });

      if (poll.data.status === 1) {
        const elapsed = (Date.now() - start) / 1000;
        times.push(elapsed);
        successes++;
        solved = true;
        console.log(`  Run ${i + 1}: ${elapsed.toFixed(1)}s`);
        break;
      }
      if (poll.data.request !== 'CAPCHA_NOT_READY') {
        console.log(`  Run ${i + 1}: Error - ${poll.data.request}`);
        break;
      }
      await new Promise(r => setTimeout(r, 5000));
    }

    if (!solved) console.log(`  Run ${i + 1}: Timeout`);
  }

  if (times.length === 0) return { error: 'No successful solves' };

  times.sort((a, b) => a - b);
  return {
    runs, successes,
    successRate: `${((successes / runs) * 100).toFixed(0)}%`,
    median: `${times[Math.floor(times.length / 2)].toFixed(1)}s`,
    min: `${times[0].toFixed(1)}s`,
    max: `${times[times.length - 1].toFixed(1)}s`,
  };
}

(async () => {
  console.log('=== reCAPTCHA v2 ===');
  const v2 = await benchmarkSolve({
    method: 'userrecaptcha',
    googlekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
    pageurl: 'https://www.google.com/recaptcha/api2/demo',
  });
  console.log(v2);

  console.log('\n=== Cloudflare Turnstile ===');
  const turnstile = await benchmarkSolve({
    method: 'turnstile',
    sitekey: 'YOUR_TURNSTILE_SITEKEY',
    pageurl: 'https://example.com',
  });
  console.log(turnstile);
})();

常见故障排查

现象 可能原因 处理方式
耗时明显高于参考值 网络延迟高,或代理线路有问题 换离服务器更近的地区测试
多次运行结果忽高忽低 验证码难度波动较大 样本量提高到 50 次以上再看趋势
频繁超时 sitekey 或 pageurl 参数不对 先手动跑一次请求,确认参数没问题
基准跑到一半报 ERROR_ZERO_BALANCE 余额撑不住整轮测试 测试前确认余额,按预计调用量充值

常见问题

GeeTest(极验)v3 和 Cloudflare Turnstile,识别速度谁更快?

按官方上限看,Turnstile <10 秒,GeeTest v3 <12 秒,差距不大,实际顺序以你账号跑出的中位数为准。

为什么高峰时段识别会变慢?

美国工作时间是全平台请求高峰,排队更慢,中位数和 P90 会略高,建议不同时段各跑一轮基准对比。

生产环境的超时应该怎么设置?

用 P90 做日常超时、P99 做最大超时,再留 20% 缓冲,只按中位数设超时会误判不少任务。

测试样本量至少要多少次才可信?

单次结果参考意义有限,至少跑 30~50 次,样本量越大越接近真实分布。

相关文章

下一步

把基准脚本跑在你自己的账号和目标页面上——获取你的 CaptchaAI API 密钥

相关指南:

该文章已禁用评论。