Устранение неполадок

Распространенные невидимые ошибки reCAPTCHA и их исправления

Невидимая reCAPTCHA убирает этот флажок, но добавляет новые режимы сбоя для автоматизации. Самыми большими проблемами являются необнаружение невидимого виджета вообще, отсутствие функции обратного вызова, отправка токенов с истекшим сроком действия и использование стандартных параметров версии 2, когда требуются невидимые параметры.

В этом руководстве описаны все распространенные ошибки и их точное исправление. Если вам нужна информация о том, как работает невидимая reCAPTCHA, прочитайтеКак работает reCAPTCHA Invisible и как его решитьпервый.


Краткий справочник ошибок

Ошибка Причина Исправить
ERROR_WRONG_GOOGLEKEY Неправильный ключ сайта или ключ сайта из другого домена. Извлеките ключ сайта из невидимого div виджета или вызова grecaptcha.render().
ERROR_PAGEURL Несоответствие URL-адресов — вместо URL-адреса iframe отправлен URL-адрес родительской страницы. Используйте точный URL-адрес, по которому загружается невидимый виджет.
ERROR_CAPTCHA_UNSOLVABLE Google пометил задачу как невыполнимую Повторите попытку, используя новый прокси и файлы cookie; проверьте, перешел ли сайт на v3
ERROR_BAD_TOKEN_OR_PAGEURL Токен отклонен целевым сайтом Убедитесь, что URL-адрес страницы точно соответствует; внедрить через обратный вызов, а не через скрытое поле
CAPCHA_NOT_READY Задача еще обрабатывается Продолжайте опрос каждые 5 секунд; невидимые решения занимают 10-30 секунд
ERROR_KEY_DOES_NOT_EXIST Неверный ключ API CaptchaAI. Проверьте ключ наcaptchaai.com/account
Токен принят, но форма не удалась Обратный вызов не выполняется после передача токена во внутренний QA endpoint Найдите и вызовите функцию data-callback с помощью токена.

Ошибка 1: Не обнаружена невидимая reCAPTCHA на странице.

Невидимый reCAPTCHA не имеет видимого флажка. Если ваш парсер не обнаруживает этого, запросы, защищенные капчей, автоматически завершаются ошибкой с ошибками отправки формы или перенаправлениями.

Как обнаружить невидимую reCAPTCHA

Найдите эти шаблоны в HTML-странице:

<!-- Pattern 1: div with data-size="invisible" -->
<div class="g-recaptcha" data-sitekey="6LdKlZEU..."
     data-size="invisible"
     data-callback="onSubmit"></div>

<!-- Pattern 2: button with data-sitekey and invisible size -->
<button class="g-recaptcha"
        data-sitekey="6LdKlZEU..."
        data-callback="onSubmit"
        data-action="submit">Submit</button>

<!-- Pattern 3: programmatic render with size: invisible -->
<script>
  grecaptcha.render('submit-btn', {
    sitekey: '6LdKlZEU...',
    callback: onSubmit,
    size: 'invisible'
  });
</script>

Скрипт обнаружения (Python):

import requests
from bs4 import BeautifulSoup
import re

def detect_invisible_recaptcha(url):
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, "html.parser")

    # Check for data-size="invisible"
    widget = soup.find("div", {"data-size": "invisible", "class": "g-recaptcha"})
    if widget:
        return {
            "type": "invisible",
            "sitekey": widget.get("data-sitekey"),
            "callback": widget.get("data-callback")
        }

    # Check for programmatic render with invisible
    scripts = soup.find_all("script")
    for script in scripts:
        if script.string and "size" in str(script.string) and "invisible" in str(script.string):
            key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
            if key_match:
                return {
                    "type": "invisible-programmatic",
                    "sitekey": key_match.group(1),
                    "callback": "check grecaptcha.render() call"
                }

    return None

Скрипт обнаружения (Node.js):

const axios = require("axios");
const cheerio = require("cheerio");

async function detectInvisibleRecaptcha(url) {
  const { data } = await axios.get(url);
  const $ = cheerio.load(data);

  // Check for data-size="invisible"
  const widget = $(".g-recaptcha[data-size='invisible']");
  if (widget.length) {
    return {
      type: "invisible",
      sitekey: widget.attr("data-sitekey"),
      callback: widget.attr("data-callback"),
    };
  }

  // Check script tags for programmatic invisible render
  const scriptContent = $("script")
    .map((_, el) => $(el).html())
    .get()
    .join("\n");
  if (scriptContent.includes("invisible")) {
    const keyMatch = scriptContent.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
    if (keyMatch) {
      return {
        type: "invisible-programmatic",
        sitekey: keyMatch[1],
        callback: "check grecaptcha.render() call",
      };
    }
  }

  return null;
}

Ошибка 2: неправильный ключ сайта — ERROR_WRONG_GOOGLEKEY.

Это происходит, когда вы отправляете ключ сайта, который не соответствует невидимому виджету reCAPTCHA на целевой странице. Распространенные причины:

  • Ключ сайта скопирован из флажка v2 на другой странице.
  • Использован ключ сайта из привязки другой версии reCAPTCHA.
  • На странице несколько виджетов reCAPTCHA, и вы выбрали не тот

Исправлено: извлеките правильный невидимый ключ сайта.

import requests
from bs4 import BeautifulSoup

def get_invisible_sitekey(url):
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, "html.parser")

    # Priority 1: invisible widget
    widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
    if widget:
        return widget["data-sitekey"]

    # Priority 2: any g-recaptcha div (may be invisible without data-size)
    widget = soup.find(class_="g-recaptcha")
    if widget and widget.get("data-sitekey"):
        return widget["data-sitekey"]

    return None

sitekey = get_invisible_sitekey("https://https://staging.example.com/qa-login")
print(f"Sitekey: {sitekey}")

Ошибка 3: обратный вызов не выполнен — форма отправляется, но ничего не происходит.

Это невидимый сбой reCAPTCHA номер один, который упускают из виду разработчики. В отличие от флажка версии 2, где достаточно ввести токен в g-recaptcha-response, невидимая reCAPTCHA почти всегда использует функцию обратного вызова JavaScript. Если вы внедрите токен, но не вызовете обратный вызов, форма никогда не будет обработана.

Как работает поток обратного вызова

  1. grecaptcha.execute() бросает невидимый вызов
  2. После решения Google вызывает функцию, указанную в data-callback.
  3. Эта функция обратного вызова отправляет форму или выполняет вызов API.

Исправлено: найти и выполнить обратный вызов.

Шаг 1. Определите имя обратного вызова:

# From HTML: data-callback="onSubmit"
# From JS: callback: onSubmit
# From grecaptcha.render: second argument with callback property

Шаг 2. Внедрить токен И вызвать обратный вызов (Selenium):

from selenium import webdriver
import requests
import time

driver = webdriver.Chrome()
driver.get("https://example.com/form")

# Get sitekey
sitekey = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-sitekey")
callback_name = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")

# Solve with CaptchaAI
task_id = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": driver.current_url,
    "invisible": 1
}).text.split("|")[1]

# Poll for result
token = None
for _ in range(60):
    time.sleep(5)
    resp = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY",
        "action": "get",
        "id": task_id
    }).text
    if resp.startswith("OK|"):
        token = resp.split("|")[1]
        break

# Inject token into the response field
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").value = "{token}";'
)

# CRITICAL: Call the callback function
driver.execute_script(f'{callback_name}("{token}");')

Шаг 2. Внедрить токен И вызвать обратный вызов (Puppeteer):

const puppeteer = require("puppeteer");
const axios = require("axios");

(async () => {
  const browser = await puppeteer.launch({ headless: "new" });
  const page = await browser.newPage();
  await page.goto("https://example.com/form");

  // Get sitekey and callback
  const { sitekey, callback } = await page.evaluate(() => {
    const el = document.querySelector(".g-recaptcha[data-size='invisible']");
    return {
      sitekey: el?.getAttribute("data-sitekey"),
      callback: el?.getAttribute("data-callback"),
    };
  });

  // Submit to CaptchaAI
  const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
    params: {
      key: "YOUR_API_KEY",
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: page.url(),
      invisible: 1,
    },
  });
  const taskId = submitResp.data.split("|")[1];

  // Poll for result
  let token;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: "YOUR_API_KEY", action: "get", id: taskId },
    });
    if (result.data.startsWith("OK|")) {
      token = result.data.split("|")[1];
      break;
    }
  }

  // Inject token and fire callback
  await page.evaluate(
    (tok, cb) => {
      document.getElementById("g-recaptcha-response").value = tok;
      if (cb && typeof window[cb] === "function") {
        window[cb](tok);
      }
    },
    token,
    callback,
  );

  await browser.close();
})();

Ошибка 4: отсутствует параметр invisible=1.

При решении невидимой reCAPTCHA с помощью CaptchaAI вы должны включать invisible=1 в свой запрос. Без него решатель рассматривает задачу как стандартную проверку флажка версии 2, что может привести к ERROR_CAPTCHA_UNSOLVABLE или токенам, которые целевой сайт отклонит.

Неправильный и правильный запрос

# WRONG — missing invisible=1
params = {
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url
}

# CORRECT — includes invisible=1
params = {
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "invisible": 1  # Required for invisible reCAPTCHA
}

response = requests.get("https://ocr.captchaai.com/in.php", params=params)

Ошибка 5. Срок действия токена истек до отправки.

Срок действия невидимых токенов reCAPTCHA истекает через 120 секунд — так же, как и в стандарте v2. Но невидимые рабочие процессы часто включают дополнительные этапы обработки между решением и отправкой, что повышает вероятность истечения срока действия.

Симптомы

  • Форма возвращает общую ошибку после передача токена во внутренний QA endpoint
  • Серверный siteverify возвращает timeout-or-duplicate
  • Токен был действителен, но переход к этапу отправки занял слишком много времени.

Исправление: Своевременное решение

Запрашивайте решение только тогда, когда вы готовы отправить его немедленно:

import requests
import time

def solve_invisible_recaptcha(api_key, sitekey, page_url):
    # Submit task
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "invisible": 1
    })
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit failed: {resp.text}")
    task_id = resp.text.split("|")[1]

    # Poll for result
    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
        })
        if result.text.startswith("OK|"):
            return result.text.split("|")[1]
        if result.text != "CAPCHA_NOT_READY":
            raise Exception(f"Solve failed: {result.text}")

    raise Exception("Solve timed out after 5 minutes")

# Usage: solve JUST before you need to submit
# 1. Navigate to page and prepare form data first
# 2. THEN solve the captcha
# 3. Inject token and submit immediately
token = solve_invisible_recaptcha("YOUR_API_KEY", sitekey, page_url)
# Submit within 120 seconds of receiving the token

Ошибка 6: Токен отклонен — ERROR_BAD_TOKEN_OR_PAGEURL.

Целевой сайт проверил токен в Google и получил ошибку. Распространенные причины:

Причина Как определить Исправить
Неправильный pageurl URL-адрес не соответствует домену при регистрации ключа сайта. Используйте точный URL-адрес, по которому загружается виджет.
Токен используется в другом домене Междоменное повторное использование токенов Решите, используя правильный домен pageurl.
Токен уже использован Отправка одного и того же токена дважды Запрашивайте новое решение для каждой отправки
несоответствие IP-адресов Ваш IP отличается от IP решателя Добавьте параметр proxy, соответствующий IP-адресу сеанса.
Невидимый флаг отсутствует Решено как стандарт v2, используется на невидимой странице. Добавьте invisible=1 в запрос на решение.

Контрольный список отладки

def debug_invisible_solve(api_key, sitekey, page_url, proxy=None):
    """Run a diagnostic solve with detailed logging."""
    print(f"Sitekey: {sitekey}")
    print(f"Page URL: {page_url}")
    print(f"Proxy: {proxy or 'none'}")

    params = {
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "invisible": 1
    }
    if proxy:
        params["proxy"] = proxy
        params["proxytype"] = "HTTP"

    # Submit
    resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
    print(f"Submit response: {resp.text}")
    if not resp.text.startswith("OK|"):
        return None

    task_id = resp.text.split("|")[1]
    print(f"Task ID: {task_id}")

    # Poll with timing
    start = time.time()
    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
        })
        elapsed = time.time() - start
        print(f"  [{elapsed:.0f}s] {result.text[:50]}")
        if result.text.startswith("OK|"):
            token = result.text.split("|")[1]
            print(f"Token received after {elapsed:.0f}s")
            print(f"Token length: {len(token)} characters")
            print(f"Token starts with: {token[:30]}...")
            return token
        if result.text != "CAPCHA_NOT_READY":
            print(f"FAILED: {result.text}")
            return None

    print("TIMEOUT after 5 minutes")
    return None

Ошибка 7: несколько виджетов reCAPTCHA на одной странице.

На некоторых страницах есть как видимый флажок версии 2, так и невидимая reCAPTCHA. Если вы решите неправильный, токен действителен, но не соответствует виджету, охраняющему нужное вам действие.

Исправлено: выберите правильный виджет.

from bs4 import BeautifulSoup

def find_all_recaptcha_widgets(html):
    soup = BeautifulSoup(html, "html.parser")
    widgets = []

    for el in soup.find_all(class_="g-recaptcha"):
        widgets.append({
            "sitekey": el.get("data-sitekey"),
            "size": el.get("data-size", "normal"),
            "callback": el.get("data-callback"),
            "tag": el.name,
            "id": el.get("id")
        })

    return widgets

# Example output:
# [
#   {"sitekey": "6LdA...", "size": "normal", "callback": None, "tag": "div", "id": "recaptcha-login"},
#   {"sitekey": "6LdB...", "size": "invisible", "callback": "onRegister", "tag": "div", "id": "recaptcha-register"}
# ]
# Use the widget with size="invisible" for the invisible solve

Полный невидимый решатель reCAPTCHA с обработкой ошибок

Эта готовая к использованию оболочка обрабатывает все вышеперечисленные ошибки:

import requests
import time
import logging

logger = logging.getLogger(__name__)

class InvisibleRecaptchaSolver:
    def __init__(self, api_key, max_retries=3):
        self.api_key = api_key
        self.max_retries = max_retries
        self.base_url = "https://ocr.captchaai.com"

    def solve(self, sitekey, page_url, proxy=None):
        """Solve invisible reCAPTCHA with automatic retry on transient errors."""
        for attempt in range(1, self.max_retries + 1):
            try:
                token = self._attempt_solve(sitekey, page_url, proxy)
                if token:
                    return token
            except Exception as e:
                logger.warning(f"Attempt {attempt} failed: {e}")
                if attempt < self.max_retries:
                    time.sleep(2 ** attempt)
        raise Exception(f"Failed to solve after {self.max_retries} attempts")

    def _attempt_solve(self, sitekey, page_url, proxy):
        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": page_url,
            "invisible": 1
        }
        if proxy:
            params["proxy"] = proxy
            params["proxytype"] = "HTTP"

        # Submit task
        resp = requests.get(f"{self.base_url}/in.php", params=params)

        if "ERROR" in resp.text:
            error = resp.text.strip()
            if error in ("ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"):
                raise Exception(f"Configuration error (do not retry): {error}")
            if error == "ERROR_ZERO_BALANCE":
                raise Exception("Account balance is zero — add funds")
            raise Exception(f"Submit error: {error}")

        if not resp.text.startswith("OK|"):
            raise Exception(f"Unexpected submit response: {resp.text}")

        task_id = resp.text.split("|")[1]

        # Poll for result
        for _ in range(60):
            time.sleep(5)
            result = requests.get(f"{self.base_url}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id
            })

            if result.text.startswith("OK|"):
                return result.text.split("|")[1]

            if result.text == "CAPCHA_NOT_READY":
                continue

            if result.text == "ERROR_CAPTCHA_UNSOLVABLE":
                logger.warning("Captcha unsolvable — will retry with new task")
                return None

            raise Exception(f"Poll error: {result.text}")

        raise Exception("Solve timed out after 5 minutes")


# Usage
solver = InvisibleRecaptchaSolver("YOUR_API_KEY")
token = solver.solve(
    sitekey="6LdKlZEU...",
    page_url="https://https://staging.example.com/qa-login"
)
print(f"Token: {token[:50]}...")
const axios = require("axios");

class InvisibleRecaptchaSolver {
  constructor(apiKey, maxRetries = 3) {
    this.apiKey = apiKey;
    this.maxRetries = maxRetries;
    this.baseUrl = "https://ocr.captchaai.com";
  }

  async solve(sitekey, pageUrl, proxy) {
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        const token = await this._attemptSolve(sitekey, pageUrl, proxy);
        if (token) return token;
      } catch (err) {
        console.warn(`Attempt ${attempt} failed: ${err.message}`);
        if (attempt < this.maxRetries) {
          await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
        }
      }
    }
    throw new Error(`Failed to solve after ${this.maxRetries} attempts`);
  }

  async _attemptSolve(sitekey, pageUrl, proxy) {
    const params = {
      key: this.apiKey,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageUrl,
      invisible: 1,
    };
    if (proxy) {
      params.proxy = proxy;
      params.proxytype = "HTTP";
    }

    // Submit task
    const submitResp = await axios.get(`${this.baseUrl}/in.php`, { params });
    if (submitResp.data.includes("ERROR")) {
      const error = submitResp.data.trim();
      if (["ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"].includes(error)) {
        throw new Error(`Configuration error (do not retry): ${error}`);
      }
      throw new Error(`Submit error: ${error}`);
    }

    const taskId = submitResp.data.split("|")[1];

    // Poll for result
    for (let i = 0; i < 60; i++) {
      await new Promise((r) => setTimeout(r, 5000));
      const result = await axios.get(`${this.baseUrl}/res.php`, {
        params: { key: this.apiKey, action: "get", id: taskId },
      });

      if (result.data.startsWith("OK|")) {
        return result.data.split("|")[1];
      }
      if (result.data === "CAPCHA_NOT_READY") continue;
      if (result.data === "ERROR_CAPTCHA_UNSOLVABLE") return null;
      throw new Error(`Poll error: ${result.data}`);
    }
    throw new Error("Solve timed out after 5 minutes");
  }
}

// Usage
const solver = new InvisibleRecaptchaSolver("YOUR_API_KEY");
solver.solve("6LdKlZEU...", "https://https://staging.example.com/qa-login").then((token) => {
  console.log(`Token: ${token.substring(0, 50)}...`);
});

Контрольный список устранения неполадок

Если не удалось решить невидимую reCAPTCHA, выполните этот контрольный список:

Шаг Проверять Команда/Action
1 Подтвердите, что он невидим, а не стандарт v2. Найдите data-size="invisible" или size: 'invisible' в вызове рендеринга.
2 Убедитесь, что ключ сайта верен Сравните с data-sitekey конкретно на невидимом виджете
3 Подтвердите invisible=1 в запросе API. Проверьте параметры in.php
4 Проверьте точное совпадение pageurl Используйте URL-адрес DevTools браузера, а не URL-адрес перенаправления.
5 Найдите имя функции обратного вызова Найдите атрибут data-callback или callback в grecaptcha.render().
6 Проверка передача токена во внутренний QA endpoint + обратный вызов Оба шага обязательны — одного токена недостаточно.
7 Проверить свежесть токена Токен необходимо использовать в течение 120 секунд.
8 Проверьте с помощью прокси, имеет ли значение IP Добавьте параметры proxy и proxytype.

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

Чем невидимая reCAPTCHA отличается от стандартной версии v2 для решения?

Метод API тот же (method=userrecaptcha), но к вашему запросу необходимо добавить invisible=1. Критическое различие заключается в внедрении: невидимая reCAPTCHA почти всегда требует вызова функции обратного вызова JavaScript после передача токена во внутренний QA endpoint, в то время как стандарт v2 обычно работает только со скрытым полем.

Почему мой токен работает при тестировании, но не работает в рабочей среде?

Скорее всего несовпадение IP. При тестировании решатель и ваш браузер могут иметь одинаковые IP-адреса. В производстве IP-адрес решателя и IP-адрес вашего сервера различаются. Чтобы исправить это, добавьте параметр прокси, соответствующий IP-адресу вашего сеанса.

Сколько времени занимает решение невидимой reCAPTCHA?

Типичное время решения составляет 10–30 секунд до CaptchaAI. Невидимые задачи обычно выполняются быстрее, чем задачи с флажками версии 2, поскольку они не требуют распознавания изображений — они основаны на анализе рисков.

Могу ли я решить невидимую reCAPTCHA без браузера?

Да. Поскольку решение происходит на стороне сервера через API, вам нужны только ключ сайта и URL-адрес страницы. Браузер необходим только в том случае, если вам необходимо выполнить функцию обратного вызова на реальной странице. Для рабочих процессов, использующих чистый API, извлеките ключ сайта, решите его с помощью CaptchaAI и отправьте токен вместе с HTTP-запросом.


Следующие шаги

  • CaptchaAI Quickstart: ваше первое решение CAPTCHA за 5 минут
  • Как решить Cloudflare Turnstile через API
  • Как решить GeeTest v3 с помощью API
Комментарии для этой статьи отключены.