Если на странице стоит форма входа с reCAPTCHA и рядом — подписка на рассылку со второй reCAPTCHA, скрипт с логикой «решил один виджет — жду результат» здесь не сработает: у каждого CAPTCHA-виджета свой sitekey, свой контейнер и свой callback, а токен, полученный для одного виджета, не проходит проверку в другом.
Порядок действий один и тот же независимо от количества виджетов на странице:
- найти все CAPTCHA и извлечь sitekey каждой;
- отправить их в CaptchaAI одним пакетом и решать параллельно, а не по очереди;
- подставить каждый полученный токен ровно в тот виджет, для которого он был запрошен, и вручную вызвать его
callback.
Как найти все CAPTCHA-виджеты на странице
Прежде чем отправлять что-либо в API, нужно понять, сколько виджетов реально есть на странице и какой sitekey у каждого. Для reCAPTCHA это обычно перебор iframe по атрибуту src, для остальных типов — атрибут data-sitekey на контейнере виджета.
Python и Selenium. Сканируем DOM после полной загрузки страницы и достаём sitekey регулярным выражением из src каждого iframe:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/multi-captcha-page")
# Find all reCAPTCHA iframes
captcha_iframes = driver.find_elements(
By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]'
)
# Extract sitekeys from each iframe's src
import re
captchas = []
for i, iframe in enumerate(captcha_iframes):
src = iframe.get_attribute("src")
match = re.search(r"k=([A-Za-z0-9_-]+)", src)
if match:
captchas.append({
"index": i,
"sitekey": match.group(1),
"iframe": iframe
})
print(f"Found {len(captchas)} CAPTCHAs on page")
for c in captchas:
print(f" [{c['index']}] sitekey: {c['sitekey']}")
Ожидаемый результат:
Found 2 CAPTCHAs on page
[0] sitekey: 6LcXyzABCDEF-login
[1] sitekey: 6LcAbcDEFGHI-signup
Важно: sitekey из первого найденного iframe нельзя переиспользовать для второго виджета «на всякий случай» — если владелец сайта выпустил разные ключи под логин и подписку, задача с чужим sitekey просто не решится на стороне CaptchaAI.
JavaScript и Puppeteer. Тот же перебор работает и через Puppeteer — только виджеты ищутся не по iframe, а по классу .g-recaptcha:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/multi-captcha-page', {
waitUntil: 'networkidle2'
});
// Extract all reCAPTCHA widgets from the page
const captchas = await page.evaluate(() => {
const widgets = document.querySelectorAll('.g-recaptcha');
return Array.from(widgets).map((el, i) => ({
index: i,
sitekey: el.getAttribute('data-sitekey'),
elementId: el.id || `captcha-${i}`,
callbackName: el.getAttribute('data-callback') || null
}));
});
console.log(`Found ${captchas.length} CAPTCHAs`);
captchas.forEach(c => console.log(` [${c.index}] ${c.sitekey}`));
})();
Параллельное решение всех найденных CAPTCHA
Отправьте все задачи в CaptchaAI одним пулом потоков, а затем опрашивайте res.php тоже параллельно — последовательная обработка здесь не даёт никакой выгоды:
- каждое решение занимает 15–30 секунд независимо от типа CAPTCHA, и это время не сокращается от того, что виджет один;
- пока один запрос ждёт ответа
res.php, поток простаивает — параллельный пул использует это время для второй, третьей и последующих задач.
Ниже — пример на Python с пулом потоков для отправки задач и отдельным пулом для опроса результата:
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = "YOUR_API_KEY"
PAGE_URL = "https://example.com/multi-captcha-page"
def submit_captcha(sitekey):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": PAGE_URL,
"json": "1"
})
result = resp.json()
if result["status"] != 1:
raise Exception(f"Submit error: {result['request']}")
return result["request"]
def poll_result(task_id, timeout=120):
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1"
})
result = resp.json()
if result["status"] == 1:
return result["request"]
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Poll error: {result['request']}")
raise TimeoutError(f"Task {task_id} timed out")
def solve_all(captchas):
# Submit all in parallel
task_ids = {}
with ThreadPoolExecutor(max_workers=len(captchas)) as pool:
futures = {
pool.submit(submit_captcha, c["sitekey"]): c["index"]
for c in captchas
}
for future in as_completed(futures):
idx = futures[future]
task_ids[idx] = future.result()
print(f"[captcha-{idx}] Submitted → task {task_ids[idx]}")
# Poll all in parallel
tokens = {}
with ThreadPoolExecutor(max_workers=len(task_ids)) as pool:
futures = {
pool.submit(poll_result, tid): idx
for idx, tid in task_ids.items()
}
for future in as_completed(futures):
idx = futures[future]
tokens[idx] = future.result()
print(f"[captcha-{idx}] Solved")
return tokens
# Example usage
captchas = [
{"index": 0, "sitekey": "6LcXyzABCDEF-login"},
{"index": 1, "sitekey": "6LcAbcDEFGHI-signup"}
]
tokens = solve_all(captchas)
Ожидаемый результат:
[captcha-0] Submitted → task 71823456
[captcha-1] Submitted → task 71823457
[captcha-1] Solved
[captcha-0] Solved
Внедрение токена в нужный виджет
У каждого виджета reCAPTCHA есть собственное скрытое поле g-recaptcha-response. Если виджетов несколько, это поле лежит внутри контейнера конкретного виджета — токен нужно подставлять именно туда, а не в первое попавшееся поле на странице.
Python и Selenium. Ищем контейнер виджета по индексу, затем внутри него — нужное поле:
def inject_tokens(driver, captchas, tokens):
for c in captchas:
idx = c["index"]
token = tokens[idx]
# Find the textarea within the widget's container
container = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")[idx]
textarea = container.find_element(
By.CSS_SELECTOR, 'textarea[name="g-recaptcha-response"]'
)
driver.execute_script(
"arguments[0].value = arguments[1];", textarea, token
)
# Trigger the callback if defined
callback = c.get("callback")
if callback:
driver.execute_script(f"{callback}('{token}');")
print(f"[captcha-{idx}] Token injected")
inject_tokens(driver, captchas, tokens)
JavaScript и Puppeteer. Та же логика через page.evaluate, только выборка виджета — по индексу в NodeList:
async function injectTokens(page, captchas, tokens) {
for (const captcha of captchas) {
const token = tokens[captcha.index];
await page.evaluate((idx, tkn, callbackName) => {
const widgets = document.querySelectorAll('.g-recaptcha');
const textarea = widgets[idx].querySelector(
'textarea[name="g-recaptcha-response"]'
);
textarea.value = tkn;
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](tkn);
}
}, captcha.index, token, captcha.callbackName);
console.log(`[captcha-${captcha.index}] Token injected`);
}
}
await injectTokens(page, captchas, tokens);
На этом шаге чаще всего теряют время: значение подставлено в
textarea, а форма всё ещё считает CAPTCHA нерешённой. Причина почти всегда одна — не вызванcallbackвиджета. Смотрите разбор ниже.
Типичные проблемы и как их устранить
Прежде чем переходить к смешанным типам CAPTCHA, разберите ошибки, которые всплывают уже на этапе одного типа виджетов — они же чаще всего бьют и в сценарии с несколькими CAPTCHA сразу:
- Токен подставлен, но форма всё равно заблокирована. Не сработал
callback— проверьте атрибутdata-callbackи вызовите функцию вручную с токеном. - Обнаруживается только первая CAPTCHA. Вторая подгружается лениво (lazy load) — дождитесь появления всех iframe и виджетов, прежде чем сканировать страницу.
- Токен попал не в тот виджет. Перепутаны индексы — сопоставляйте токены с sitekey, а не с позиционным индексом массива.
ERROR_WRONG_GOOGLEKEY. Sitekey извлечён неверно — сверьте значение изsrciframe или атрибутаdata-sitekeyнапрямую в DevTools.
Разные типы CAPTCHA на одной странице
Если на странице сочетаются reCAPTCHA и Cloudflare Turnstile — частый случай, когда старая форма логина осталась на reCAPTCHA, а новую защиту от ботов уже перевели на Turnstile, — распознавайте каждый тип отдельно и отправляйте с правильным параметром method:
def detect_all_captchas(driver):
detected = []
# reCAPTCHA
recaptchas = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")
for i, el in enumerate(recaptchas):
detected.append({
"type": "userrecaptcha",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"recaptcha-{i}"
})
# Turnstile
turnstiles = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
for i, el in enumerate(turnstiles):
detected.append({
"type": "turnstile",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"turnstile-{i}"
})
return detected
Для reCAPTCHA параметр method — userrecaptcha, для Turnstile — turnstile. Смешивать их в одном запросе нельзя: у каждого типа свой набор параметров API.
Часто задаваемые вопросы
Ниже — вопросы, которые чаще всего возникают уже после того, как базовый сценарий «найти → решить → подставить» реализован, но что-то в конкретном случае идёт не так.
Сколько потоков CaptchaAI нужно, чтобы решать несколько CAPTCHA на странице одновременно?
Один поток — это одна CAPTCHA, решаемая в моменте. Для двух виджетов на странице достаточно двух свободных потоков; план BASIC ($15/мес, 5 потоков) с запасом покрывает большинство форм с 2–3 CAPTCHA. Число решений на поток не ограничено, доплата за количество виджетов не предусмотрена.
Почему форма всё ещё блокируется, хотя токен reCAPTCHA уже подставлен в textarea?
Скорее всего не вызван callback. Подстановка значения в g-recaptcha-response сама по себе не запускает событие — сайт слушает вызов функции из атрибута data-callback. Найдите имя этой функции в разметке виджета и вызовите её явно с токеном, как показано выше.
Может ли каждый виджет на странице использовать собственный sitekey?
Да, и это норма, а не исключение — форма логина и форма подписки почти всегда получают разные sitekey от владельца сайта в консоли reCAPTCHA. Извлекайте sitekey отдельно для каждого виджета и никогда не полагайтесь на то, что он один на всю страницу.
Как отличить страницу с двумя CAPTCHA от одной CAPTCHA с двумя элементами интерфейса?
Смотрите на DOM, а не на визуальную разметку страницы:
- сколько независимых iframe с
recaptcha/api2/anchorвsrcприсутствует на странице; - сколько контейнеров
.g-recaptchaили.cf-turnstileнайдено селектором; - у скольких из них разный
data-sitekey.
Если таких элементов больше одного и sitekey отличается, перед вами разные задачи — их нужно решать и подставлять по отдельности, а не как один виджет с двумя подсказками.
Что делать, если вторая CAPTCHA появляется только после отправки первой формы?
Пересканируйте страницу после каждой отправки формы — новый виджет мог отрисоваться только что. Используйте WebDriverWait в Selenium или page.waitForSelector в Puppeteer, чтобы дождаться появления нового iframe или контейнера, а не сканировать DOM вслепую по таймеру.
Решайте любое количество CAPTCHA на странице с одним API-ключом CaptchaAI
Оформите API-ключ на captchaai.com и подключите его к пайплайну обнаружения и решения из этого руководства. Один ключ и один тарифный план по потокам покрывают форму с одной CAPTCHA так же, как страницу с пятью виджетами разных типов — доплата за количество CAPTCHA на странице не предусмотрена.