两者都是使用相同底层技术的 reCAPTCHA v2 变体。 复选框版本显示“我不是机器人”并且可能呈现图像网格。 不可见版本会在用户操作(按钮单击、表单提交或页面加载)时自动触发,而没有可见的小部件。对于 CaptchaAI 解决方案,唯一的区别是将 invisible=1 添加到您的请求中 - 但token 提交过程有很大不同。
并排比较
| 特征 | v2 复选框 | v2 隐形 |
|---|---|---|
| 可见小部件 | 是的——“我不是机器人”复选框 | 否——对用户隐藏 |
| 触发机制 | 用户点击复选框 | 单击按钮、表单提交或页面加载 |
| 形象挑战 | 当 Google 怀疑有机器人时显示 | 可疑时右下角弹出 |
| 用户体验 | 适度摩擦 | 低/zero摩擦 |
| 执行 | 将 g-recaptcha div 添加到页面 |
将 data-callback 添加到按钮或 grecaptcha.execute() |
| 站点密钥格式 | 和隐形一样 | 与复选框相同 |
| 令牌字段 | g-recaptcha-response |
g-recaptcha-response |
| 回调函数 | 选修的 | 几乎总是需要 |
| CaptchaAI方法 | method=userrecaptcha |
method=userrecaptcha + invisible=1 |
| 令牌生命周期 | 120秒 | 120秒 |
| 求解时间 | 10-30秒 | 10-25秒 |
每个在 HTML 中的显示方式
v2 复选框
<!-- Standard checkbox widget -->
<div class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
data-callback="onSubmit">
</div>
<!-- Widget renders as: -->
<!-- [✓] I'm not a robot reCAPTCHA logo -->
用户看到复选框并与之交互。如果 Google 怀疑,则会内嵌显示图像网格挑战。
v2 隐形
<!-- Pattern 1: Invisible widget on a button -->
<button class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
data-callback="onSubmit"
data-size="invisible">
Submit
</button>
<!-- Pattern 2: Invisible div (programmatic trigger) -->
<div class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
data-size="invisible"
data-callback="onSubmit">
</div>
<!-- Pattern 3: Programmatic render -->
<script>
grecaptcha.render('submit-btn', {
sitekey: '6Le-wvkSAAAAAPBMRTvw...',
callback: onSubmit,
size: 'invisible'
});
</script>
没有可见的小部件。当用户触发指定元素或调用 grecaptcha.execute() 时,reCAPTCHA 就会激活。
如何检测页面上有哪个变体
Python检测:
import requests
from bs4 import BeautifulSoup
import re
def detect_recaptcha_variant(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# Check for invisible indicators
invisible_widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
if invisible_widget:
return {
"variant": "invisible",
"sitekey": invisible_widget.get("data-sitekey"),
"callback": invisible_widget.get("data-callback")
}
# Check for programmatic invisible in scripts
for script in soup.find_all("script"):
if script.string and "invisible" in str(script.string):
key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
if key_match:
return {
"variant": "invisible-programmatic",
"sitekey": key_match.group(1),
"callback": "check grecaptcha.render() call"
}
# Check for standard checkbox
checkbox_widget = soup.find(class_="g-recaptcha")
if checkbox_widget:
return {
"variant": "checkbox",
"sitekey": checkbox_widget.get("data-sitekey"),
"callback": checkbox_widget.get("data-callback")
}
return None
result = detect_recaptcha_variant("https://staging.example.com/qa-login")
print(result)
Node.js 检测:
const axios = require("axios");
const cheerio = require("cheerio");
async function detectRecaptchaVariant(url) {
const { data } = await axios.get(url);
const $ = cheerio.load(data);
// Check for invisible
const invisible = $(".g-recaptcha[data-size='invisible']");
if (invisible.length) {
return {
variant: "invisible",
sitekey: invisible.attr("data-sitekey"),
callback: invisible.attr("data-callback"),
};
}
// Check scripts for programmatic invisible
const scripts = $("script")
.map((_, el) => $(el).html())
.get()
.join("\n");
if (scripts.includes("invisible")) {
const keyMatch = scripts.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
if (keyMatch) {
return {
variant: "invisible-programmatic",
sitekey: keyMatch[1],
callback: "check render call",
};
}
}
// Check for standard checkbox
const checkbox = $(".g-recaptcha");
if (checkbox.length) {
return {
variant: "checkbox",
sitekey: checkbox.attr("data-sitekey"),
callback: checkbox.attr("data-callback"),
};
}
return null;
}
快速浏览器控制台检查:
const el = document.querySelector('[data-size="invisible"]');
console.log(el ? "Invisible reCAPTCHA" : "Checkbox reCAPTCHA");
| 检测信号 | v2 复选框 | v2 隐形 |
|---|---|---|
data-size="invisible" |
不存在 | 展示 |
| 页面上可见的复选框 | 是的 | 不 |
grecaptcha.execute() 已调用 |
否(用户点击) | 是(程序化) |
| 挑战弹出位置 | 内联,位于复选框下方 | 页面右下角 |
使用 CaptchaAI 解决
v2 复选框
import requests
import time
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": "6Le-wvkSAAAA...",
"pageurl": "https://example.com/form"
})
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
v2 隐形
import requests
import time
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": "6Le-wvkSAAAA...",
"pageurl": "https://example.com/form",
"invisible": 1 # Only parameter difference
})
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
token 提交——关键区别
v2 复选框:隐藏字段通常就足够了
# Selenium — inject into hidden field
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# If the page uses a callback, also call it
callback = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")
if callback:
driver.execute_script(f'{callback}("{token}");')
v2 Invisible:几乎总是需要回调
# Selenium — inject AND call the callback
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# CRITICAL: Invisible reCAPTCHA almost always requires calling the callback
callback_name = driver.find_element(
"css selector", ".g-recaptcha[data-size='invisible']"
).get_attribute("data-callback")
driver.execute_script(f'{callback_name}("{token}");')
// Puppeteer — invisible callback injection
await page.evaluate((tok) => {
// Set the hidden field
document.getElementById("g-recaptcha-response").value = tok;
// Find and call the callback function
const widget = document.querySelector("[data-size='invisible']");
const cbName = widget?.getAttribute("data-callback");
if (cbName && typeof window[cbName] === "function") {
window[cbName](tok);
}
}, token);
主要区别: v2 复选框表单通常仅适用于隐藏字段注入,因为用户单击复选框已经注册了回调。不可见的 reCAPTCHA 永远不会有这样的点击——必须显式调用回调。
适用于两种变体的通用求解器
import requests
import time
from bs4 import BeautifulSoup
class RecaptchaV2UniversalSolver:
def __init__(self, api_key):
self.api_key = api_key
def detect_and_solve(self, page_url, page_html=None):
if not page_html:
page_html = requests.get(page_url).text
soup = BeautifulSoup(page_html, "html.parser")
# Detect variant
invisible = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
widget = invisible or soup.find(class_="g-recaptcha")
if not widget:
raise Exception("No reCAPTCHA widget found")
sitekey = widget.get("data-sitekey")
is_invisible = invisible is not None
callback = widget.get("data-callback")
params = {
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url
}
if is_invisible:
params["invisible"] = 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 {
"token": result.text.split("|")[1],
"variant": "invisible" if is_invisible else "checkbox",
"callback": callback,
"sitekey": sitekey
}
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve error: {result.text}")
raise Exception("Timed out")
# Usage
solver = RecaptchaV2UniversalSolver("YOUR_API_KEY")
result = solver.detect_and_solve("https://staging.example.com/qa-login")
print(f"Variant: {result['variant']}, Callback: {result['callback']}")
故障排除
| 问题 | 复选框修复 | 隐形修复 |
|---|---|---|
| 令牌不被接受 | 注入g-recaptcha-response字段 |
注入并调用 data-callback 函数 |
| 未找到小部件 | 查找 .g-recaptcha 类 |
检查 data-size="invisible" 或脚本渲染调用 |
| 表单提交但失败 | 检查是否需要回调 | 回调几乎总是需要的——找到并调用它 |
| 注入后页面重新加载 | JavaScript 验证失败 | 在表单自动提交触发器之前调用回调 |
常问问题
隐形的 reCAPTCHA 更难解决吗?
不会。通过 CaptchaAI 的求解过程几乎相同。将 invisible=1 添加到您的请求中是唯一的更改。额外的挑战是在注入方面:您必须找到并执行回调函数。
两者都使用相同的站点密钥格式吗?
是的。站点密钥格式是相同的。您无法仅通过 sitekey 来区分变体 - 检查 HTML 中的 data-size="invisible"。
隐形 reCAPTCHA 是否仍会显示图像挑战?
是的。当 Google 检测到可疑行为时,不可见的 reCAPTCHA 会在页面右下角显示一个图像挑战弹出窗口。这就是为什么你仍然需要 CaptchaAI——隐形版本仍然会带来挑战。
网站可以在复选框和不可见之间切换吗?
是的。这是站点端的配置更改 - 相同的站点密钥,不同的实现。有些网站在桌面上使用复选框,但在移动设备上不可见。始终动态检测变体。
哪个解决速度更快?
隐形解决方案通常会稍快一些(10-25 秒 vs 10-30 秒),因为它们往往会触发更少的图像挑战。但差异很小。