Troubleshooting

验证码解决率下降:性能回归诊断

验证码解决率突然下降,原因通常在四个方向里:错误码、sitekey 或验证码类型、代理健康度,或 token 时效。本文给出一套可执行的诊断流程,帮你在联系支持前先锁定问题环节。

常见场景:解决率一夜之间从 95% 跌到 60%,第一反应是怀疑 CaptchaAI,但很多案例根源其实在自己的代码、代理或目标站点变化。按下面顺序走一遍,通常几分钟到十几分钟就能定位。

自查决策树:先定位问题方向

Solve rate dropped
├── Is the API returning errors? → Check error codes
│   ├── ERROR_WRONG_USER_KEY → API key issue
│   ├── ERROR_ZERO_BALANCE → Balance depleted
│   ├── ERROR_NO_SLOT_AVAILABLE → Rate limiting
│   └── ERROR_CAPTCHA_UNSOLVABLE → CAPTCHA changed
├── Are tokens returned but rejected by the target site?
│   ├── Token expired before submission → Speed up injection
│   ├── Sitekey changed → Re-extract from page
│   └── Domain mismatch → Check pageurl parameter
├── Are proxies failing?
│   ├── Proxy banned by target → Rotate proxies
│   └── Proxy timeout → Check proxy health
└── Did the target site change?
    ├── New CAPTCHA type → Update method parameter
    ├── JavaScript changes → Re-analyze page
    └── Rate limiting by site → Reduce frequency

先按这棵树判断大方向:API 报错、token 被拒绝、代理问题,还是站点变化。

速查表:先看看你的情况像哪一种

赶时间的话,先对照下表找到最像的场景,再跳到对应步骤。

场景 最可能的原因 建议的第一步
100% 失败,且全部是 ERROR_WRONG_USER_KEY API Key 无效 重新核对 API Key
数天内逐渐下降 代理质量退化 检查代理表现
突然跌到 0% sitekey 或页面结构变了 重新提取验证码参数
已解决,但 token 被网站拒绝 token 过期或域名不匹配 检查提交时序和 pageurl
测试站点正常,目标站点失败 该站点有特殊限制 对比两个站点的参数差异

常见问题先看这里

赶时间的话,这三个问题往往能直接省掉整套诊断流程。

先查错误码还是先测试目标站点?

建议先查错误码。返回 ERROR_WRONG_USER_KEYERROR_ZERO_BALANCE 这类明确报错时,问题通常在你这边或账户状态,几分钟就能定位;错误码正常但解决率仍低,再去核对目标站点的 sitekey 和验证码类型。

代理是不是解决率下降的头号嫌疑?

代理确实是基于 token 的验证码(reCAPTCHA、Turnstile 等)常见的下降原因之一,但不是唯一原因。若验证码类型支持无代理解决,先测一轮不带代理的请求;解决率恢复正常,再排查代理是否被封禁或地理位置不匹配。

ERROR_CAPTCHA_UNSOLVABLE 出现几次才需要上报?

复杂验证码本身有 2%–5% 的正常无法解决率,偶尔出现不必上报。只有比例在之前正常的 sitekey 上持续超过 15%–20% 时,才建议带上错误分布和站点信息联系支持。

以下是完整诊断步骤,供上面三个问题没覆盖到的情况使用。

第一步:核对账户状态与错误码

运行下面这段脚本,几分钟内拿到诊断结果:

# diagnose_solve_rate.py
import os
import requests
from collections import Counter

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

def check_balance():
    """Verify API key and balance."""
    resp = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "getbalance", "json": "1",
    })
    result = resp.json()
    print(f"Balance: {result}")
    return result

def test_solve(sitekey, pageurl, runs=5):
    """Run test solves and collect error statistics."""
    errors = Counter()
    successes = 0

    for i in range(runs):
        # Submit
        resp = requests.get("https://ocr.captchaai.com/in.php", params={
            "key": API_KEY,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": "1",
        })
        result = resp.json()

        if result.get("status") != 1:
            errors[result.get("request", "UNKNOWN")] += 1
            print(f"  Run {i+1}: Submit error: {result.get('request')}")
            continue

        task_id = result["request"]
        import time
        time.sleep(15)

        # Poll
        for _ in range(25):
            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:
                successes += 1
                print(f"  Run {i+1}: Solved")
                break
            if poll_result.get("request") != "CAPCHA_NOT_READY":
                errors[poll_result.get("request", "UNKNOWN")] += 1
                print(f"  Run {i+1}: Error: {poll_result.get('request')}")
                break
            time.sleep(5)
        else:
            errors["TIMEOUT"] += 1
            print(f"  Run {i+1}: Timeout")

    print(f"\nResults: {successes}/{runs} solved")
    if errors:
        print(f"Errors: {dict(errors)}")

# Run diagnostics
print("=== Balance Check ===")
check_balance()

print("\n=== Test Solves ===")
test_solve("YOUR_SITEKEY", "https://your-staging.example.com", runs=5)

先看 getbalance 返回值和测试提交的错误分布,若错误码不止一种,按出现次数排序:ERROR_CAPTCHA_UNSOLVABLE(验证码太复杂或已变化,需上报并核对 sitekey)、ERROR_WRONG_CAPTCHA_ID(轮询了错误的任务 ID)、ERROR_ZERO_BALANCE(余额用尽,充值即可)、ERROR_NO_SLOT_AVAILABLE(触发限流,降低并发)、CAPCHA_NOT_READY 超时(调大轮询时间,同时确认 sitekey 有效)。错误码明确时,问题多半在账户或 API Key,不用继续排查站点或代理。

第二步:核对 sitekey 与验证码类型有没有变

解决率下降最常见的原因,就是目标站点改了 sitekey 或页面结构。

打开目标页面,开 DevTools(F12),找这些位置:

  • reCAPTCHA:data-sitekey 属性,或 grecaptcha.render 调用参数
  • Cloudflare Turnstile:Turnstile 组件里的 data-sitekey
  • GeeTest(极验):初始化参数中的 gt 字段

拿这个值和代码里保存的 sitekey 逐字符比对——哪怕只改动一个字符,也可能导致 100% 失败。

提示:若测试环境在中国大陆网络内,访问 reCAPTCHA 官方脚本可能有额外延迟,容易被误判成“解决率下降”,建议先在网络稳定的环境下复测一次,排除这个变量。

同时确认验证码类型本身没变——不少站点会在供应商之间迁移,比如 reCAPTCHA v2 → v3(隐形验证)、reCAPTCHA → Cloudflare Turnstile、图片验证码 → reCAPTCHA Enterprise。类型变了,记得同步更新代码里的 method 参数。

第三步:排查代理健康度

代理质量直接影响解决率,对基于 token 的验证码类型尤其明显。常见问题和处理方式:

  • 代理被目标网站封禁(token 已解决但被拒绝)→ 更换为新的自有服务器基础设施
  • 代理返回错误 ERROR_PROXY_NOT_FOUND(代理已失效)→ 确认代理是否存活、可访问
  • 检测到数据中心代理(解决率偏低)→ 切换到自有服务器基础设施
  • 代理地理位置不匹配(结果时好时坏)→ 让代理所在地区与目标站点匹配

如果验证码类型支持无代理解决,先在不带代理的情况下测一轮,就能快速判断问题是不是出在代理上。

第四步:检查 token 时效

CAPTCHA token 的有效期都很短——reCAPTCHA v2/v3 约 120 秒,Cloudflare Turnstile 约 300 秒,GeeTest v3 约 60 秒。如果管道从拿到 token 到把它注入表单之间花的时间太久,token 会过期,目标站点自然会拒绝它。

测量 getTaskResult 返回到表单提交之间的耗时,超过 60 秒就该优化管道,把这段时间压缩下来。

第五步:和历史基线做对比

如果你之前跑过基准测试,把当前指标和基线比一比:

指标 基线 当前值 差值 是否需要关注
解决率 95% 下降超过 5% 就要排查
中位解决耗时 15 秒 增加超过 50% 就要排查
错误率 2% 超过 5% 就要排查
token 接受率 98% 下降超过 3% 说明站点变了

什么时候该联系 CaptchaAI 支持

出现下面这些情况,再联系 CaptchaAI 支持:

  • 诊断步骤都排查过,解决率依然低
  • 之前正常的 sitekey 上,ERROR_CAPTCHA_UNSOLVABLE 超过 20%
  • 余额正常,但求解持续失败
  • 问题持续超过 2 小时

报告里建议包含:

  1. 验证码类型和 sitekey
  2. 目标站点 URL
  3. 错误分布(诊断脚本输出)
  4. 问题开始时间
  5. 你对代码的改动

相关文章

下一步

让验证码识别管道保持稳定 —— 获取你的 CaptchaAI API Key

相关指南:

该文章已禁用评论。