先给结论:对写自动化的人来说,reCAPTCHA v3 的标准版和 Enterprise 版只差一个提交参数——enterprise=1。两者都不弹窗、不出九宫格,都返回 0.0(机器人)到 1.0(真人)的分数。真正踩坑的不是识别本身,而是 Enterprise 允许站点给每个 action 单独设阈值。同一个 token 在标准版放行、在 Enterprise 被打回,原因通常就在这里。
一分钟判断站点用的是标准版 v3 还是 Enterprise
肉眼分不出来,两版都在后台静默打分,只能看两处:脚本是 enterprise.js 还是 api.js,JS 调的是 grecaptcha.enterprise.execute() 还是 grecaptcha.execute()。另外 v3 的 sitekey 在脚本 URL 的 render= 里,不在 data-sitekey 上。
一段正则就能把版本、sitekey 和 action 一起抓出来:
import requests
import re
def detect_v3_version(url):
html = requests.get(url).text
if "enterprise.js" in html:
version = "enterprise"
elif "recaptcha/api.js" in html and "render=" in html:
version = "standard"
else:
return None
# Extract sitekey
key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
sitekey = key_match.group(1) if key_match else None
# Extract action
action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
action = action_match.group(1) if action_match else None
return {"version": version, "sitekey": sitekey, "action": action}
Node.js 同理:
const axios = require("axios");
async function detectV3Version(url) {
const { data: html } = await axios.get(url);
const version = html.includes("enterprise.js")
? "enterprise"
: html.includes("recaptcha/api.js") && html.includes("render=")
? "standard"
: null;
const keyMatch = html.match(/render[=:]\s*['"]?([A-Za-z0-9_-]{40})/);
const actionMatch = html.match(/action['"]?\s*[:=]\s*['"](\w+)/);
return {
version,
sitekey: keyMatch?.[1],
action: actionMatch?.[1],
};
}
别把版本写死。站点切到 Enterprise 往往只换一行脚本,不会有公告;判断放进每次请求最稳。
标准版 v3 与 Enterprise 的能力对照
多出来的功能几乎都在站点方,不在你这一侧:
| 功能项 | 标准版 v3 | Enterprise v3 |
|---|---|---|
| 静默运行 | 是 | 是 |
| 分数(0.0–1.0) | 是 | 是 |
| action 参数 | 必填 | 必填 |
| 原因代码 | 无 | 有 |
| 按 action 自定义阈值 | 无 | 有(Cloud Console) |
| 密码泄露检测 | 无 | 有 |
| Account Defender | 无 | 有 |
| 反欺诈标签 | 无 | 有 |
| 多因素认证集成 | 无 | 有 |
| 校验接口 | siteverify(免费) |
recaptchaenterprise.googleapis.com |
| 每月额度 | 100 万次评估免费 | 按次计费 |
| JS 文件 | api.js?render=KEY |
enterprise.js?render=KEY |
| CaptchaAI 提交参数 | version=v3 |
version=v3 + enterprise=1 |
对你直接生效的只有最后一行;其余决定站点后台怎么解读你的 token。
原因代码:站点看得到,你看不到
Enterprise 的评估响应带一组原因代码,说明分数怎么来的。它们只返回给站点后端,不出现在 CaptchaAI 的识别结果里;价值在于反推站点在看什么。
| 代码 | 含义 | 对自动化的提示 |
|---|---|---|
AUTOMATION |
检测到自动化行为特征 | 用真实浏览器环境,别裸跑请求 |
UNEXPECTED_ENVIRONMENT |
浏览器环境异常 | 排查无头浏览器的默认特征 |
TOO_MUCH_TRAFFIC |
同一来源请求量过高 | 加限流,降低并发 |
UNEXPECTED_USAGE_PATTERNS |
交互节奏不正常 | 操作之间留出真实间隔 |
LOW_CONFIDENCE_SCORE |
信号太少,无法可信打分 | 让页面多加载一会儿再触发 |
SUSPECTED_CARDING |
疑似盗刷信用卡 | 与常规自动化无关 |
SUSPECTED_CHARGEBACK |
疑似拒付欺诈 | 与常规自动化无关 |
后两项针对支付欺诈,常规 QA 碰不到;前四项才是“分数变低”的实际来源。
阈值:Enterprise 真正变严的地方
标准版 v3 用 Google 的全局模型,站点通常只维护一个阈值,常见 0.5。Enterprise 在此之上又叠一层:
| 评分维度 | 标准版 | Enterprise |
|---|---|---|
| 基础模型 | Google 全局模型 | 全局模型 + 站点自定义信号 |
| 阈值粒度 | 全站一个 | 每个 action 一个(登录 0.7、结算 0.9) |
| 自适应学习 | 基础 | 基于本站流量持续学习 |
| 分数精度 | 0.0–1.0,两位小数 | 区间相同,信号更细 |
| 误判处理 | 人工调阈值 | 结合原因代码分析 |
所以 Enterprise 站点上 action 不再可有可无:你提交 action=verify,站点按 login 的 0.7 去卡,token 就白拿了。从 grecaptcha.enterprise.execute() 的实参里原样抠出 action 即可。
一个具体场景。 给出海电商后台做回归测试常撞上:注册页是标准版、阈值 0.5,怎么提交都过;下单页迁到 Enterprise 后单独给 checkout 设了 0.9。脚本一行没改,注册照常通过,下单却间歇性失败——同一站点两条链路两套阈值。分别提取并提交对应 action 即可。
国内站点更常见的是 GeeTest(极验)和腾讯防水墙,v3 基本只出现在出海业务上。
用 CaptchaAI 识别:两段代码的唯一差别
CaptchaAI 支持 v3 与 v3 Enterprise,方法都是 userrecaptcha,走 in.php 提交、res.php 轮询。标准版:
import requests
import time
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"version": "v3",
"googlekey": sitekey,
"action": "login",
"pageurl": page_url
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
Enterprise 只多一个 enterprise 参数,其余不变:
import requests
import time
# Only difference: enterprise=1
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"version": "v3",
"enterprise": 1,
"googlekey": sitekey,
"action": "login",
"pageurl": page_url
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
把判断和提交合成一个类最省事,站点换版本时代码不用动:
class V3AutoSolver:
def __init__(self, api_key):
self.api_key = api_key
def solve(self, page_url, action=None):
import re
html = requests.get(page_url).text
is_enterprise = "enterprise.js" in html
key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
if not key_match:
raise Exception("No v3 sitekey found")
if not action:
act_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
action = act_match.group(1) if act_match else "verify"
params = {
"key": self.api_key,
"method": "userrecaptcha",
"version": "v3",
"googlekey": key_match.group(1),
"action": action,
"pageurl": page_url
}
if is_enterprise:
params["enterprise"] = 1
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key, "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
return result.text.split("|")[1]
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve error: {result.text}")
raise Exception("Timed out")
计费也不按版本分账:CaptchaAI 按并发线程计费,套餐内识别次数不限,两版共用同一份线程额度。BASIC $15/月、5 线程,STANDARD $30/月、15 线程,ADVANCE $90/月、50 线程,更高并发再往上选。
常见故障对照
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| token 在 Enterprise 站点被拒 | 漏了 enterprise=1 |
确认加载的是 enterprise.js,补上参数 |
| Enterprise 站点分数偏低 | action 传错 |
从 grecaptcha.enterprise.execute() 取准确 action |
| 测试环境正常,生产失败 | 自适应学习认出了你的请求模式 | 降并发、拉开间隔,节奏更接近真实用户 |
ERROR_WRONG_GOOGLEKEY |
从 data-sitekey 取值了 |
v3 的 sitekey 在脚本 URL 的 render= 里 |
长时间 CAPCHA_NOT_READY |
轮询太急或任务在排队 | 保持 5 秒一次,设整体超时上限 |
常见问题
action 传错会怎样,可以随便填一个吗?
标准版站点通常只有一个全站阈值,填错影响不大;Enterprise 按 action 分别设阈值,填错等于拿另一条链路的判定线来卡你,token 大概率被拒。原样取值,不要自己编。
Enterprise 站点上的分数为什么比标准版低?
分数未必更低,是判定线更高。Enterprise 允许按 action 分别设阈值,同一个 0.7 在一条链路上通过、在另一条上就不够用。先确认该 action 的阈值再看分数。
站点从标准版迁到 Enterprise,我要改多少代码?
提交侧只需加 enterprise=1。真正该改的是判断逻辑:版本检测放进每次请求,站点静默换版时才不会整批失败。
原因代码能在 CaptchaAI 的返回里看到吗?
看不到。原因代码由 Google 的评估接口返回给站点后端,识别服务这一侧拿不到。它的用处是反向排查:知道站点在看 AUTOMATION、TOO_MUCH_TRAFFIC 这类信号,就能判断该调并发还是该调节奏。
国内测试环境跑 v3 老是超时,是识别慢吗?
多半不是。reCAPTCHA 的脚本由 Google 域名分发,境内加载本身就不稳定,页面没渲染完就触发识别,自然拿不到结果。把这段网络耗时算进整体超时,或换一个能正常访问该站点的网络环境再测。