在接口层面,reCAPTCHA v2 Enterprise 和标准 v2 只差一个参数:提交任务时加上 enterprise=1。method 仍是 userrecaptcha,返回的仍是 g-recaptcha-response,现有 v2 代码可以直接复用。
小部件长得一样,差别在后端:Enterprise 走 Google 企业风控更严格地校验 token。容易踩坑的是三点:sitekey 取自 anchor URL、sa= 的 action 不能漏、token 要配响应里的 user_agent。
动手前的准备
| 需要的东西 | 说明 |
|---|---|
| CaptchaAI API Key | 在 CaptchaAI 控制台 注册获取 |
| Node.js 14+ | 用内置 fetch,更低版本装 node-fetch |
| sitekey | anchor URL 里的 k= 参数 |
| pageurl | 验证码所在页面的完整 URL |
| action(可选) | anchor URL 里的 sa= 参数 |
装依赖记得带国内镜像源。
第 1 步:确认这是 Enterprise v2
打开 DevTools 的 Network 面板,按 anchor 过滤:
https://www.google.com/recaptcha/enterprise/anchor?ar=1&k=6LdxxXXxAAAAAAcX...&sa=LOGIN&...
k= 是 sitekey,sa=(如果有)是 action。标准 v2 走 /recaptcha/api2/anchor,看到 api2 就别加 enterprise=1。
第 2 步:向 CaptchaAI 提交任务
把 sitekey、pageurl 和 enterprise=1 一起发给 in.php,拿回任务 ID:
const API_KEY = "YOUR_API_KEY";
async function submitTask(sitekey, pageurl, action) {
const params = new URLSearchParams({
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
enterprise: "1",
json: "1",
});
if (action) {
params.set("action", action);
}
const response = await fetch(
`https://ocr.captchaai.com/in.php?${params}`
);
const data = await response.json();
if (data.status !== 1) {
throw new Error(`Submit failed: ${data.request}`);
}
console.log(`Task submitted. ID: ${data.request}`);
return data.request;
}
json=1让接口返回 JSON;页面没有 action 就别传,也别传空字符串。
第 3 步:轮询识别结果
第一次轮询前等 20 秒,之后每 5 秒查一次 res.php:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function pollResult(taskId) {
await delay(20000);
for (let attempt = 0; attempt < 30; attempt++) {
const params = new URLSearchParams({
key: API_KEY,
action: "get",
id: taskId,
json: "1",
});
const response = await fetch(
`https://ocr.captchaai.com/res.php?${params}`
);
const data = await response.json();
if (data.status === 1) {
console.log(`Solved. Token: ${data.request.substring(0, 60)}...`);
return {
token: data.request,
userAgent: data.user_agent || "",
};
}
if (data.request !== "CAPCHA_NOT_READY") {
throw new Error(`Solve failed: ${data.request}`);
}
console.log(`Attempt ${attempt + 1}: not ready, waiting 5s...`);
await delay(5000);
}
throw new Error("Solve timed out");
}
CAPCHA_NOT_READY表示还在识别中(拼写是接口原样),识别耗时一般 15–30 秒。- 返回其他值即任务失败,应立刻抛错,别继续轮询。
第 4 步:把 token 提交回表单
结果作为 g-recaptcha-response 随表单提交;响应带了 user_agent 就用同一个值发请求头:
async function submitForm(token, userAgent) {
const headers = { "Content-Type": "application/x-www-form-urlencoded" };
if (userAgent) {
headers["User-Agent"] = userAgent;
}
const response = await fetch("https://example.com/api/login", {
method: "POST",
headers,
body: new URLSearchParams({
username: "user",
password: "pass",
"g-recaptcha-response": token,
}),
});
console.log(`Response status: ${response.status}`);
return response;
}
Puppeteer 或 Playwright 里做法一样:把 token 写进页面的 g-recaptcha-response 文本域再提交。
完整可运行脚本
const API_KEY = "YOUR_API_KEY";
const SITE_KEY = "6LdxxXXxAAAAAAcXxxXxxX91xxxxxxxx8xxOx7A";
const PAGE_URL = "https://staging.example.com/qa-login";
const ACTION = "LOGIN"; // optional — omit if not in anchor URL
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveRecaptchaV2Enterprise() {
// Submit task
const submitParams = new URLSearchParams({
key: API_KEY,
method: "userrecaptcha",
googlekey: SITE_KEY,
pageurl: PAGE_URL,
enterprise: "1",
action: ACTION,
json: "1",
});
const submitRes = await fetch(
`https://ocr.captchaai.com/in.php?${submitParams}`
);
const submitData = await submitRes.json();
if (submitData.status !== 1) {
throw new Error(`Submit error: ${submitData.request}`);
}
const taskId = submitData.request;
console.log(`Task ID: ${taskId}`);
// Poll for result
await delay(20000);
for (let i = 0; i < 30; i++) {
const pollParams = new URLSearchParams({
key: API_KEY,
action: "get",
id: taskId,
json: "1",
});
const pollRes = await fetch(
`https://ocr.captchaai.com/res.php?${pollParams}`
);
const pollData = await pollRes.json();
if (pollData.status === 1) {
return {
token: pollData.request,
userAgent: pollData.user_agent || "",
};
}
if (pollData.request !== "CAPCHA_NOT_READY") {
throw new Error(`Solve error: ${pollData.request}`);
}
await delay(5000);
}
throw new Error("Solve timed out");
}
(async () => {
const { token, userAgent } = await solveRecaptchaV2Enterprise();
console.log(`Token: ${token.substring(0, 60)}...`);
if (userAgent) console.log(`User-Agent: ${userAgent}`);
})();
预期输出
Task ID: 73849562810
Token: 03AGdBq24PBCqLmOx2V4pGHJjkR2xZ1r...
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
排错对照表
| 错误 | 原因 | 处理方式 |
|---|---|---|
ERROR_WRONG_USER_KEY |
API Key 格式不对 | 确认是控制台里的 32 位字符 |
ERROR_KEY_DOES_NOT_EXIST |
找不到这个 Key | 到 CaptchaAI 控制台 核对 |
ERROR_ZERO_BALANCE |
余额不足 | 充值后重试 |
ERROR_BAD_TOKEN_OR_PAGEURL |
sitekey 或 URL 不对 | 从 anchor URL 重新取 k= |
ERROR_CAPTCHA_UNSOLVABLE |
本次未能识别 | 确认是 Enterprise v2 后重试 |
| 站点拒绝 token | User-Agent 不一致 | 用响应里返回的 user_agent |
出海项目的两个现实问题
国内站点多用 GeeTest(极验)、腾讯防水墙;Enterprise v2 更多出现在出海业务:海外 SaaS 后台、跨境电商商家中心。
- 前端脚本走 Google 域名:reCAPTCHA 的脚本由 Google 域名托管,内地网络下未必稳定,小部件加载不出来常被误判成代码 bug。识别请求走
ocr.captchaai.com,是另一条链路。 - 线程数按峰值并发估:CaptchaAI 按线程计费,套餐内识别次数不限,规划依据是“同时有多少页面在等结果”,不是每天跑多少次。BASIC($15/月,5 线程)够一个巡检脚本,多节点采集集群可选 ADVANCE($90/月,50 线程)。
采集范围请限定在自有或已获授权的站点,遵守《网络安全法》与 PIPL。
常见问题
拿到的 token 有效期多久?
大约 2 分钟,拿到就立刻提交表单,别先入库再消费。
一直返回 CAPCHA_NOT_READY 怎么办?
先确认 pageurl 是验证码真实出现的页面,再确认 sitekey 取自 Enterprise anchor URL。
识别失败会扣费吗?
不会按次扣。CaptchaAI 按线程计费,套餐内识别次数不限;失败任务只占线程时间,重试即可。
现在就跑通第一次识别
到 CaptchaAI 官网 注册拿 API Key,把脚本里的 sitekey、pageurl 换成自己的,几分钟就能拿到第一个 token。