Comparisons

标准与企业 reCAPTCHA v3 解决指南

从调用识别接口的角度看,标准版 v3 和 Enterprise v3 的区别只有一个参数:enterprise=1。提交方式、轮询节奏、拿到的 token 格式完全一样。

麻烦在前一步:判断页面用的是哪一个。两个版本都不渲染可见控件,参数写反的后果却很直接——接口正常返回 token,提交却被后端拒绝,日志上表现为“识别成功但登录失败”。


第一步:判断页面用的是标准版还是 Enterprise

判据只能从页面加载的 JavaScript 里找,可靠信号有两个:脚本文件是 api.js 还是 enterprise.js,执行函数是 grecaptcha.execute() 还是 grecaptcha.enterprise.execute()

Python:抓页面源码判断

import requests
import re

def detect_v3_type(url):
    resp = requests.get(url)
    html = resp.text

    # Check for enterprise.js
    if "enterprise.js" in html:
        version = "enterprise_v3"
        execute_fn = "grecaptcha.enterprise.execute"
    elif "recaptcha/api.js" in html and "render=" in html:
        version = "standard_v3"
        execute_fn = "grecaptcha.execute"
    else:
        return None

    # Extract sitekey from render parameter
    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 parameter
    action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
    action = action_match.group(1) if action_match else "unknown"

    return {
        "version": version,
        "sitekey": sitekey,
        "action": action,
        "execute_fn": execute_fn
    }

info = detect_v3_type("https://staging.example.com/qa-login")
print(info)

Node.js:同一套判据

const axios = require("axios");

async function detectV3Type(url) {
  const { data: html } = await axios.get(url);

  let version, executeFn;
  if (html.includes("enterprise.js")) {
    version = "enterprise_v3";
    executeFn = "grecaptcha.enterprise.execute";
  } else if (html.includes("recaptcha/api.js") && html.includes("render=")) {
    version = "standard_v3";
    executeFn = "grecaptcha.execute";
  } else {
    return 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] || null,
    action: actionMatch?.[1] || "unknown",
    executeFn,
  };
}

排查时的控制台快检

// Paste in DevTools console
if (document.querySelector('script[src*="enterprise.js"]')) {
  console.log("Enterprise v3");
  console.log("Execute:", typeof grecaptcha?.enterprise?.execute);
} else if (document.querySelector('script[src*="api.js"][src*="render="]')) {
  console.log("Standard v3");
  console.log("Execute:", typeof grecaptcha?.execute);
}

检测要放在每次加载页面后执行,不要把版本写死在配置里——站点迁到 Enterprise 不会通知集成方。


两个版本的差异对照

对比项 标准 v3 Enterprise v3
是否显示控件 无,隐形运行 无,隐形运行
分数(0.0–1.0)
action 参数 必填 必填
脚本文件 api.js?render=KEY enterprise.js?render=KEY
执行函数 grecaptcha.execute() grecaptcha.enterprise.execute()
原因码 有(AUTOMATION、TOO_MUCH_TRAFFIC 等)
按 action 配置阈值 不支持 支持,在 Cloud Console 配置
密码泄露检测
Account Defender
站点侧校验接口 siteverify,免费 recaptchaenterprise.googleapis.com,按量付费
CaptchaAI 提交参数 version=v3 version=v3enterprise=1
典型识别耗时 10–20 秒 10–20 秒

原因码、自定义阈值属于站点运营方,集成方看不到配置,它们只影响后端对同一个 token 判定的宽严。


用 CaptchaAI 识别:两段代码只差一行

标准 v3

import requests
import time

# Submit
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]

# Poll
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 v3:多一个 enterprise 参数

提交时加上 enterprise: 1,轮询部分一个字不改:

import requests
import time

# Submit — add enterprise=1
resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "version": "v3",
    "enterprise": 1,  # Required for Enterprise
    "googlekey": sitekey,
    "action": "login",
    "pageurl": page_url
})
task_id = resp.text.split("|")[1]

# Polling is identical to standard
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

自动判断版本的通用封装

生产脚本更实用的写法是检测和提交合成一个方法:页面只拉一次,版本、sitekey、action 一起取出。

import requests
import time
import re

class RecaptchaV3Solver:
    def __init__(self, api_key):
        self.api_key = api_key

    def detect_and_solve(self, page_url, action=None):
        """Auto-detect standard vs enterprise and solve."""
        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")
        sitekey = key_match.group(1)

        if not action:
            action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
            action = action_match.group(1) if action_match else "verify"

        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "version": "v3",
            "googlekey": sitekey,
            "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 failed: {result.text}")

        raise Exception("Timed out")

solver = RecaptchaV3Solver("YOUR_API_KEY")
token = solver.detect_and_solve("https://staging.example.com/qa-login", action="login")
print(f"Token: {token[:40]}...")

action 参数:分数高低的隐形开关

action 是 v3 独有的上下文标记,两个版本都必填。站点给不同动作设不同期望值,登录页发成 homepage,分数就会偏低、token 被拒。正确做法是从页面脚本里抓真实取值:

import re

def find_v3_actions(html):
    """Extract all action parameters from page JavaScript."""
    # Look for grecaptcha.execute(key, {action: '...'})
    pattern = r"(?:grecaptcha\.(?:enterprise\.)?execute|action)\s*[(:]\s*['\"](\w+)"
    actions = re.findall(pattern, html)
    return list(set(actions))

# Common actions: "login", "submit", "register", "checkout", "homepage"

抓不到时的排查顺序:

  • 在页面脚本里搜 execute(,看括号内的第二个参数;
  • 再按 loginsubmitcheckout 逐个试;
  • 每次都看后端返回,而不是只看拿没拿到 token。

把 token 交回页面

两个版本一致:写进 g-recaptcha-response 字段,或直接放进 POST 请求体。

# For browser-based workflows (Selenium)
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").value = "{token}";'
)

# For pure HTTP workflows
requests.post(page_url, data={
    "g-recaptcha-response": token,
    "username": "user",
    "password": "pass"
})
// Puppeteer
await page.evaluate((tok) => {
  document.getElementById("g-recaptcha-response").value = tok;
}, token);

// Pure HTTP (axios)
await axios.post(pageUrl, new URLSearchParams({
  "g-recaptcha-response": token,
  username: "user",
  password: "pass",
}));

出海业务里的实际情况

国内站点很少用 reCAPTCHA v3,主流是 GeeTest(极验)、网易易盾、腾讯防水墙;v3 更多出现在出海电商、跨境 SaaS 和海外合作方页面上。由此带来一个常见误判:reCAPTCHA 脚本由 Google 域名分发,境内网络下不一定能稳定加载,本地调试时页面一直转圈,往往是脚本没下载下来,和识别服务无关。这类回归建议放到境外服务器跑。

自动化只应针对自有或已授权的环境。网络安全法、数据安全法、PIPL 和 robots 协议是绕不开的边界。


最常见的五个参数错误

现象 直接原因 处理方式
标准 v3 上带了 enterprise=1 token 可能不被站点接受 加参数前先确认页面里有 enterprise.js
Enterprise v3 上漏掉 enterprise=1 token 被后端拒绝 检测到 enterprise.js 就必须带上
action 取值不对 分数偏低,token 被拒 从页面 JavaScript 里提取真实字符串
漏写 version=v3 任务被当成 v2 处理 基于分数的 reCAPTCHA 一律带 version=v3
用 v2 的 sitekey 提交 v3 返回 ERROR_WRONG_GOOGLEKEY v3 的 sitekey 来自 render=KEY 参数

常见问题

分数很低,是识别服务不行吗?

不一定。v3 的分数由 Google 结合站点自身数据给出,action 是否匹配、请求来源、账号状态都会影响。先确认 action 与页面脚本一致,再看对方阈值是否偏严。

识别 Enterprise v3 更耗资源吗?

不会。两者提交方式、轮询逻辑和典型耗时(10–20 秒)一样,占用同一个线程。CaptchaAI 按并发线程计费而非按次计费:BASIC $15/月 5 线程、STANDARD $30/月 15 线程、ADVANCE $90/月 50 线程,套餐内识别次数不限。

在 Selenium 脚本里怎么判断当前页面是 Enterprise?

在已加载的页面源码里搜 enterprise.js 即可:

page_source = driver.page_source
is_enterprise = "enterprise.js" in page_source

轮询多久算超时?

按 5 秒一次轮询,v3 通常 10–20 秒返回,上限设 2–3 分钟比较稳妥,超时就重新提交。返回值既不是 CAPCHA_NOT_READY 也不是 OK|,说明是错误码,应立刻中断记录。


相关指南

该文章已禁用评论。