Некоторые веб-сайты реализуют reCAPTCHA v2 с функцией обратного вызова вместо стандартного скрытого поля g-recaptcha-response. Когда вы решаете CAPTCHA и вводите токен в скрытое поле, ничего не происходит — страница его игнорирует. Это связано с тем, что сайт ожидает, что вы вместо этого вызовете функцию JavaScript с помощью токена.
В этом руководстве показано, как обнаружить reCAPTCHA v2 на основе обратного вызова, решить проблему с помощью API CaptchaAI и правильно вызвать обратный вызов. Вызов API идентичен стандартному reCAPTCHA v2 — меняется только шаг передача токена во внутренний QA endpoint.
Новичок в reCAPTCHA v2? Начните сКак решить reCAPTCHA v2 с помощью APIдля стандартного потока, затем вернитесь сюда для варианта обратного вызова.
Что вам нужно, прежде чем начать
| Требование | Подробности |
|---|---|
| CaptchaAI API-ключ | Получите один изcaptchaai.com/api.php. Строка из 32 символов. |
| URL целевой страницы | Полный URL-адрес, по которому загружается виджет reCAPTCHA v2. |
| reCAPTCHA v2 ключ сайта | Открытый ключ, привязанный к экземпляру виджета. |
| Инструмент автоматизации браузера | Selenium, Puppeteer или Playwright — для вызова обратного вызова требуется выполнение JavaScript. |
| Название функции обратного вызова | Функция JavaScript, которую сайт ожидает получить токен. |
Как определить реализацию обратного вызова
Стандарт reCAPTCHA v2 записывает решенный токен в скрытую текстовую область g-recaptcha-response. Реализации обратного вызова пропускают это и вызывают функцию JavaScript напрямую. Вот как определить разницу.
Способ 1. Проверьте атрибут data-callback.
Проверьте div виджета reCAPTCHA в исходном коде страницы:
<div class="g-recaptcha"
data-sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
data-callback="SubmitToken">
</div>
Если data-callback существует, сайт использует обратный вызов. Значение (SubmitToken) — это нужное вам имя функции.
Способ 2. Проверьте вызовы grecaptcha.render().
Найдите в JavaScript страницы grecaptcha.render:
grecaptcha.render('recaptcha-container', {
sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
callback: userVerified
});
Свойство callback называет функцию. В данном случае userVerified.
Способ 3. Проверьте внутреннюю конфигурацию reCAPTCHA.
Откройте консоль браузера на целевой странице и запустите:
___grecaptcha_cfg.clients[0]
Перейдите по дереву объектов, чтобы найти свойство callback. Точный путь зависит от сайта — это может быть clients[0].aa.l.callback или что-то еще, в зависимости от версии reCAPTCHA и минимизации. Если на странице есть несколько экземпляров reCAPTCHA, проверьте clients[1], clients[2] и т. д.
Скрипт быстрого обнаружения
Запустите это в консоли браузера, чтобы автоматически найти имена обратных вызовов:
// Check data-callback attributes
document.querySelectorAll('[data-callback]').forEach(el => {
console.log('data-callback:', el.getAttribute('data-callback'));
});
// Check internal config
if (typeof ___grecaptcha_cfg !== 'undefined') {
Object.keys(___grecaptcha_cfg.clients).forEach(key => {
const client = ___grecaptcha_cfg.clients[key];
console.log(`Client ${key}:`, JSON.stringify(client, null, 2));
});
}
Чем решение обратного вызова отличается от стандартного v2
Вызов API для CaptchaAI идентичен. Единственная разница заключается в том, что вы делаете с токеном после его получения.
| Шаг | Стандарт v2 | Обратный звонок v2 |
|---|---|---|
| 1. Отправьте заявку на CaptchaAI | method=userrecaptcha + ключ сайта + адрес страницы |
Такой же |
| 2. Опрос на результат | action=get + идентификатор капчи |
Такой же |
| 3. Получить токен | Тот же формат токена | Такой же |
| 4. Внедрить токен | Установить значение поля g-recaptcha-response |
Вызов функции обратного вызова с помощью токена |
| 5. Отправить форму | Триггерная форма отправки | Обычно автоматически — этим занимается обратный вызов |
Важно: Не устанавливайте
g-recaptcha-responseв реализациях на основе обратного вызова. Страница игнорирует это поле и ожидает срабатывания функции обратного вызова. Установка поля без вызова обратного вызова приведет к тому, что CAPTCHA никогда не была решена.
Решение потока
Page → extract sitekey + pageurl + callback name
↓
POST to in.php (method=userrecaptcha)
↓
receive captcha ID
↓
wait 15–20 seconds
↓
GET res.php (action=get, id=…)
↓ ↓
CAPCHA_NOT_READY status=1 → token
(wait 5s, retry) ↓
invoke callback(token)
↓
site processes token automatically
Реализация Python (Selenium)
import time
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
PAGE_URL = "https://https://staging.example.com/qa-login"
CALLBACK_NAME = "SubmitToken" # The callback function name from the page
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def solve_recaptcha_v2(api_key, sitekey, pageurl):
"""Submit a reCAPTCHA v2 task and return the solved token."""
# Step 1: Submit the captcha
submit_resp = requests.post(
SUBMIT_URL,
data={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1,
},
timeout=30,
)
submit_resp.raise_for_status()
submit_data = submit_resp.json()
if submit_data.get("status") != 1:
raise RuntimeError(f"Submit failed: {submit_data}")
captcha_id = submit_data["request"]
print(f"Task created — captcha ID: {captcha_id}")
# Step 2: Wait before first poll
time.sleep(15)
# Step 3: Poll for result
for _ in range(60):
result_resp = requests.get(
RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
timeout=30,
)
result_resp.raise_for_status()
result_data = result_resp.json()
if result_data.get("request") == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result_data.get("status") == 1:
return result_data["request"]
raise RuntimeError(f"Polling error: {result_data}")
raise TimeoutError("reCAPTCHA v2 solve timed out")
def detect_callback_name(driver):
"""Detect the reCAPTCHA callback function name from the page."""
# Try data-callback attribute first
callback = driver.execute_script("""
const el = document.querySelector('[data-callback]');
if (el) return el.getAttribute('data-callback');
return null;
""")
if callback:
return callback
# Try internal reCAPTCHA config
callback = driver.execute_script("""
if (typeof ___grecaptcha_cfg === 'undefined') return null;
const clients = ___grecaptcha_cfg.clients;
for (const key of Object.keys(clients)) {
const client = clients[key];
// Walk the object tree to find a callback function
const json = JSON.stringify(client);
const match = json.match(/"callback":"(\\w+)"/);
if (match) return match[1];
}
return null;
""")
return callback
# Main workflow
driver = webdriver.Chrome()
driver.get(PAGE_URL)
# Detect the callback name (or use the known name)
detected = detect_callback_name(driver)
callback_name = detected or CALLBACK_NAME
print(f"Using callback: {callback_name}")
# Solve the CAPTCHA
token = solve_recaptcha_v2(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")
# Invoke the callback with the token
driver.execute_script(f"{callback_name}(arguments[0]);", token)
print("Callback invoked — site should process the token automatically")
# Wait for the page to process
time.sleep(3)
driver.quit()
Что это делает:
- Отправляет ключ сайта и URL-адрес страницы в
in.phpс помощьюmethod=userrecaptcha— идентично стандарту v2. - Опрашивает
res.phpкаждые 5 секунд, пока токен не будет готов. - Обнаруживает имя функции обратного вызова из DOM страницы.
- Вызывает функцию обратного вызова с решенным токеном, используя
execute_script. - Все остальное — отправку формы, проверку или перенаправление страницы обрабатывает собственный JavaScript сайта.
Реализация Node.js (Puppeteer)
const puppeteer = require("puppeteer");
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
const PAGE_URL = "https://https://staging.example.com/qa-login";
const CALLBACK_NAME = "SubmitToken";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveRecaptchaV2(apiKey, sitekey, pageurl) {
// Step 1: Submit the captcha
const submitResp = await fetch(SUBMIT_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
key: apiKey,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
json: "1",
}),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) {
throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
}
const captchaId = submitData.request;
console.log(`Task created — captcha ID: ${captchaId}`);
// Step 2: Wait before first poll
await sleep(15_000);
// Step 3: Poll for result
for (let i = 0; i < 60; i++) {
const resultResp = await fetch(
`${RESULT_URL}?${new URLSearchParams({
key: apiKey,
action: "get",
id: captchaId,
json: "1",
})}`
);
const resultData = await resultResp.json();
if (resultData.request === "CAPCHA_NOT_READY") {
await sleep(5_000);
continue;
}
if (resultData.status === 1) {
return resultData.request;
}
throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
}
throw new Error("reCAPTCHA v2 solve timed out");
}
async function detectCallbackName(page) {
return page.evaluate(() => {
// Try data-callback attribute
const el = document.querySelector("[data-callback]");
if (el) return el.getAttribute("data-callback");
// Try internal config
if (typeof ___grecaptcha_cfg !== "undefined") {
const clients = ___grecaptcha_cfg.clients;
for (const key of Object.keys(clients)) {
const json = JSON.stringify(clients[key]);
const match = json.match(/"callback":"(\w+)"/);
if (match) return match[1];
}
}
return null;
});
}
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto(PAGE_URL, { waitUntil: "networkidle2" });
// Detect callback
const detected = await detectCallbackName(page);
const callbackName = detected || CALLBACK_NAME;
console.log(`Using callback: ${callbackName}`);
// Solve the CAPTCHA
const token = await solveRecaptchaV2(API_KEY, SITEKEY, PAGE_URL);
console.log(`Solved token: ${token.slice(0, 80)}...`);
// Invoke the callback
await page.evaluate(
(name, tkn) => {
window[name](tkn);
},
callbackName,
token
);
console.log("Callback invoked — site should process the token automatically");
await sleep(3_000);
await browser.close();
})();
PHP-реализация
Вызов API в PHP такой же. Для вызова обратного вызова требуется контекст браузера, поэтому в этом примере рассматривается решение на стороне сервера. Для этапа внедрения используйте автономный браузер (например, PHP WebDriver).
<?php
$apiKey = "YOUR_CAPTCHAAI_API_KEY";
$sitekey = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
$pageurl = "https://https://staging.example.com/qa-login";
// Step 1: Submit
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
"key" => $apiKey,
"method" => "userrecaptcha",
"googlekey" => $sitekey,
"pageurl" => $pageurl,
"json" => 1,
]));
$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
die("Submit failed: " . $submit);
}
$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";
// Step 2: Wait and poll
sleep(15);
for ($i = 0; $i < 60; $i++) {
$result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
"key" => $apiKey,
"action" => "get",
"id" => $captchaId,
"json" => 1,
]));
$resultData = json_decode($result, true);
if ($resultData["request"] === "CAPCHA_NOT_READY") {
sleep(5);
continue;
}
if ($resultData["status"] === 1) {
$token = $resultData["request"];
echo "Solved token: " . substr($token, 0, 80) . "...\n";
// Pass $token to your browser automation to invoke the callback
break;
}
die("Polling error: " . $result);
}
После получения токена в PHP используйте инструмент автоматизации браузера (например, php-webdriver), чтобы выполнить:
SubmitToken("TOKEN_FROM_CAPTCHAAI");
Распространенные ошибки
| # | Ошибка | Что происходит | Исправить |
|---|---|---|---|
| 1 | Установка g-recaptcha-response вместо вызова обратного вызова |
Страница игнорирует токен — форма никогда не отправляется | Найдите имя обратного вызова и вызовите его с помощью токена. |
| 2 | Неправильное имя функции обратного вызова | Ошибка JavaScript: функция не определена | Перепроверьте data-callback, grecaptcha.render() или внутреннюю конфигурацию. |
| 3 | Обратный вызов выполняется по другому индексу клиента | Неправильный экземпляр reCAPTCHA, нацеленный на страницы с несколькими виджетами | Проверьте ___grecaptcha_cfg.clients[1], clients[2] и т. д. |
| 4 | Вызов обратного вызова до того, как страница будет готова | Функция еще не определена в контексте страницы | Дождитесь DOMContentLoaded или networkidle перед вызовом. |
| 5 | Использование запутанного имени /minified | Имя обратного вызова в исходном коде искажено | Используйте консоль браузера во время выполнения, чтобы найти фактическую ссылку на функцию. |
| 6 | Совмещение обратного вызова версии 2 с невидимой версией 2 | Некоторые невидимые реализации также используют обратные вызовы. | Проверьте, присутствует ли data-size="invisible" — если да, см.Как решить reCAPTCHA Invisible с помощью API |
Поиск неисправностей
Токен решен, но страница не реагирует
Самая распространенная причина: вы устанавливаете g-recaptcha-response вместо вызова обратного вызова. Проверьте, есть ли у виджета data-callback или callback в grecaptcha.render(). Если это так, вы должны вызвать эту функцию.
ReferenceError: SubmitToken is not defined
Функция обратного вызова еще не загружена или имя неверно. Пытаться:
- Подтвердите имя, проверив
data-callbackили внутреннюю конфигурацию. - Прежде чем вызывать, дождитесь полной загрузки страницы.
- На минифицированных сайтах функцию можно присвоить переменной — отметьте в консоли
window.SubmitToken.
Токен работает на стандартной версии v2, но не работает на этой странице.
Вероятно, вы столкнулись с реализацией обратного вызова. Следуйте шагам обнаружения выше для подтверждения, затем переключитесь на вызов обратного вызова.
ERROR_BAD_TOKEN_OR_PAGEURL
Пара sitekey/pageurl недействительна. Это ошибка API, не связанная с обратным вызовом и стандартом. Повторно извлеките оба значения со страницы.
На странице имеется несколько виджетов reCAPTCHA.
Каждый виджет может иметь свой собственный обратный вызов. Проверьте каждый элемент div g-recaptcha или проверьте ___grecaptcha_cfg.clients на наличие всех зарегистрированных экземпляров. Сопоставьте виджет с формой, на которую вы ориентируетесь.
ERROR_CAPTCHA_UNSOLVABLE
Задача не могла быть решена. Повторите попытку с новым запросом. Это не зависит от обратного вызова.
Полную информацию об ошибках см.Распространенные ошибки решения reCAPTCHA v2.
Почему CaptchaAI подходит для этого
| Фактор | Деталь |
|---|---|
| Тот же вызов API | Поток submit/poll идентичен стандартному reCAPTCHA v2 — дополнительные параметры не требуются. |
| Успех | 99,5%+ для reCAPTCHA v2 (обратный вызов и стандартное использование одного и того же решателя) |
| Определение скорости | Менее 60 секунд |
| Совместимость токенов | Возвращенный токен работает как с внедрением g-recaptcha-response, так и с вызовом обратного вызова. |
| Цены | Планы на основе потоков от $15/month для неограниченного количества решений |
Токен CaptchaAI возвращается один и тот же независимо от того, как сайт реализует reCAPTCHA v2. Разница полностью заключается в вашем клиентском коде — в том, как вы доставляете токен на страницу.
Полный работоспособный пример
Нужен полноценный рабочий проект с настройкой среды, опросом, повторными попытками и обработкой ошибок?
Полный работоспособный пример см. на GitHub →.
Часто задаваемые вопросы
Что такое обратный вызов reCAPTCHA v2?
Обратный вызов — это функция JavaScript, которую веб-сайт регистрирует для получения решенного токена reCAPTCHA. Вместо записи токена в скрытое поле g-recaptcha-response, reCAPTCHA напрямую вызывает функцию. Функция обычно запускает отправку формы, проверку или перенаправление страницы.
Чем обратный вызов отличается от стандартного reCAPTCHA v2 для вызова API?
Это совсем не другое. Вы отправляете тот же запрос method=userrecaptcha с тем же ключом сайта и URL-адресом страницы. Единственная разница заключается в том, что вы делаете с токеном после его получения — вы вызываете функцию обратного вызова вместо установки значения поля.
Как узнать имя функции обратного вызова?
Три места для проверки: (1) атрибут data-callback в div reCAPTCHA, (2) свойство callback в вызове grecaptcha.render() на странице JavaScript, (3) объект ___grecaptcha_cfg.clients[0] в консоли браузера — перемещайтесь по дереву, чтобы найти свойство callback.
Могу ли я использовать токен обоими методами?
Сам токен работает в любом случае. Но если сайт ожидает обратного вызова, установка g-recaptcha-response будет игнорироваться. Всегда сопоставляйте метод внедрения с тем, что ожидает сайт.
Нужен ли мне браузер для вызова обратного вызова?
Да. Обратный вызов — это функция JavaScript в контексте страницы. Вам понадобится Selenium, Puppeteer, Playwright или аналогичный инструмент, который может запускать JavaScript на целевой странице.
Начните решать обратный вызов reCAPTCHA v2
- Получите ключ API —captchaai.com/api.php
- Определить имя обратного вызова — проверьте
data-callback,grecaptcha.render()или внутреннюю конфигурацию. - Скопируйте приведенный выше код Python или Node.js — замените заполнители своим ключом, ключом сайта, URL-адресом страницы и именем обратного вызова.
- Запустите — токен поступает менее чем через 60 секунд, срабатывает обратный вызов, и страница обрабатывает результат.
- Застряли? Начните сРаспространенные ошибки решения reCAPTCHA v2или прочитать полностьюДокументация по API CaptchaAI
Похожие статьи
- Решите Recaptcha Invisible Python
- Cloudflare Turnstile Recaptcha V2. Обработка на той же площадке.
- Подробное описание Recaptcha Enterprise Assessment API