Explainers

Как обрабатывать reCAPTCHA v2 в рабочих процессах веб-скрапинга

Когда парсер натыкается на reCAPTCHA v2, эмулировать клик по чекбоксу или сетке изображений — не лучший план: куда быстрее извлечь sitekey и адрес страницы и отправить их в API решателя. В ответ приходит готовый токен, вы подставляете его в форму — и процесс продолжается за один HTTP-вызов, без ручного взаимодействия с виджетом CaptchaAI.

В этом руководстве — рабочий код для Python (Selenium + requests) и Node.js (Puppeteer), а также HTTP-only вариант для сайтов, которые принимают токен напрямую в форме.


Быстрый старт: парсинг через reCAPTCHA v2

  1. Получите API-ключ на captchaai.com/api.php.
  2. Извлеките sitekey (googlekey) с целевой страницы.
  3. Возьмите код примера ниже — Python или Node.js, в зависимости от стека, — и подставьте свой API-ключ.
  4. Увеличивайте число потоков тарифа по мере роста объёма парсинга.

Как устроен процесс

У каждого виджета reCAPTCHA v2 есть два параметра, которые нужны парсеру:

Параметр Что это Где взять
googlekey публичный sitekey виджета атрибут data-sitekey в HTML страницы
pageurl адрес страницы, на которой показывается CAPTCHA текущий URL — driver.current_url в Selenium или page.url() в Puppeteer

Парсер отправляет эти значения в API CaptchaAI, дожидается решённого токена и вставляет его в поле g-recaptcha-response страницы (либо вызывает функцию, указанную в data-callback). Бэкенд целевого сайта проверяет токен у Google и пропускает запрос дальше.

Если сайт использует enterprise-версию виджета, добавьте параметр enterprise=1 к запросу CaptchaAI — подробный разбор в статье Как решить reCAPTCHA v2 Enterprise через API.

Совет: держите значение enterprise фиксированным для сайта — переключение между обычным и enterprise-виджетом на одном домене встречается редко, а лишний флаг только замедляет решение.


Пример: мониторинг цен на бронировании и маркетплейсах

Типичный сценарий для такой связки — агентство или небольшая команда, которая парсит сайты бронирования отелей, авиабилетов или маркетплейсы, чтобы раз в несколько часов сверять цены конкурентов. reCAPTCHA v2 обычно всплывает на странице поиска или на шаге оформления заказа, и без решателя парсер просто зависает на виджете, а не падает с ошибкой — это легко спутать с багом в коде.

Что стоит учитывать при таком парсинге:

  • держите прокси ближе к региону целевого сайта — задержка ниже, а поведение сессии больше похоже на обычного посетителя из этого региона;
  • запросы к in.php/res.php идут через собственную инфраструктуру CaptchaAI, а не через ваш прокси-пул — эти два канала между собой не связаны;
  • собирайте только те данные, которые вы вправе обрабатывать: публичные цены и наличие мест, без персональных данных других пользователей — требования 152-ФЗ «О персональных данных» и GDPR действуют для трансграничных команд одинаково.

Python: Selenium + CaptchaAI

import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

# Step 1: Open the page with Selenium
driver = webdriver.Chrome()
driver.get("https://example.com/protected-page")

# Step 2: Extract the sitekey
sitekey = driver.find_element(By.CSS_SELECTOR, ".g-recaptcha").get_attribute("data-sitekey")
page_url = driver.current_url

# Step 3: Submit to CaptchaAI
response = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "json": 1
}).json()

task_id = response["request"]

# Step 4: Poll for result
token = None
for _ in range(40):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY",
        "action": "get",
        "id": task_id,
        "json": 1
    }).json()

    if result.get("status") == 1:
        token = result["request"]
        break
    if result.get("request") != "CAPCHA_NOT_READY":
        raise RuntimeError(f"Solve failed: {result['request']}")

# Step 5: Inject the token and submit
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").innerHTML = "{token}";'
)

# Check for callback
callback = driver.execute_script(
    'var el = document.querySelector(".g-recaptcha"); '
    'return el ? el.getAttribute("data-callback") : null;'
)
if callback:
    driver.execute_script(f'{callback}("{token}");')
else:
    driver.find_element(By.CSS_SELECTOR, "form").submit()

# Step 6: Scrape the data
print(driver.page_source[:500])
driver.quit()

Node.js: Puppeteer + CaptchaAI

const puppeteer = require("puppeteer");

async function scrapeWithCaptcha(url) {
  const browser = await puppeteer.launch({ headless: "new" });
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: "networkidle2" });

  // Extract sitekey
  const sitekey = await page.$eval(".g-recaptcha", (el) => el.dataset.sitekey);

  // Submit to CaptchaAI
  const submitRes = await fetch(
    `https://ocr.captchaai.com/in.php?${new URLSearchParams({
      key: "YOUR_API_KEY",
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: url,
      json: 1,
    })}`
  );
  const { request: taskId } = await submitRes.json();

  // Poll for result
  let token;
  for (let i = 0; i < 40; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const res = await fetch(
      `https://ocr.captchaai.com/res.php?${new URLSearchParams({
        key: "YOUR_API_KEY",
        action: "get",
        id: taskId,
        json: 1,
      })}`
    );
    const data = await res.json();
    if (data.status === 1) {
      token = data.request;
      break;
    }
    if (data.request !== "CAPCHA_NOT_READY")
      throw new Error(`Solve failed: ${data.request}`);
  }

  // Inject token
  await page.evaluate((t) => {
    document.getElementById("g-recaptcha-response").innerHTML = t;
    const cb = document.querySelector(".g-recaptcha")?.dataset.callback;
    if (cb && window[cb]) window[cb](t);
  }, token);

  // Wait for navigation after form submit
  await page.waitForNavigation({ waitUntil: "networkidle2" });
  const content = await page.content();
  await browser.close();
  return content;
}

scrapeWithCaptcha("https://example.com/protected-page").then(console.log);

Headless-режим: как не попасть под блокировку раньше CAPTCHA

Часть сайтов распознаёт headless-браузер и блокирует его ещё до появления reCAPTCHA — тогда решённый токен просто некуда подставить. Если блокировка происходит раньше, чем вы видите виджет:

Симптом Что сделать
блокировка до появления виджета headless: "new" в Puppeteer вместо старого headless-режима
блокировка сразу после запуска Chromium флаг --disable-blink-features=AutomationControlled
подозрительный User-Agent по умолчанию подставить реальную строку User-Agent
блокировки после серии запросов подряд ротация прокси для запросов после решения CAPTCHA — обычная сетевая практика, а не способ обойти саму проверку

Решаем reCAPTCHA v2 без браузера (HTTP-only)

Если целевой сайт принимает решённый токен прямо в форме — без JavaScript-колбэка, — браузер вообще не нужен. Такой вариант быстрее и легче по ресурсам, потому что вы не поднимаете Chromium ради одного запроса. Подходит, когда:

  • форма отправляется обычным POST-запросом, без data-callback;
  • сайт не проверяет состояние DOM или куки, которые ставит только настоящий браузер;
  • нужно масштабировать парсинг горизонтально — HTTP-only процесс легче по памяти, чем Chromium на каждый поток.
import requests
import time

session = requests.Session()
session.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0"

# Load the page to get cookies
session.get("https://example.com/protected-page")

# Solve the CAPTCHA
sitekey = "6Le-wvkSAAAAAN..."  # extracted from page HTML
solve_resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY", "method": "userrecaptcha",
    "googlekey": sitekey, "pageurl": "https://example.com/protected-page",
    "json": 1
}).json()

task_id = solve_resp["request"]
time.sleep(15)

# Poll
for _ in range(30):
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id, "json": 1
    }).json()
    if result.get("status") == 1:
        token = result["request"]
        break
    time.sleep(5)

# Submit with token
resp = session.post("https://example.com/protected-page", data={
    "g-recaptcha-response": token,
    "other_field": "value"
})
print(resp.text[:500])

Часто задаваемые вопросы

Сколько потоков CaptchaAI нужно для стабильного объёма?

Ориентируйтесь на число одновременных запросов, а не на почасовой лимит: один поток CaptchaAI — это одна задача reCAPTCHA v2, которая решается прямо сейчас.

Тариф Потоков Задач reCAPTCHA v2 параллельно
BASIC ($15/мес) 5 до 5
ADVANCE ($90/мес) 50 до 50
ENTERPRISE ($300/мес) 200 до 200

Решение reCAPTCHA v2 обычно занимает 15–60 секунд, так что итоговая скорость парсинга упирается не только в число потоков, но и во время конкретной задачи. Начните с одного тарифа и увеличивайте потоки, если очередь на res.php растёт быстрее, чем парсер успевает её разбирать.

Можно ли переиспользовать токен reCAPTCHA v2 для нескольких запросов?

Нет. Токен одноразовый и рассчитан примерно на 2 минуты — сайт отклонит повторное использование одного и того же значения на другой странице или в другом запросе. На каждую защищённую страницу нужен отдельный вызов in.php и свежий токен.

Что делать, если res.php долго отдаёт CAPCHA_NOT_READY или приходит явная ошибка?

Если статус держится дольше 60–90 секунд, увеличьте интервал между опросами и лимит попыток в цикле. Если приходит ERROR_CAPTCHA_UNSOLVABLE, проверьте, что googlekey и pageurl извлечены верно: sitekey привязан к конкретному домену, и для чужого адреса токен не решится.

Нужен ли браузер, если сайт принимает g-recaptcha-response напрямую?

Не всегда. Если сайт принимает g-recaptcha-response как обычное POST-поле формы — без JavaScript-колбэка, — подойдёт HTTP-only подход из этой статьи. Браузер нужен, только если токен передаётся через data-callback или сайт проверяет состояние DOM перед отправкой.

Токен получен и подставлен, а парсер всё равно блокируется — почему?

Решённый токен снимает только проверку reCAPTCHA. Остальные признаки автоматизации сайт может отследить отдельно и заблокировать запрос ещё до показа CAPTCHA — тогда помогает не API, а настройка браузера из раздела выше.


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

Тема Материал
Решение reCAPTCHA v2 через API читать
CAPTCHA в Selenium на Python читать
CAPTCHA в Puppeteer на Node.js читать
Частые ошибки решения reCAPTCHA v2 читать
Комментарии для этой статьи отключены.