Tutorials

Обработка нескольких CAPTCHA на одной странице

Если на странице стоит форма входа с 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 извлечён неверно — сверьте значение из src iframe или атрибута 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 параметр methoduserrecaptcha, для 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 на странице не предусмотрена.


Похожие руководства по CaptchaAI API

Комментарии для этой статьи отключены.