Explainers

reCAPTCHA v2 Invisible:触发检测和解决

reCAPTCHA v2 Invisible 不渲染控件,只在按钮被点击、或代码执行到 grecaptcha.execute() 时才触发。典型表现:接口返回 200,后端却提示校验没过,页面上找不到复选框。

要找的不是元素,而是调用。整条链路三步:判定类型、提交时带 invisible=1、触发页面回调。


四种方法判断是不是 Invisible reCAPTCHA

控制台里从声明式控件查到程序化调用,命中一条即停。

方法一:读 data-size 属性

// Browser console
const widgets = document.querySelectorAll('.g-recaptcha');
widgets.forEach((el, i) => {
  const size = el.getAttribute('data-size');
  const sitekey = el.getAttribute('data-sitekey');
  console.log(`Widget ${i}: size=${size}, sitekey=${sitekey}`);
  if (size === 'invisible') {
    console.log('  → This is Invisible reCAPTCHA');
  }
});

声明式渲染的站点把类型写在属性里,data-size="invisible" 即可确认。

方法二:查 grecaptcha-badge 徽章

const badge = document.querySelector('.grecaptcha-badge');
if (badge) {
  console.log('reCAPTCHA badge found — likely Invisible reCAPTCHA');
  console.log('Badge visibility:', getComputedStyle(badge).visibility);
}

徽章常被 CSS 隐藏,但元素还在 DOM 里,查得到就基本是 Invisible 版。

方法三:在内联脚本里搜 grecaptcha.execute

// Look for grecaptcha.execute in page scripts
document.querySelectorAll('script:not([src])').forEach(s => {
  if (s.textContent.includes('grecaptcha.execute')) {
    console.log('Found grecaptcha.execute — Invisible reCAPTCHA');
    const match = s.textContent.match(/grecaptcha\.execute\s*\(\s*['"]?([^'",\s)]+)/);
    if (match) console.log('Sitekey:', match[1]);
  }
});

前两步落空说明是程序化渲染,这一步顺带抓出 sitekey。

方法四:看脚本的 render 参数

document.querySelectorAll('script[src*="recaptcha"]').forEach(s => {
  if (s.src.includes('render=') && !s.src.includes('render=explicit')) {
    console.log('Invisible/v3 reCAPTCHA detected in script:', s.src);
  }
});

注意区分:不带 action 的是 v2 Invisible,带 {action: 'submit'} 的是 v3,认错 token 会被拒收。


Invisible 与标准 v2 差在哪

对比项 reCAPTCHA v2 标准版 reCAPTCHA v2 Invisible
可见控件 有(复选框) 无(仅徽章)
data-size normalcompact invisible
DOM 结构 .g-recaptcha div div 或程序化调用

检测思路要从“找元素”换成“找调用”。


用 CaptchaAI 识别 Invisible reCAPTCHA:invisible=1 是关键

方法名仍是 userrecaptcha,只多一个 invisible=1。必填三项:googlekeypageurlinvisible

Python 示例

import requests
import time

API_KEY = "YOUR_API_KEY"

# Submit with invisible flag
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6Le-SITEKEY",
    "pageurl": "https://staging.example.com/qa-login",
    "invisible": "1",  # critical for Invisible reCAPTCHA
    "json": "1",
}).json()

if resp["status"] != 1:
    raise Exception(f"Submit error: {resp['request']}")

task_id = resp["request"]
print(f"Submitted: {task_id}")

# Poll for result
for _ in range(24):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()

    if result["status"] == 1:
        print(f"Token: {result['request'][:50]}...")
        break
    if result["request"] != "CAPCHA_NOT_READY":
        raise Exception(f"Error: {result['request']}")

每 5 秒轮询一次 res.php,最多 24 次;CAPCHA_NOT_READY 继续等,其他错误码直接抛出。

Node.js 示例

const axios = require('axios');

const resp = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: 'YOUR_API_KEY',
    method: 'userrecaptcha',
    googlekey: '6Le-SITEKEY',
    pageurl: 'https://staging.example.com/qa-login',
    invisible: 1,
    json: 1,
  }
});
const taskId = resp.data.request;
console.log(`Submitted: ${taskId}`);

CaptchaAI 按线程计费:BASIC $15/月、5 线程,STANDARD $30/月、15 线程,套餐内次数不限,要估的是并发。


拿到 token 后必须触发回调

只写 g-recaptcha-response 不够,页面等的是 data-callback 被调用:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")

# After solving, inject and trigger
driver.execute_script("""
    // Set the token
    document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];

    // Find and trigger the callback
    var widget = document.querySelector('.g-recaptcha');
    var callbackName = widget ? widget.getAttribute('data-callback') : null;

    if (callbackName && typeof window[callbackName] === 'function') {
        window[callbackName](arguments[0]);
    }
""", token)

# Submit the form
driver.find_element(By.CSS_SELECTOR, "form#login").submit()

顺序固定,缺一步就过不了:

  1. 写入 g-recaptcha-response
  2. 调用 data-callback
  3. 提交表单。

回调没反应?先找触发元素

一种绑在按钮上:

<button class="g-recaptcha"
        data-sitekey="6Le-SITEKEY"
        data-callback="onSubmit"
        data-size="invisible">
  Submit
</button>

另一种写在点击事件里:

// Site's code
document.getElementById('submit-btn').addEventListener('click', function() {
  grecaptcha.execute();
});

两种一起扫一遍:

// Find elements with g-recaptcha class that are buttons
document.querySelectorAll('button.g-recaptcha, input.g-recaptcha').forEach(el => {
  console.log('Trigger element:', el.tagName, el.textContent.trim());
  console.log('  data-sitekey:', el.getAttribute('data-sitekey'));
  console.log('  data-callback:', el.getAttribute('data-callback'));
});

仍找不到,就去 Network 面板抓初始化请求。


国内联调要注意什么

reCAPTCHA 依赖 Google 托管脚本,内地网络下不一定加载得上,grecaptcha 未定义、回调不触发的表现和识别失败很像,先确认脚本再怀疑 token。国内站点主流是 GeeTest(极验)、网易易盾,reCAPTCHA 多见于出海业务;调试请在自有或已授权的站点上做。


排查清单

现象 原因 处理方式
token 被拒收 漏了 invisible=1 补上 invisible: "1"
找不到 sitekey .g-recaptcha div 改查 grecaptcha.execute()
提交后校验失败 回调未触发 data-callback 手动调用

常见问题

徽章被 CSS 隐藏了还能判断吗?

能。CSS 只影响显示,.grecaptcha-badge 仍在 DOM 中;连徽章都没有就用方法三。

token 提交了页面为什么没反应?

多半是回调没调用。校验逻辑写在 data-callback 里,只改文本域不算数。

CaptchaAI 支持极验和 hCaptcha 吗?

GeeTest(极验)v3 支持,v4 官方口径是即将支持;CaptchaFox、Friendly Captcha、Lemin 为测试版;hCaptcha 与 FunCaptcha 不支持。


用 CaptchaAI 跑通 Invisible 流程

前往 CaptchaAI 官网领取 API Key,把上面的片段接到自有 staging 表单跑通。


相关指南

该文章已禁用评论。