Как исправить CAPCHA_NOT_READY при опросе результата

capcha_not_ready - How to Fix CAPCHA_NOT_READY When Polling for a Result

Короткий ответ: CAPCHA_NOT_READY — это не ошибка. Так API сообщает, что капча ещё решается, а вы запросили результат слишком рано. В вашем запросе исправлять нечего. Вам просто нужен правильный ритм опроса и цикл, который знает, когда остановиться. В этом руководстве рассматривается и то, и другое, а также поведение «чтение один раз», из-за которого рабочий скрипт начинает сбивать с толку.

Что на самом деле означает CAPCHA_NOT_READY

Когда вы отправляете капчу в /in.php вы получаете обратно ID, а не ответ. Распознавание происходит в фоне. Затем вы опрашиваете /res.php с этим ID, пока ответ не будет готов.

Пока это не так, каждый опрос возвращает одну и ту же строку:

# Poll for the result. Note action=get and the id from /in.php.
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=CAPTCHA_ID"

# Still working:
CAPCHA_NOT_READY

# Done:
OK|03AGdBq26Sxo...

И да, это пишется CAPCHA, а не CAPTCHA. Эта опечатка присутствует в API 2captcha с самого начала. CapSkip полностью совместим с этим API, поэтому ошибка в написании сохранена намеренно. Если её исправить, сломается каждая существующая клиентская библиотека, которая проверяет точную строку. Указывайте её в коде в точности так же.

Опрашивайте по правильному графику

Большинство сталкивается с этим постоянно, потому что запрашивают результат сразу после отправки. Разные типы капч требуют очень разного времени, поэтому первая проверка не должна происходить в один и тот же момент для всех них.

ТипОжидание перед первой проверкойЗатем повторяйте каждые
Изображение / текст1 секунда5 секунд
reCAPTCHA v2от 15 до 20 секунд5 секунд
reCAPTCHA v3От 10 до 15 секунд5 секунд
GeeTestоколо 5 секунд5 секунд
Cloudflare Turnstileоколо 5 секунд5 секунд

Опрос чаще, чем раз в 5 секунд, не ускоряет решение. Он лишь впустую расходует запросы.

Цикл опроса, который завершается

Версия для shell, наглядно показывающая структуру:

# Submit, capture the id, give it a head start, then poll.
ID=$(curl -s "http://127.0.0.1:8080/in.php?key=YOUR_API_KEY&method=userrecaptcha&googlekey=YOUR_SITEKEY&pageurl=https://example.com" | cut -d'|' -f2)

sleep 15
while :; do
  RES=$(curl -s "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=$ID")
  [ "$RES" = "CAPCHA_NOT_READY" ] || break
  sleep 5
done
echo "$RES"

У этого цикла есть недостаток, о котором стоит сказать: он выполняется бесконечно, если что-то пошло не так выше по цепочке. В реальном коде ограничьте его.

# pip install requests
import time
import requests

BASE = "http://127.0.0.1:8080"

def solve_recaptcha(sitekey, page_url, api_key="YOUR_API_KEY", timeout=180):
    task = requests.get(BASE + "/in.php", params={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": 1,
    }).json()
    task_id = task["request"]

    time.sleep(15)                      # reCAPTCHA needs a head start
    deadline = time.monotonic() + timeout

    while time.monotonic() < deadline:
        res = requests.get(BASE + "/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id,
        }).text.strip()

        if res.startswith("OK|"):
            return res.split("|", 1)[1]

        # Anything that is not the pending string is terminal.
        if res != "CAPCHA_NOT_READY":
            raise RuntimeError(res or "empty response: already read, or bad id")

        time.sleep(5)

    raise TimeoutError("gave up after %ss" % timeout)

Три вещи делают это безопасным: дедлайн, чтобы избежать зависания, обработка любой строки, отличной от pending, как финальной, чтобы реальные ошибки проявлялись сразу, и однократное чтение ответа.

Или полностью пропустите цикл

Если вы используете один из официальных SDK, ничего из вышеперечисленного вас не касается. Опрос происходит внутри вызова, и вы получаете токен напрямую, поэтому CAPCHA_NOT_READY никогда не доходит до вашего кода.

# pip install capskip
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

# Submit and poll happen inside this one call.
result = solver.recaptcha(
    sitekey="YOUR_SITEKEY",
    url="https://example.com/page-with-recaptcha",
)

print(result["code"])   # token, ready to inject
// npm install capskip
const { CapSkip } = require('capskip');

const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const result = await solver.recaptcha('YOUR_SITEKEY', 'https://example.com/page-with-recaptcha');

console.log(result.code);
// composer require capskip/capskip
use CapSkip\CapSkip;

$solver = new CapSkip(['host' => '127.0.0.1', 'port' => 8080]);
$result = $solver->recaptcha('YOUR_SITEKEY', 'https://example.com/page-with-recaptcha');

echo $result['code'];
// dotnet add package CapSkip
using CapSkip;

var solver = new CapSkipClient(host: "127.0.0.1", port: 8080);
var result = await solver.RecaptchaAsync("YOUR_SITEKEY", "https://example.com/page-with-recaptcha");

Console.WriteLine(result.Code);

Полные сигнатуры методов для всех четырёх языков находятся на SDK для распознавания капчи странице. Приведённый выше «сырой» цикл — это по-прежнему то, что вам нужно для Go, Java, Ruby или чего-либо без официального пакета.

Ловушка одноразового чтения

Именно эта отнимает полдня. Каждый результат можно прочитать только один раз. Запросите результат снова после успешного чтения — и вы получите пустой ответ, а не токен, который у вас уже был.

Поэтому пустое тело не означает “ещё в процессе”. Оно означает одно из двух:

  • Вы уже получили этот результат и отбросили его
  • ID не существует — обычно это искажённый или обрезанный ID, полученный при парсинге OK|ID

Сохраняйте токен сразу, как только получите его. Не опрашивайте повторно, чтобы “подтвердить” его.

Когда это действительно проблема

Если CAPCHA_NOT_READY никогда не разрешается, строка pending — это симптом, а не причина. Проверьте следующее по порядку:

ОтветЧто это значитИсправить
ERROR_CAPTCHA_UNSOLVABLEПопытка решения была предпринята и не удаласьУбедитесь, что sitekey и pageurl актуальны, затем отправьте повторно
ERROR_WRONG_ID_FORMATID не является допустимым целым числомВы разбираете OK|ID неверно. Разделите по вертикальной черте, возьмите поле 2
ERROR_GOOGLEKEYsitekey был отклонён при отправкеСчитайте его заново с «живой» страницы, а не из кешированного источника
Пустое телоУже прочитано или неизвестный IDСохраняйте результат при первом чтении
Pending дольше 3 минутРешатель не запущен или недоступенУбедитесь, что сервис запущен на настроенном порту. См. руководство по настройке

Полный список строк ошибок находится в Документация по API.

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

Является ли CAPCHA_NOT_READY ошибкой, которую нужно логировать?

Не как ошибку. Это нормальное промежуточное состояние, и вы будете видеть его несколько раз за одно решение. Логируйте его на уровне debug, если вообще логируете, иначе реальные сбои утонут в шуме.

Вернётся ли ответ быстрее при более частом опросе?

Нет. Время решения не зависит от того, как часто вы спрашиваете. 5 секунд — это задокументированный интервал, и всё, что чаще, — это впустую потраченные запросы.

Почему пишут CAPCHA, а не CAPTCHA?

Это старая опечатка в оригинальном API 2captcha, ставшая частью контракта. CapSkip полностью совместим с этим API по принципу drop-in, поэтому строка сохраняется в точности. Исправление написания сломало бы каждый клиент, который её проверяет.

Могу ли я получить состояние ожидания в формате JSON?

Да. Добавьте json=1 к запросу, и ответы возвращаются в виде объекта с status и request поля вместо обычного текста. Сама строка pending остаётся неизменной.

Сводка

Дайте решению фору перед первой проверкой, повторяйте попытку каждые 5 секунд, расценивайте всё, кроме CAPCHA_NOT_READY как терминальное, ограничьте цикл дедлайном и считайте результат ровно один раз. В этом весь шаблон.

Если вы вообще предпочли бы не писать цикл, CapSkip — это распознавание капчи который работает локально и поставляется с SDK для Python, Node.js, PHP и .NET, берущими опрос на себя.