Если на странице подключены и reCAPTCHA v2, и v3, решать нужно не обе сразу: сначала v3, и только при низком score — переключаться на v2. v3 работает в фоне и молча выставляет оценку риска, а v2 всплывает как чек-бокс, когда оценка не дотягивает до порога сайта. Для автоматизации это два набора ключей и логика ветвления, которую нужно повторить самостоятельно.
Зачем сайты совмещают v2 и v3
User visits page
↓
reCAPTCHA v3 runs invisibly in background
↓
Score returned to server (e.g., 0.4)
↓
Score below threshold (e.g., < 0.7)?
├─ YES → Show reCAPTCHA v2 checkbox/image challenge
└─ NO → Allow action without visible CAPTCHA
Такая связка даёт сайту лучшее из обоих подходов:
| Сценарий | Что происходит |
|---|---|
| Высокий score v3 | CAPTCHA не показывается — минимум трения при входе или регистрации |
| Низкий score v3 | Показывается задача v2 — резервный уровень защиты |
| Порог перехода | Задаёт владелец сайта — он же решает, что считать «подозрительным» |
Как выглядит связка v2 + v3 в коде
Вариант 1: сначала v3, затем v2 при низком score
<!-- Both scripts loaded -->
<script src="https://www.google.com/recaptcha/api.js?render=V3_SITE_KEY"></script>
<script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>
<form id="loginForm">
<!-- v2 widget (hidden initially) -->
<div id="recaptcha-v2-container" style="display:none;">
<div class="g-recaptcha" data-sitekey="V2_SITE_KEY"></div>
</div>
<button type="submit">Login</button>
</form>
<script>
// First attempt: v3 invisible
grecaptcha.ready(function() {
grecaptcha.execute('V3_SITE_KEY', {action: 'login'}).then(function(v3Token) {
fetch('/api/verify-v3', {
method: 'POST',
body: JSON.stringify({token: v3Token})
})
.then(r => r.json())
.then(data => {
if (data.score < 0.7) {
// Score too low → show v2 fallback
document.getElementById('recaptcha-v2-container').style.display = 'block';
grecaptcha.render('recaptcha-v2-container', {sitekey: 'V2_SITE_KEY'});
} else {
// Score OK → submit form directly
document.getElementById('loginForm').submit();
}
});
});
});
</script>
Если ответ приходит с задержкой
Разводите таймаут /api/verify-v3 и таймаут решения токена в CaptchaAI — иначе виджет v2 может отрисоваться дважды при повторной отправке формы.
Вариант 2: разные ключи сайта под разные действия
Homepage → v3 only (passive score)
Login page → v3 assessment, v2 fallback
Checkout → v2 always (high security)
Contact form → v3 only
Вариант 3: один скрипт на оба режима
<script src="https://www.google.com/recaptcha/api.js?render=V3_SITE_KEY"></script>
<script>
// v3 execute
grecaptcha.execute('V3_SITE_KEY', {action: 'login'});
// v2 render (uses a different site key)
grecaptcha.render('v2-container', {sitekey: 'V2_SITE_KEY'});
</script>
Как найти на странице оба ключа — v2 и v3
Прежде чем писать логику решения, вытащите со страницы оба site key: у v3 и у v2 они разные.
Определение на Python
import requests
import re
def detect_dual_recaptcha(url):
"""Detect if a page uses both reCAPTCHA v2 and v3."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
}
html = requests.get(url, headers=headers, timeout=15).text
result = {
"has_v3": False,
"has_v2": False,
"v3_site_key": None,
"v2_site_key": None,
"dual": False,
"pattern": None,
}
# Detect v3 (render parameter or enterprise.execute)
v3_match = re.search(r"api\.js\?render=([A-Za-z0-9_-]+)", html)
if v3_match and v3_match.group(1) != "explicit":
result["has_v3"] = True
result["v3_site_key"] = v3_match.group(1)
# Detect v3 in execute calls
v3_execute = re.search(
r"grecaptcha\.(?:enterprise\.)?execute\s*\(\s*['\"]([^'\"]+)['\"]",
html,
)
if v3_execute:
result["has_v3"] = True
if not result["v3_site_key"]:
result["v3_site_key"] = v3_execute.group(1)
# Detect v2 (g-recaptcha class or explicit render)
v2_match = re.search(r'data-sitekey="([^"]+)"', html)
if v2_match:
key = v2_match.group(1)
if key != result.get("v3_site_key"):
result["has_v2"] = True
result["v2_site_key"] = key
# Check for explicit v2 render
v2_render = re.search(
r"grecaptcha\.render\s*\([^,]+,\s*\{[^}]*sitekey:\s*['\"]([^'\"]+)",
html,
)
if v2_render:
result["has_v2"] = True
if not result["v2_site_key"]:
result["v2_site_key"] = v2_render.group(1)
result["dual"] = result["has_v3"] and result["has_v2"]
if result["dual"]:
# Determine pattern
if "display:none" in html or "display: none" in html:
result["pattern"] = "v3_pre_assessment_v2_fallback"
else:
result["pattern"] = "v2_v3_simultaneous"
return result
detection = detect_dual_recaptcha("https://staging.example.com/qa-login")
print(detection)
Определение на Node.js
const axios = require("axios");
async function detectDualRecaptcha(url) {
const { data: html } = await axios.get(url, { timeout: 15000 });
const result = {
hasV3: false,
hasV2: false,
v3SiteKey: null,
v2SiteKey: null,
dual: false,
};
// v3 detection
const v3Match = html.match(/api\.js\?render=([A-Za-z0-9_-]+)/);
if (v3Match && v3Match[1] !== "explicit") {
result.hasV3 = true;
result.v3SiteKey = v3Match[1];
}
// v2 detection
const v2Match = html.match(/data-sitekey="([^"]+)"/);
if (v2Match && v2Match[1] !== result.v3SiteKey) {
result.hasV2 = true;
result.v2SiteKey = v2Match[1];
}
result.dual = result.hasV3 && result.hasV2;
return result;
}
detectDualRecaptcha("https://staging.example.com/qa-login").then(console.log);
Стратегии решения v2 и v3 через API CaptchaAI
- Сначала v3, v2 — только при фолбэке (самая точная).
- Пропустить v3, сразу решать v2 (самая экономная).
- Определять сценарий через браузер (сложная логика).
Стратегия 1: сначала v3, v2 — только при срабатывании фолбэка
Такой порядок точнее всего повторяет логику самого сайта:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_v3(site_key, page_url, action="login"):
"""Solve reCAPTCHA v3 and return token."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"version": "v3",
"action": action,
"json": 1,
}).json()
task_id = submit["request"]
for _ in range(60):
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.get("status") == 1:
return result["request"]
raise TimeoutError("v3 solve timeout")
def solve_v2(site_key, page_url):
"""Solve reCAPTCHA v2 and return token."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1,
}).json()
task_id = submit["request"]
for _ in range(60):
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.get("status") == 1:
return result["request"]
raise TimeoutError("v2 solve timeout")
def solve_dual_recaptcha(v3_key, v2_key, page_url, action="login"):
"""Handle dual reCAPTCHA: try v3, fall back to v2."""
# Step 1: Try v3
v3_token = solve_v3(v3_key, page_url, action)
# Step 2: Submit v3 token to target
response = requests.post(f"{page_url}/verify", data={
"g-recaptcha-response": v3_token,
})
# Step 3: Check if v2 fallback is needed
if "recaptcha" in response.text.lower() and v2_key:
print("v3 score too low — v2 fallback triggered")
v2_token = solve_v2(v2_key, page_url)
return {"version": "v2", "token": v2_token}
return {"version": "v3", "token": v3_token}
result = solve_dual_recaptcha(
v3_key="6LcExample_v3_key",
v2_key="6LcExample_v2_key",
page_url="https://staging.example.com/qa-login",
)
print(f"Solved with {result['version']}")
Стратегия 2: пропустить v3 и сразу решать v2
Если по опыту вы знаете, что для автоматизированного трафика сайт всегда показывает v2 (а score v3 стабильно ниже порога), решение v3 можно вообще не запускать:
# If you consistently fail v3 assessment, just solve v2 directly
token = solve_v2(v2_site_key, page_url)
submit_form(token)
Так вы не платите за решение v3, которое всё равно не пройдёт порог, и не тратите лишний раунд запрос-ответ.
Стратегия 3: определять сценарий через браузер
Для сайтов со сложной логикой удобнее не гадать по HTML заранее, а проверить прямо в браузере, какой виджет реально показан:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")
time.sleep(3)
# Check if v2 widget is visible
v2_visible = driver.execute_script("""
const container = document.querySelector('.g-recaptcha');
if (!container) return false;
const style = window.getComputedStyle(container.parentElement);
return style.display !== 'none' && style.visibility !== 'hidden';
""")
if v2_visible:
# v2 is showing — solve v2
sitekey = driver.find_element(
By.CSS_SELECTOR, "[data-sitekey]"
).get_attribute("data-sitekey")
token = solve_v2(sitekey, driver.current_url)
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
else:
# v3 only — solve v3
# Extract v3 key from page source
v3_key = driver.execute_script(
"return document.querySelector('script[src*=\"render=\"]')"
".src.match(/render=([^&]+)/)[1];"
)
token = solve_v3(v3_key, driver.current_url)
# Inject v3 token into the form
driver.execute_script(f"""
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'g-recaptcha-response';
input.value = '{token}';
document.querySelector('form').appendChild(input);
""")
driver.find_element(By.CSS_SELECTOR, "form").submit()
Пример: QA-стенд с двойной reCAPTCHA
С этим чаще всего сталкивается команда, гоняющая регресс-тесты логина на стейджинге: репутация IP у v3 проседает от повторов, и после 20–30 попыток подряд всплывает v2. Для такого объёма хватает плана BASIC ($15/мес, 5 потоков).
Если форма собирает email или телефон, держите в уме 152-ФЗ: стенд должен работать только с данными, которые вы вправе обрабатывать.
Особые случаи, которые ломают наивную логику
- Два разных site key — легко перепутать.
- Enterprise-скоринг v3 с обычным v2 для задачи.
- Несколько форм — у каждой свой экземпляр.
Два site key на одной странице
| Ключ | Где искать |
|---|---|
| v3 | В URL скрипта ?render=KEY и в вызове grecaptcha.execute('KEY', ...) |
| v2 | В атрибуте data-sitekey="KEY" на виджете |
Перепутаете ключи местами — получите невалидный токен.
reCAPTCHA Enterprise: скоринг v3, задача — обычный v2
# Detect and handle Enterprise + v2 combo
if "recaptcha/enterprise.js" in html:
# Use enterprise parameter for v3
v3_params = {"enterprise": 1, "version": "v3"}
else:
v3_params = {"version": "v3"}
Несколько форм на одной странице
Если на странице несколько форм — например, логин и регистрация, — у каждой обычно свой экземпляр reCAPTCHA. Вытаскивайте site key из конкретной формы, с которой работаете, а не первый попавшийся на странице:
# Target the login form specifically
login_form = soup.find("form", id="login-form")
widget = login_form.find(attrs={"data-sitekey": True})
sitekey = widget["data-sitekey"]
Частые вопросы про reCAPTCHA v2 и v3
Нужно ли решать и v2, и v3 на одной странице?
Обычно нет. Сначала автоматически срабатывает v3; если его score проходит порог сайта, задача v2 не появляется вовсе. Решать v2 нужно только тогда, когда фолбэк уже сработал.
Как отличить, что сработал фолбэк v2, от того, что сломалась интеграция?
Смотрите на ответ сервера сразу после отправки токена v3. HTML виджета v2 или редирект на шаг с CAPTCHA — это штатный фолбэк. Пустой ответ, код 500 или редирект не на форму — ищите проблему в самой интеграции, например в устаревшем site key.
Можно ли повлиять на порог, при котором вместо v3 показывается v2?
Нет, порог задаёт владелец сайта. Со стороны автоматизации остаётся только закладывать в сценарий, что фолбэк v2 способен сработать, и не считать это ошибкой.
Что делать, если v2 всплывает через раз при одинаковых условиях?
Это нормально: score v3 пересчитывается для каждой сессии заново и зависит от истории IP и поведения браузера — сигналов, которые вы не контролируете напрямую.
Закладывайте в логику оба исхода, а не только «счастливый путь» без v2.
Какой site key использовать для v3, а какой для v2?
| Версия | Site key |
|---|---|
| v3 | api.js?render=V3_KEY в URL скрипта |
| v2 | data-sitekey="V2_KEY" в HTML виджета |
Это всегда два разных значения, даже если оба виджета встроены в одну форму.
Коротко о главном
Двойная reCAPTCHA — это v3 для незаметной оценки и v2 как запасной уровень при низком score. Обнаружить связку помогают два признака: render= в URL скрипта (v3) и data-sitekey на виджете (v2). Рабочая стратегия — решать v3 первым через CaptchaAI и переходить к v2, только если сайт запросил фолбэк. У каждой версии свой ключ и отдельный вызов API.
Похожие статьи
- reCAPTCHA на одностраничных приложениях: динамическая подгрузка
- Как обработать callback reCAPTCHA v2 через API
- Cloudflare Turnstile и reCAPTCHA v2 на одном сайте