如何用 cURL 和原始 HTTP API 识别验证码

你并不需要 SDK。CapSkip API 与 2captcha 兼容,所以只用两次调用就能通过 curl 识别验证码:先把任务 POST 到 /in.php ,拿回一个 ID,然后轮询 /res.php ,直到答案出现。一切都运行在 127.0.0.1:8080上,因此既没有远程端点,也不会按次计费。本指南给出每种验证码类型的确切参数、JSON 响应结构,以及一段可以直接粘贴进终端的脚本。
你需要什么
- CapSkip 应用正在运行,且本地服务已启动。端口和密钥设置见 设置指南.
curl。macOS、所有 Linux 发行版以及 Windows 10 build 1803 及以上版本都自带它。jq,用于从 JSON 响应里提取字段。它是可选的,但有了它示例就能写成一行。
密钥校验默认关闭,所以任何非空字符串都可以用作 key。宁可随便发一个,也别什么都不发:密钥为空会返回 ERROR_WRONG_USER_KEY.
依赖清单到此为止,因为整个 API 只有两个端点:
| 端点 | 作用 | 返回 |
|---|---|---|
/in.php | 提交任务 | 一个数字形式的验证码 ID |
/res.php | 查询该 ID 是否已完成 | 答案,或 CAPCHA_NOT_READY |
两个端点都接受 GET 或 POST。POST 是更好的习惯,因为带查询参数的页面 URL 会让 GET 请求在第一个未编码的 & 处被悄悄截断。
第 1 步:提交任务
reCAPTCHA v2 是最短的例子。两个值就能确定这个任务:页面上的 sitekey,以及它所在页面的 URL。
# No install step. curl is already on your machine. curl -X POST http://127.0.0.1:8080/in.php \ -d "key=YOUR_API_KEY" \ -d "method=userrecaptcha" \ -d "googlekey=YOUR_SITEKEY" \ -d "pageurl=https://example.com/page-with-recaptcha" OK|2122988149 # the number after the pipe is your captcha ID
注意参数名。reCAPTCHA 用的是 googlekey。Turnstile 用的是 sitekey。发送 sitekey 设置为 userrecaptcha 是收到 ERROR_GOOGLEKEY 响应的最常见原因。
第 2 步:轮询结果
先等待,再询问。立刻轮询只会白白浪费一次请求,拿到的还是 CAPCHA_NOT_READY.
sleep 15 curl -X POST http://127.0.0.1:8080/res.php \ -d "key=YOUR_API_KEY" \ -d "action=get" \ -d "id=2122988149" OK|03AGdBq26... # the token, ready to inject into the form
关于 /res.php 有两点容易让人踩坑。任务还在跑的时候它会返回 CAPCHA_NOT_READY ,这不是错误,意思是继续轮询。还有, 每个结果只能读取一次,所以答案一到就立刻存下来。第二次读取同一个 ID 会返回空。
第一次轮询前要等多久,取决于验证码类型:
| 类型 | 首次轮询等待 |
|---|---|
| 图片验证码 | 1 秒 |
| reCAPTCHA v2 | 15 到 20 秒 |
| reCAPTCHA v3 | 10 到 15 秒 |
| 极验 v3 | 约 5 秒 |
加上 json=1 就能解析响应
纯文本格式适合人在终端里看,交给脚本就很别扭。加上 json=1 ,两个端点都会改为返回一个结构稳定的对象。
// in.php with json=1
{"status": 1, "request": "2122988149"}
// res.php with json=1, once it is solved
{"status": 1, "request": "03AGdBq26..."}
// res.php with json=1, still working
{"status": 0, "request": "CAPCHA_NOT_READY"}status 为 1 表示成功,其他情况一律为 0,而真正有用的值始终放在 request里。于是整件事就变成了两条 jq 表达式。
所有 method 一览表
九种验证码类型,五个 method 取值。各种变体只是额外的参数,而不是新的端点。
| 类型 | method | 必需参数 |
|---|---|---|
| 图片验证码,文件上传 | post | file |
| 图片验证码,base64 | base64 | body |
| reCAPTCHA v2 | userrecaptcha | googlekey, pageurl |
| reCAPTCHA v2 Invisible | userrecaptcha | 加上 invisible=1 |
| reCAPTCHA Enterprise | userrecaptcha | 加上 enterprise=1 |
| reCAPTCHA v3 | userrecaptcha | 加上 version=v3, action |
| Turnstile 小组件 | turnstile | sitekey, pageurl |
| Turnstile 挑战页面 | turnstile | 加上 data, pagedata |
| 极验 v3 | geetest | gt, challenge, pageurl |
图片既可以作为表单文件上传,也可以以 base64 放进请求体:
# File upload. Note the @ in front of the path. Use -F for every # field here: curl refuses to mix -F and -d in one request. curl -X POST http://127.0.0.1:8080/in.php \ -F "key=YOUR_API_KEY" -F "method=post" -F "[email protected]" # Or send the bytes inline, already base64 encoded. curl -X POST http://127.0.0.1:8080/in.php \ -d "key=YOUR_API_KEY" \ -d "method=base64" \ --data-urlencode "body=$(base64 < captcha.png | tr -d '\n')"
使用 --data-urlencode 适用于任何包含 +, / 或 =的内容。base64 数据这三个字符全都有,而普通的 -d 会把它们弄坏。
Turnstile 还会一并返回 user agent
Cloudflare 会把 token 绑定到生成它的浏览器指纹上,所以用另一个 user agent 提交这个 token 就会被拒绝,哪怕 token 本身完全有效。原始 API 会在两个地方给出实际使用的那一个:
- 使用
json=1时,作为响应上的userAgent字段。 - 在纯文本模式下,作为响应头
X-Turnstile-User-Agent响应头读取相同的值。
# -i prints the headers, which is where the user agent lives # when you are not using json=1. curl -i -X POST http://127.0.0.1:8080/res.php \ -d "key=YOUR_API_KEY" -d "action=get" -d "id=2122988149" X-Turnstile-User-Agent: Mozilla/5.0 ... OK|0.abc123...
整页挑战还需要 data (也就是 cData 值)和 pagedata (chlPageData),要在提交前立刻从页面上抓取。小组件模式两者都不需要。 Turnstile 识别工具 页面对这个区别有更详细的说明。
极验(GeeTest)的答案是三个字段,而不是一个
极验不会返回单个 token。请求 JSON,你就会拿到该站点自己的前端本来会回传的那三个值。
{
"status": 1,
"request": {
"geetest_challenge": "...",
"geetest_validate": "...",
"geetest_seccode": "..."
}
}加载的 gt 值对每个站点是固定的。而 challenge 值是一次性的,大约一分钟后就失效,所以要在提交前立刻获取,绝不要在长脚本的开头就取。
让识别走代理
两个参数,加到同一个 /in.php 调用里:
curl -X POST http://127.0.0.1:8080/in.php \ -d "key=YOUR_API_KEY" \ -d "method=userrecaptcha" \ -d "googlekey=YOUR_SITEKEY" \ -d "pageurl=https://example.com/page-with-recaptcha" \ -d "proxy=login:[email protected]:3128" \ -d "proxytype=HTTPS"
proxytype 接受 HTTP、HTTPS、SOCKS5 或 SOCKS5H。代理只对 reCAPTCHA、Turnstile 和极验生效。图片验证码识别读取的是你手头已有的像素,根本不会接触目标站点,所以代理在那里毫无作用。
一段完整脚本
提交、带上限地轮询、打印 token。大约二十行,唯一的依赖就是 curl.
#!/usr/bin/env bash
set -euo pipefail
API="http://127.0.0.1:8080"
KEY="YOUR_API_KEY"
# Submit and keep only the part after the pipe.
ID=$(curl -s -X POST "$API/in.php" \
-d "key=$KEY" -d "method=userrecaptcha" \
-d "googlekey=YOUR_SITEKEY" \
-d "pageurl=https://example.com/page-with-recaptcha" | cut -d'|' -f2)
sleep 15
# Poll every 5s, give up after 20 tries so this cannot hang forever.
for _ in $(seq 20); do
R=$(curl -s -X POST "$API/res.php" -d "key=$KEY" -d "action=get" -d "id=$ID")
[ "$R" = "CAPCHA_NOT_READY" ] || { echo "${R#OK|}"; exit 0; }
sleep 5
done
echo "timed out waiting for $ID" >&2; exit 1加载的 || { ...; exit 0; } 分支会在任何不等于 CAPCHA_NOT_READY的内容上触发,其中也包括错误码。这是刻意为之:出错意味着停下,而不是继续轮询。
在这一层会遇到的错误
| 代码 | 含义 | 修复 |
|---|---|---|
ERROR_WRONG_USER_KEY | 密钥缺失或为空 | 发送任意非空的 key |
ERROR_WRONG_METHOD | 错误的 method 或 action | 对照上表检查拼写 |
ERROR_BAD_PARAMETERS | 缺少某个必需参数 | 对照上面的必需参数列检查 |
ERROR_GOOGLEKEY | 加载的 googlekey 值被拒绝 | 你可能误把 sitekey 发过来了 |
ERROR_PAGEURL | 加载的 pageurl 值被拒绝 | 带上协议头,并用 POST 而不是 GET |
CAPCHA_NOT_READY | 仍在处理中 | 这不是错误。继续轮询同一个 ID |
| 响应为空 | 已经读过,或者根本没有这个 ID | 结果只能读一次。第一次拿到就存下来 |
API 可能返回的每个错误码的确切文案,都列在 API 文档.
什么时候该放下 curl
原始 HTTP 非常适合冒烟测试、shell 管道,或者没有官方客户端的语言。而对于应用代码, CapSkip SDK 值得为它多加一个依赖,最重要的理由只有一个:它们不会按固定间隔轮询。它们从 250 毫秒起步,逐步退避到 pollingInterval,因此一次识别通常比上面那个手写循环更快返回,而后者每次都要老老实实等满 15 秒。
它们还会把错误字符串变成有类型的异常,并替你处理好 Turnstile 的 user agent 和极验的三字段答案。Python、Node.js、PHP 和 .NET 都有官方客户端。
常见问题
我可以用 GET 代替 POST 吗?
可以,两个端点都接受。问题在于, pageurl 如果自身带有查询字符串,就会在第一个未编码的 & 处被截断,于是你会收到 ERROR_PAGEURL ,或者对着错误的页面完成一次识别。如果非用 GET 不可,先把 URL 交给 --data-urlencode 处理一遍。
我该发送什么 API 密钥?
任意非空字符串。密钥校验默认关闭,因为服务只监听 localhost,所以 key=capskip 也完全可用。但它必须存在:省略它,你得到的是 ERROR_WRONG_USER_KEY ,而不是识别结果。
在 Windows PowerShell 里能用吗?
使用 curl.exe ,必须这样显式写出来。在 PowerShell 中, curl 是 Invoke-WebRequest的别名,而后者并不认识 -d ,会抛出一个看起来像 API 问题的参数错误。写成 curl.exe 就能绕过这个别名。
我可以同时跑多个识别任务吗?
可以。你想提交多少任务就提交多少,然后各自独立轮询每个 ID。没有任何东西会把它们串行化,也没有每分钟配额,因为计算发生在你自己的硬件上,而不是在共享队列里。
小结
把任务 POST 到 /in.php,保存返回的 ID,按类型等待相应的时长,然后轮询 /res.php ,直到拿到的东西不再是 CAPCHA_NOT_READY。若响应由脚本读取,请加上 json=1 。注意两个命名陷阱: googlekey 用于 reCAPTCHA,而 sitekey 用于 Turnstile;另外别忘了结果只能读取一次。
因为它是一个 验证码识别工具 ,就跑在你自己的机器上,上面那个循环既没有配额要遵守,也没有余额要充值。把它指向 localhost,唯一的限制就是你的 CPU 处理队列的速度。
