Как решить капчу через cURL и сырой HTTP API

solve captcha with curl - How to Solve CAPTCHA with cURL and the Raw HTTP API

SDK вам не нужен. API CapSkip совместим с 2captcha, поэтому решить капчу через curl можно ровно двумя запросами: отправьте задачу POST-запросом на /in.php и получите ID, затем опрашивайте /res.php пока не появится ответ. Всё работает на 127.0.0.1:8080, так что удалённого эндпоинта нет и плата за каждое решение не взимается. В этом руководстве точные параметры для каждого типа капчи, форматы JSON-ответов и скрипт, который можно вставить прямо в терминал.

Что понадобится

  • Запущенное приложение CapSkip с активным локальным сервисом. Настройки порта и ключа описаны в руководство по настройке.
  • curl. Он есть в macOS, в любом дистрибутиве Linux и в Windows 10 сборки 1803 и новее.
  • jq если хотите вытаскивать поля из JSON-ответов. Необязательно, но с ним примеры умещаются в одну строку.

Проверка ключа по умолчанию отключена, поэтому подойдёт любая непустая строка в поле key. Главное что-то передать: при пустом ключе возвращается ERROR_WRONG_USER_KEY.

На этом список зависимостей заканчивается, потому что весь API это два эндпоинта:

ЭндпоинтЧто делаетВозвращает
/in.phpОтправляет задачуЧисловой ID капчи
/res.phpСпрашивает, готов ли этот IDОтвет или CAPCHA_NOT_READY

Оба принимают GET и POST. POST привычка получше: URL страницы с query-параметрами незаметно обрежет GET-запрос на первом незакодированном амперсанде.

Шаг 1: отправьте задачу

reCAPTCHA v2 самый короткий пример. Задачу определяют два значения: sitekey со страницы и URL страницы, на которой она стоит.

# No install step. curl is already on your machine.
curl -X POST http://127.0.0.1:8080/in.php \
  -d "key=YOUR_API_KEY" \
  -d "method=userrecaptcha" \
  -d "googlekey=YOUR_SITEKEY" \
  -d "pageurl=https://example.com/page-with-recaptcha"

OK|2122988149   # the number after the pipe is your captcha ID

Обратите внимание на имя параметра. reCAPTCHA использует googlekey. Turnstile использует sitekey. Отправка sitekey в userrecaptcha самая частая причина ответа ERROR_GOOGLEKEY .

Шаг 2: опрашивайте результат

Сначала подождите, потом спрашивайте. Немедленный опрос просто тратит запрос и возвращает CAPCHA_NOT_READY.

sleep 15

curl -X POST http://127.0.0.1:8080/res.php \
  -d "key=YOUR_API_KEY" \
  -d "action=get" \
  -d "id=2122988149"

OK|03AGdBq26...   # the token, ready to inject into the form

Две особенности /res.php , о которые спотыкаются. Он возвращает CAPCHA_NOT_READY пока задача ещё выполняется: это не ошибка, а сигнал продолжать опрос. И каждый результат читается только один раз, поэтому сохраняйте ответ сразу же. Повторное чтение того же ID вернёт пустоту.

Сколько ждать до первого опроса, зависит от типа:

ТипПервый опрос через
Картинка1 секунда
reCAPTCHA v2от 15 до 20 секунд
reCAPTCHA v3От 10 до 15 секунд
GeeTest v3около 5 секунд

Добавьте json=1, чтобы разбирать ответ

Простой текстовый формат удобен человеку в терминале и неудобен скрипту. Добавьте json=1 к любому из эндпоинтов и вместо него получите стабильный объект.

// in.php with json=1
{"status": 1, "request": "2122988149"}

// res.php with json=1, once it is solved
{"status": 1, "request": "03AGdBq26..."}

// res.php with json=1, still working
{"status": 0, "request": "CAPCHA_NOT_READY"}

status равен 1 при успехе и 0 во всех остальных случаях, а нужное значение всегда лежит в request. Так что всё сводится к двум выражениям jq .

Все методы в одной таблице

Девять типов капчи, пять значений method. Варианты это дополнительные параметры, а не новые эндпоинты.

ТипmethodОбязательные параметры
Картинка, загруженный файлpostfile
Картинка, base64base64body
reCAPTCHA v2userrecaptchagooglekey, pageurl
reCAPTCHA v2 Invisibleuserrecaptchaплюс invisible=1
reCAPTCHA Enterpriseuserrecaptchaплюс enterprise=1
reCAPTCHA v3userrecaptchaплюс version=v3, action
Виджет Turnstileturnstilesitekey, pageurl
Страница-проверка Turnstileturnstileплюс data, pagedata
GeeTest v3geetestgt, challenge, pageurl

Картинка уходит либо как файл формы, либо как base64 в теле запроса:

# File upload. Note the @ in front of the path. Use -F for every
# field here: curl refuses to mix -F and -d in one request.
curl -X POST http://127.0.0.1:8080/in.php \
  -F "key=YOUR_API_KEY" -F "method=post" -F "[email protected]"

# Or send the bytes inline, already base64 encoded.
curl -X POST http://127.0.0.1:8080/in.php \
  -d "key=YOUR_API_KEY" \
  -d "method=base64" \
  --data-urlencode "body=$(base64 < captcha.png | tr -d '\n')"

Использовать --data-urlencode для всего, что содержит +, / или =. В base64-данных встречаются все три символа, а обычный -d их испортит.

Turnstile возвращает ещё и user agent

Cloudflare привязывает токен к отпечатку браузера, который его получил, поэтому токен, отправленный с другим user agent, отклоняется, даже если сам он полностью валиден. Сырой API отдаёт использованное значение в двух местах:

  • С json=1в виде поля userAgent в ответе.
  • В текстовом режиме как HTTP-заголовок ответа X-Turnstile-User-Agent заголовок ответа.
# -i prints the headers, which is where the user agent lives
# when you are not using json=1.
curl -i -X POST http://127.0.0.1:8080/res.php \
  -d "key=YOUR_API_KEY" -d "action=get" -d "id=2122988149"

X-Turnstile-User-Agent: Mozilla/5.0 ...
OK|0.abc123...

Полностраничным проверкам нужны ещё data (значение cData) и pagedata (chlPageData), считанные со страницы прямо перед отправкой. Режиму виджета не нужно ни то, ни другое. На странице Сервис распознавания Turnstile эта разница разобрана подробнее.

Ответ GeeTest это три поля, а не одно

GeeTest не возвращает один токен. Запросите JSON и получите три значения, которые отправил бы обратно собственный фронтенд сайта.

{
  "status": 1,
  "request": {
    "geetest_challenge": "...",
    "geetest_validate":  "...",
    "geetest_seccode":   "..."
  }
}

The gt значение постоянно для сайта. А значение challenge значение одноразовое и живёт около минуты, поэтому получайте его непосредственно перед отправкой, а не в начале длинного скрипта.

Решение через прокси

Два параметра, добавленные к тому же /in.php запросу:

curl -X POST http://127.0.0.1:8080/in.php \
  -d "key=YOUR_API_KEY" \
  -d "method=userrecaptcha" \
  -d "googlekey=YOUR_SITEKEY" \
  -d "pageurl=https://example.com/page-with-recaptcha" \
  -d "proxy=login:[email protected]:3128" \
  -d "proxytype=HTTPS"

proxytype принимает HTTP, HTTPS, SOCKS5 или SOCKS5H. Прокси работают только для reCAPTCHA, Turnstile и GeeTest. Распознавание картинки читает пиксели, которые у вас уже есть, и целевой сайт вообще не трогает, поэтому прокси там ничего не даёт.

Готовый скрипт

Отправить, опросить с ограничением, вывести токен. Около двадцати строк, без зависимостей кроме curl.

#!/usr/bin/env bash
set -euo pipefail

API="http://127.0.0.1:8080"
KEY="YOUR_API_KEY"

# Submit and keep only the part after the pipe.
ID=$(curl -s -X POST "$API/in.php" \
  -d "key=$KEY" -d "method=userrecaptcha" \
  -d "googlekey=YOUR_SITEKEY" \
  -d "pageurl=https://example.com/page-with-recaptcha" | cut -d'|' -f2)

sleep 15

# Poll every 5s, give up after 20 tries so this cannot hang forever.
for _ in $(seq 20); do
  R=$(curl -s -X POST "$API/res.php" -d "key=$KEY" -d "action=get" -d "id=$ID")
  [ "$R" = "CAPCHA_NOT_READY" ] || { echo "${R#OK|}"; exit 0; }
  sleep 5
done

echo "timed out waiting for $ID" >&2; exit 1

The || { ...; exit 0; } срабатывает на всём, что не равно CAPCHA_NOT_READY, включая коды ошибок. Так задумано: ошибка означает остановиться, а не продолжать опрос.

Ошибки, которые встречаются на этом уровне

КодОзначаетИсправить
ERROR_WRONG_USER_KEYКлюч отсутствует или пустПередайте любой непустой key
ERROR_WRONG_METHODНеверный method или actionСверьте написание с таблицей выше
ERROR_BAD_PARAMETERSНе хватает обязательного параметраСравните со столбцом обязательных параметров выше
ERROR_GOOGLEKEYThe googlekey значение отклоненоСкорее всего, вы отправили sitekey вместо него
ERROR_PAGEURLThe pageurl значение отклоненоУкажите схему и используйте POST вместо GET
CAPCHA_NOT_READYЕщё выполняетсяНе ошибка. Продолжайте опрашивать тот же ID
Пустой ответУже прочитан либо такого ID нетРезультат читается один раз. Сохраняйте первый же

Точные формулировки всех кодов, которые может вернуть API, есть в Документация по API.

Когда пора уходить с curl

Сырой HTTP отлично подходит для быстрой проверки, shell-конвейера или языка без официального клиента. Для прикладного кода CapSkip SDKs стоят добавленной зависимости прежде всего по одной причине: они не опрашивают с постоянным интервалом. Они начинают с 250 мс и увеличивают паузу до pollingInterval, поэтому решение обычно возвращается быстрее, чем в самописном цикле выше, который каждый раз честно выжидает свои 15 секунд.

Они же превращают строки ошибок в типизированные исключения и сами разбираются с user agent для Turnstile и трёхполевым ответом GeeTest. Официальные клиенты есть для Python, Node.js, PHP и .NET.

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

Можно ли использовать GET вместо POST?

Да, оба эндпоинта его принимают. Загвоздка в том, что pageurl со своей собственной query-строкой обрежется на первом незакодированном амперсанде, и вы получите ERROR_PAGEURL или решение не той страницы. Если GET всё-таки нужен, прогоните URL через --data-urlencode заранее.

Какой API-ключ передавать?

Любую непустую строку. Проверка ключа по умолчанию отключена, потому что сервис слушает только localhost, поэтому key=capskip работает нормально. Но он должен присутствовать: без него вы получите ERROR_WRONG_USER_KEY , а не решение.

Работает ли это в Windows PowerShell?

Использовать curl.exe явно. В PowerShell curl это псевдоним для Invoke-WebRequest, который не понимает -d и выдаст ошибку параметра, похожую на проблему API. Запись curl.exe обходит псевдоним.

Можно ли решать несколько капч одновременно?

Да. Отправляйте сколько угодно задач и опрашивайте каждый ID независимо. Ничто их не сериализует, и поминутной квоты нет, потому что работа идёт на вашем железе, а не в общей очереди.

Сводка

Отправьте задачу POST-запросом на /in.php, сохраните ID, выждите подходящую типу паузу, затем опрашивайте /res.php пока не получите что-то кроме CAPCHA_NOT_READY. Добавьте json=1 если ответ читает скрипт. Помните про две ловушки в именах: googlekey для reCAPTCHA против sitekey для Turnstile, и про то, что результат читается лишь однажды.

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