自动化脚本卡在 Cloudflare Turnstile 上,页面却看不到验证码图案——这是常见场景。Turnstile 在后台静默校验,仅在信号不足时弹出小组件,最终产出 cf-turnstile-response token 交给后端。
拿到 token 是唯一的通关条件。本文用 CaptchaAI API 演示提取 sitekey、提交任务、轮询、回填 token 四步,代码以 Python 与 Node.js 为主。先看 CaptchaAI 快速入门 了解通用 4 步模型更顺。
开始前要准备好三样东西
- CaptchaAI API key:在 captchaai.com 仪表盘获取。
- Turnstile sitekey:从目标页面提取,固定以
0x开头。 - 页面 URL:Turnstile 组件实际出现的完整地址。
- 运行环境:Python 3.7+ 或 Node.js 14+。
国内网络装 Python 依赖较慢时,可加镜像加速:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests。
第 1 步:从页面里挖出 sitekey
sitekey 一般写死在 HTML 的 div 或 script 标签里:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQFLr1GavNl"></div>
也可能是 JavaScript 动态渲染出来的:
turnstile.render('#widget', {
sitekey: '0x4AAAAAAAC3DHQFLr1GavNl',
callback: function(token) { /* ... */ }
});
定位 sitekey 有三种常见办法:
- DevTools:打开 Elements,搜索
data-sitekey或cf-turnstile。 - 查看源代码:
Ctrl+U,直接搜0x前缀字符串。 - Network 面板:过滤
challenges.cloudflare.com,参数含 sitekey。
sitekey 固定以
0x开头,长度约 22 字符,与 reCAPTCHA 的6L...前缀不同——认错 sitekey 是常见报错原因。
第 2 步:把任务提交给 CaptchaAI 解决 Turnstile
向 https://ocr.captchaai.com/in.php 发 POST 请求,method 填 turnstile:
import requests
API_KEY = "YOUR_CAPTCHAAI_KEY"
SITEKEY = "0x4AAAAAAAC3DHQFLr1GavNl"
PAGEURL = "https://staging.example.com/qa-login"
r = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": SITEKEY,
"pageurl": PAGEURL,
"json": 1,
})
data = r.json()
if data["status"] != 1:
raise RuntimeError(f"submit failed: {data}")
task_id = data["request"]
print("task id:", task_id)
Node.js 版本:
const axios = require("axios");
const { data } = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: process.env.CAPTCHAAI_KEY,
method: "turnstile",
sitekey: "0x4AAAAAAAC3DHQFLr1GavNl",
pageurl: "https://staging.example.com/qa-login",
json: 1,
},
});
if (data.status !== 1) throw new Error(`submit failed: ${JSON.stringify(data)}`);
const taskId = data.request;
成功返回 {"status": 1, "request": "<task_id>"},存好 task_id 供下一步轮询用。
第 3 步:轮询拿到 token
通常 10–25 秒出结果:先睡 10 秒,之后每 5 秒轮询一次,最多 40 次(约 200 秒上限):
import time
time.sleep(10)
for _ in range(40):
r = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
res = r.json()
if res["status"] == 1:
token = res["request"]
break
if res["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"solver error: {res}")
time.sleep(5)
else:
raise TimeoutError("turnstile solving timed out")
print("token (前 60 字符):", token[:60])
token 是一段 Base64 字符串,以 0. 开头,长度约 400–600 字符,比 reCAPTCHA token 更长,属正常现象。
第 4 步:把 token 写回页面并提交表单
把 token 塞进表单隐藏字段 cf-turnstile-response,再提交表单。
Selenium:
driver.execute_script(
"document.querySelector('[name=cf-turnstile-response]').value = arguments[0];",
token,
)
driver.find_element("css selector", "form").submit()
Playwright:
page.evaluate(
"(t) => document.querySelector('[name=cf-turnstile-response]').value = t",
token,
)
page.click("button[type=submit]")
纯 HTTP 提交: 不跑浏览器时,把 token 放进请求体的 cf-turnstile-response 字段一起 POST。
token 有效期约 120–300 秒,拿到立刻用;超时后端会返回
timeout-or-duplicate,需回第 2 步重来。
完整 Python 示例
import os, time, requests
API = "https://ocr.captchaai.com"
KEY = os.environ["CAPTCHAAI_KEY"]
def solve_turnstile(sitekey: str, pageurl: str) -> str:
r = requests.post(f"{API}/in.php", data={
"key": KEY, "method": "turnstile",
"sitekey": sitekey, "pageurl": pageurl, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] != 1:
raise RuntimeError(f"submit: {j}")
tid = j["request"]
time.sleep(10)
for _ in range(40):
r = requests.get(f"{API}/res.php", params={
"key": KEY, "action": "get", "id": tid, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] == 1:
return j["request"]
if j["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"poll: {j}")
time.sleep(5)
raise TimeoutError("timeout")
if __name__ == "__main__":
print(solve_turnstile("0x4AAAAAAAC3DHQFLr1GavNl", "https://staging.example.com/qa-login"))
常见错误码怎么处理
ERROR_WRONG_USER_KEY(key 格式不对):确认CAPTCHAAI_KEY完整、无多余空格。ERROR_KEY_DOES_NOT_EXIST(key 不存在):回仪表盘核对 key,勿用截断版本。ERROR_ZERO_BALANCE(余额为零):先充值再重试。ERROR_PAGEURL(缺少 pageurl):确认传入完整 URL,含https://。ERROR_CAPTCHA_UNSOLVABLE(多次失败):核对 sitekey 与 pageurl 是否配对,再重试一次。
更多错误码见 reCAPTCHA v2 教程,两者共用同一套错误体系。
跑不通时按这个顺序排查
- sitekey 动态变化:部分站点每次访问签发新 sitekey,需先抓页面再提取。
- pageurl 不精确:须与实际页面完全一致(含路径,不含 query)。
- TLS 特征被拦截:建议用真实浏览器、
curl_cffi或 Playwright 提交,而非裸requests。 - token 已过期:超过 2 分钟未用需回第 2 步重提交。
- 出口 IP 质量差:数据中心 IP 段易触发额外挑战,优先用自有服务器基础设施。
常见问题
一直返回 CAPCHA_NOT_READY,是代码写错了吗?
不是,这是正常中间状态,任务还在队列里,按 5 秒间隔继续轮询即可;超过 40 次仍如此才需检查 sitekey/pageurl。
token 能保留下来下次复用吗?
不能。token 与本次请求绑定,有效期约 120–300 秒,过期或用过即失效,每次都要重新提交加轮询。
隐形 Turnstile(看不到小部件)也能这样解决吗?
可以,流程完全一样,依然只需要 sitekey 和 pageurl。
高频调用会被限流或额外收费吗?
CaptchaAI 按线程数计费,套餐内单线程求解次数不设上限,不按次数扣费;超出并发线程数的请求会排队而非直接失败。
下一步
- 阅读 CaptchaAI 快速入门,掌握通用的 4 步调用模型。
- 遇到相似场景可参考 reCAPTCHA v2 解决方案。
- 去 captchaai.com 注册拿 API key,接入上面的代码。
免费注册 CaptchaAI,5 分钟内拿到你的第一个 Turnstile token。