如何在轮询结果时修复 CAPCHA_NOT_READY

简短回答: CAPCHA_NOT_READY 并不是错误。它是 API 在告诉你验证码仍在识别中,而你查询得太早了。你的请求没有任何需要修复的地方。你只需要正确的轮询节奏,以及一个知道何时停止的循环。本指南将涵盖这两点,还有那个会把正常脚本变得令人困惑的一次性读取行为。
CAPCHA_NOT_READY 实际含义
当你向 /in.php 提交验证码时,你得到的是一个 ID,而不是答案。识别在后台进行。然后你用该 ID 轮询 /res.php ,直到答案就绪。
在此之前,每次轮询都返回相同的字符串:
# Poll for the result. Note action=get and the id from /in.php. curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=CAPTCHA_ID" # Still working: CAPCHA_NOT_READY # Done: OK|03AGdBq26Sxo...
是的,它拼写为 CAPCHA,而不是 验证码。这个拼写错误自 2captcha API 诞生之初就存在。CapSkip 与该 API 即插即用兼容,因此故意保留了这个拼写错误。如果纠正它,每一个检查该确切字符串的现有客户端库都会失效。请在你的代码中精确匹配它。
按正确的节奏轮询
大多数人频繁遇到这个问题,是因为他们在提交后立即轮询。不同验证码类型所需时间差别很大,因此不应对所有类型在同一时刻进行首次检查。
| 类型 | 首次检查前等待 | 然后每隔 |
|---|---|---|
| 图片 / 文字 | 1 秒 | 5 秒 |
| reCAPTCHA v2 | 15 到 20 秒 | 5 秒 |
| reCAPTCHA v3 | 10 到 15 秒 | 5 秒 |
| 极验 | 约 5 秒 | 5 秒 |
| Cloudflare Turnstile | 约 5 秒 | 5 秒 |
比每 5 秒更快地轮询并不会让识别更快,只会浪费请求。
一个会终止的轮询循环
Shell 版本,清晰地展示其结构:
# Submit, capture the id, give it a head start, then poll. ID=$(curl -s "http://127.0.0.1:8080/in.php?key=YOUR_API_KEY&method=userrecaptcha&googlekey=YOUR_SITEKEY&pageurl=https://example.com" | cut -d'|' -f2) sleep 15 while :; do RES=$(curl -s "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=$ID") [ "$RES" = "CAPCHA_NOT_READY" ] || break sleep 5 done echo "$RES"
那个循环有一个值得指出的缺陷:如果上游出问题,它会永远运行。在真实代码中,请给它设上限。
# pip install requests
import time
import requests
BASE = "http://127.0.0.1:8080"
def solve_recaptcha(sitekey, page_url, api_key="YOUR_API_KEY", timeout=180):
task = requests.get(BASE + "/in.php", params={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": 1,
}).json()
task_id = task["request"]
time.sleep(15) # reCAPTCHA needs a head start
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
res = requests.get(BASE + "/res.php", params={
"key": api_key,
"action": "get",
"id": task_id,
}).text.strip()
if res.startswith("OK|"):
return res.split("|", 1)[1]
# Anything that is not the pending string is terminal.
if res != "CAPCHA_NOT_READY":
raise RuntimeError(res or "empty response: already read, or bad id")
time.sleep(5)
raise TimeoutError("gave up after %ss" % timeout)三点让它更安全:一个截止时间使其不会挂起;把任何非 pending 字符串都视为终止状态,使真正的错误立即显现;以及只读取一次答案。
或者完全跳过循环
如果你使用的是官方 SDK 之一,上述问题都与你无关。轮询在调用内部完成,你会直接拿回 token,因此 CAPCHA_NOT_READY 永远不会到达你的代码。
# pip install capskip
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
# Submit and poll happen inside this one call.
result = solver.recaptcha(
sitekey="YOUR_SITEKEY",
url="https://example.com/page-with-recaptcha",
)
print(result["code"]) # token, ready to inject// npm install capskip
const { CapSkip } = require('capskip');
const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const result = await solver.recaptcha('YOUR_SITEKEY', 'https://example.com/page-with-recaptcha');
console.log(result.code);// composer require capskip/capskip
use CapSkip\CapSkip;
$solver = new CapSkip(['host' => '127.0.0.1', 'port' => 8080]);
$result = $solver->recaptcha('YOUR_SITEKEY', 'https://example.com/page-with-recaptcha');
echo $result['code'];// dotnet add package CapSkip
using CapSkip;
var solver = new CapSkipClient(host: "127.0.0.1", port: 8080);
var result = await solver.RecaptchaAsync("YOUR_SITEKEY", "https://example.com/page-with-recaptcha");
Console.WriteLine(result.Code);四种语言的完整方法签名都在 CAPTCHA 识别 SDK 页面上。对于 Go、Java、Ruby 或任何没有官方包的语言,上面的原始循环仍是你需要的。
只读一次的陷阱
这是那种会浪费你一个下午的问题。 每个结果只能读取一次。 成功读取后再次轮询,你得到的是空响应,而不是你已经拿到的 token。
因此,空响应体并不意味着“仍在处理”。它意味着两件事之一:
- 你已经取回了这个结果并把它丢弃了
- 该 ID 不存在,通常是解析时弄错或截断的 ID
OK|ID
一拿到 token 就立即存储它。不要为了“确认”而重新轮询。
当它确实成为问题时
如果指定了 CAPCHA_NOT_READY 始终无法解决时,pending 字符串是症状而非原因。请按顺序检查以下各项:
| 响应 | 含义 | 修复 |
|---|---|---|
ERROR_CAPTCHA_UNSOLVABLE | 已尝试识别但失败 | 核实 sitekey 和 pageurl 是否为实时有效的,然后重新提交 |
ERROR_WRONG_ID_FORMAT | 该 ID 不是有效整数 | 你在解析 OK|ID 时出错了。按竖线分割,取第 2 个字段 |
ERROR_GOOGLEKEY | sitekey 在提交时被拒绝 | 从实时页面重新读取它,而不是从缓存源 |
| 空响应体 | 已读取,或未知 ID | 在首次读取时存储结果 |
| pending 超过 3 分钟 | 识别引擎未运行或不可达 | 确认服务在所配置的端口上处于运行状态。参见 设置指南 |
完整的错误字符串列表见 API 文档.
常见问题
CAPCHA_NOT_READY 是我应该记录的错误吗?
不是作为错误。它是正常的进行中状态,每次识别你都会看到它数次。如果要记录,请以 debug 级别记录,否则你会把真正的失败淹没在噪声里。
更快地轮询会更早返回答案吗?
不会。识别时间与你询问的频率无关。每 5 秒是文档规定的间隔,更快只是浪费请求。
为什么拼写为 CAPCHA 而不是 CAPTCHA?
这是原始 2captcha API 中的一个旧拼写错误,已成为约定的一部分。CapSkip 与该 API 即插即用兼容,因此原样保留该字符串。修正拼写会破坏每一个检查它的客户端。
我能以 JSON 形式获取 pending 状态吗?
可以。在请求中添加 json=1 ,响应就会以对象形式返回,包含 status 和 request 字段,而非纯文本。pending 字符串本身不变。
小结
在首次检查前给识别一点提前量,每 5 秒重试一次,把除 CAPCHA_NOT_READY 以外的任何内容都视为终止,用截止时间给循环设上限,并且只读取一次结果。这就是全部套路。
如果你根本不想编写这个循环,CapSkip 是一款 验证码识别工具 在本地运行,并提供 Python、Node.js、PHP 和 .NET 的 SDK,为你处理轮询。
