Как решать капчу в Postman и опрашивать API до получения токена

solve captcha in postman - How to Solve CAPTCHA in Postman and Poll for the Token

Решать капчу в Postman можно, не написав ни строчки кода приложения. API совместим с 2captcha и работает на вашей собственной машине, так что нужно всего два запроса: отправьте задачу POST-запросом на /in.php и сохраните ID, затем опрашивайте /res.php до тех пор, пока не придёт ответ. Короткий post-response скрипт превращает второй запрос в цикл, а переменная коллекции переносит ID между ними. В этом руководстве есть точные поля, оба скрипта и та единственная настройка, которая не даёт циклу крутиться вечно.

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

  • Десктопное приложение Postman. Оно обращается к 127.0.0.1 напрямую, без всякого агента. Веб-версия разобрана ниже, ей нужна ещё одна деталь
  • Запущенный и доступный CapSkip. Local mode слушает 127.0.0.1 на порту 8080 для клиента на той же машине, а Server mode слушает ваш сетевой или публичный IP, чтобы до него мог добраться ноутбук, коллега или хостинговый раннер. Оба режима настраиваются в разделе Настройки подключения
  • sitekey и URL страницы, на которой решается капча

Больше ничего. Ни SDK, ни зависимостей, ни аккаунта. Проверка ключа по умолчанию выключена, поэтому в качестве значения ключа подойдёт любая непустая строка, а если не отправить ничего, вернётся ERROR_WRONG_USER_KEY вместо решения.

Сначала заведите три переменные коллекции

Держите их на уровне коллекции, а не в окружении. Переменная коллекции уезжает вместе с экспортом, поэтому всё продолжит работать, когда коллекцию импортирует кто-то другой.

ПеременнаяНачальное значениеЗачем
baseUrlhttp://127.0.0.1:8080Одно место, которое нужно поменять, когда решатель переедет на сервер
apiKeycapskipЛюбая непустая строка. Проверка по умолчанию отключена
captchaIdпустоСкрипт отправки её записывает, запрос опроса её читает

Запрос 1: отправляем задачу

POST-запрос на {{baseUrl}}/in.php с телом в виде формы. Выберите x-www-form-urlencoded, а не raw JSON: API читает поля формы.

КлючЗначение
key{{apiKey}}
methoduserrecaptcha
googlekeyYOUR_SITEKEY
pageurlhttps://example.com/page-with-recaptcha
json1

Поле json=1 имеет в Postman больше значения, чем в терминале. Без него ответ приходит простой строкой OK, за которой идут вертикальная черта и ID, и разбирать её приходится вручную. С ним вы получаете объект, и работает подсветка форматирования.

Добавьте это на вкладку Scripts, в раздел Post-response. В более старых версиях Postman та же вкладка называется Tests.

// Post-response script on the submit request.
const body = pm.response.json();

pm.test('task accepted', function () {
    pm.expect(body.status).to.eql(1);
});

// Hand the ID to the polling request.
pm.collectionVariables.set('captchaId', body.request);

Форма ответа одинакова для всех типов капчи. Status равен 1, когда задача принята, а нужное значение всегда лежит в поле request.

{"status": 1, "request": "2122988149"}

Следите за именем параметра. reCAPTCHA принимает googlekey, а Turnstile принимает sitekey, и именно отправка не того параметра чаще всего вызывает ответ ERROR_GOOGLEKEY. Сервис распознавания Turnstile содержит полный список полей для challenge-страниц, которым нужны ещё и значения cData и chlPageData, снятые со страницы.

Тот же запрос для остальных восьми типов

Меняется только запрос на отправку. Пять значений method покрывают каждый тип, который поддерживает CapSkip, а варианты задаются дополнительными полями в том же теле формы, а не отдельными эндпоинтами. Продублируйте Запрос 1, примените последний столбец и остальное в запросе не трогайте.

ТипЧто поставить в methodЧто ещё поменять в теле
Картинка, загруженный файлmethod = postПереключите тело на form-data и приложите картинку как file
Картинка, base64method = base64Уберите googlekey и pageurl, отправьте body с закодированной картинкой
reCAPTCHA v2 checkboxmethod = userrecaptchaНичего. Это и есть запрос, показанный выше
reCAPTCHA v2 Invisiblemethod = userrecaptchaДобавьте invisible со значением 1
reCAPTCHA Enterprisemethod = userrecaptchaДобавьте enterprise со значением 1
reCAPTCHA v3method = userrecaptchaДобавьте version со значением v3 и action
Виджет Turnstilemethod = turnstileПереименуйте googlekey в sitekey
Страница-проверка Turnstilemethod = turnstileПереименуйте googlekey в sitekey, затем добавьте data и pagedata
GeeTest v3method = geetestОтправьте gt и challenge вместо googlekey

Два из них требуют внимания именно в Postman. Загруженная картинка это единственный случай, где x-www-form-urlencoded не подходит, потому что файлу нужно multipart-тело, так что переключите именно этот запрос на form-data. А у GeeTest есть срок: значение challenge истекает примерно за минуту, поэтому получайте его непосредственно перед отправкой, а не переиспользуйте сохранённое раньше.

Запрос на опрос не меняется никогда. Именно в этом смысл собрать всё это как коллекцию: один Запрос 2 обслуживает каждый тип из списка, потому что ответ всегда приходит в одной и той же форме.

Запрос 2: опрашиваем, пока не придёт ответ

POST-запрос на {{baseUrl}}/res.php, с телом формы того же вида.

КлючЗначение
key{{apiKey}}
actionget
id{{captchaId}}
json1

Пока задача выполняется, приходит CAPCHA_NOT_READY, именно в таком написании, с пропущенной буквой. Это статус, а не ошибка, и единственная правильная реакция на него: спросить ещё раз.

// Post-response script on the polling request.
const body = pm.response.json();
const tries = Number(pm.collectionVariables.get('tries') || 0);

if (body.request === 'CAPCHA_NOT_READY' && tries < 20) {
    // Run this same request again.
    pm.collectionVariables.set('tries', tries + 1);
    pm.execution.setNextRequest(pm.info.requestId);
} else {
    pm.collectionVariables.set('captchaToken', body.request);
    pm.collectionVariables.set('tries', 0);
    pm.execution.setNextRequest(null);
}

В этом скрипте стоит отметить три вещи, потому что на каждой из них можно потерять полдня.

Цикл работает только в Collection Runner. В документации Postman прямо сказано, что setNextRequest не действует, когда вы отправляете отдельный запрос, поэтому клик по Send на запросе опроса просто отправляет его один раз, и скрипт выглядит мёртвым. Запускайте коллекцию, Postman CLI или Newman.

Счётчик не опционален. Без потолка задача, которая никогда не завершится, будет крутиться в цикле, пока вы не остановите прогон вручную. Двадцать попыток с интервалом в пять секунд дают больше ста секунд ожидания, чего с запасом хватает на те 15-20 секунд, которые нужны задаче reCAPTCHA v2.

Ссылайтесь на запрос по ID. Передача pm.info.requestId направляет цикл на текущий выполняемый запрос, поэтому переименование запроса позже не сломает цепочку незаметно.

Задайте в раннере задержку, иначе вы будете долбить эндпоинт

Скрипт выше крутится настолько быстро, насколько успевает раннер, а опрашивать задачу, которая работает всего две секунды, бессмысленно. В конфигурации прогона у Collection Runner есть поле Delay в миллисекундах, которое применяется перед каждым запросом. Поставьте 5000.

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

ТипНе готово раньше
Картинка1 секунда
reCAPTCHA v2от 15 до 20 секунд
reCAPTCHA v3От 10 до 15 секунд
GeeTest v3около 5 секунд

В Newman та же настройка задаётся флагом командной строки, поэтому коллекция, которая работает в приложении, без изменений работает и в CI.

# npm install -g newman
newman run captcha.postman_collection.json --delay-request 5000

Читаем обычный текстовый ответ

Уберите json=1, и тело вернётся текстом, что иногда как раз и нужно. Помогут две небольшие функции.

// Plain text mode: OK|2122988149
const id = pm.response.text().split('|')[1];

// Turnstile also returns the user agent, as a header.
const ua = pm.response.headers.get('X-Turnstile-User-Agent');

Этот заголовок не мелочь. Cloudflare привязывает токен Turnstile к отпечатку браузера, который его получил, поэтому токен нужно отправлять с тем же user agent, иначе сайт его отклонит, хотя сам токен совершенно валиден.

Ещё одно правило, на которое попадаются именно пользователи Postman, потому что приложение слишком уж легко позволяет кликнуть Send дважды: результат читается только один раз. Второе чтение того же ID возвращает пустоту, и выглядит это в точности как неудачное решение. Сохраняйте токен в переменную при первом чтении.

Вопрос об агенте и как направить Postman на сервер

Если вы пользуетесь веб-версией Postman, а не десктопным приложением, запросы идут через агент, и именно выбор агента решает, доступен ли 127.0.0.1 вообще. Cloud Agent работает в инфраструктуре Postman и не может дотянуться ни до чего в частной сети. Desktop Agent работает на вашей машине и может.

Как вы запускаете PostmanДотянется ли он до локального решателя?
Десктопное приложениеДа, агент не нужен
Веб-версия с Desktop AgentДа, агент маршрутизирует запросы через вашу машину
Веб-версия с Cloud AgentНет, он не видит частную сеть

Server mode меняет этот расклад. Запустите CapSkip на машине, которая слушает вашу сеть или публичный IP, поправьте переменную baseUrl, чтобы она указывала на неё, и за ней последует каждый запрос коллекции. Больше ничего не меняется, потому что поменялся только адрес.

# The collection variable is the only edit.
baseUrl = http://YOUR_SERVER_IP:8080

Рекомендуется статический публичный IP, а шаги описаны в разделе Настройки подключения. Server mode по-прежнему остаётся вашим собственным оборудованием и по-прежнему без тарификации: меняется то, где слушает решатель, а не то, кому он принадлежит.

Частые ошибки и что они означают

Что вы видитеПричинаИсправить
ERROR_WRONG_USER_KEYПоле key пришло пустым, потому что {{apiKey}} не подставилсяЗадайте apiKey на уровне коллекции, а не в окружении, которое нужно не забыть выбрать
ERROR_WRONG_METHODОпечатка в method или в значении actionИ то и другое это поля формы, а не HTTP-методы: отправка принимает поле method, а опрос принимает поле action со значением get
ERROR_GOOGLEKEYsitekey от Turnstile отправлен в userrecaptchaПриведите method в соответствие с именем поля
ERROR_PAGEURLУ URL страницы нет схемы, либо он обрезанУкажите https и используйте тело формы, а не строку запроса
Пустое тело ответаЭтот ID уже был прочитан один разСохраняйте токен при первом чтении; за новым отправьте задачу заново
Не удалось отправить запрос, соединение отклоненоПо этому адресу никто не слушаетЗапустите решатель или проверьте, что выбран Desktop Agent
Скрипт выполняется, но цикла нетВы нажали Send вместо запуска коллекцииsetNextRequest работает только при прогоне коллекции

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

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

Можно ли уложиться в один запрос?

Технически да, через pm.sendRequest в pre-request скрипте, и это редко того стоит. Песочница рассчитана на короткие скрипты, поэтому блокирующий опрос делает запрос как будто зависшим, без всякой обратной связи, пока он ждёт. Два запроса плюс раннер показывают каждую попытку, а ради этого в Postman и заходят вместо кода.

Заработает ли это в CI через Newman?

Да. setNextRequest работает в Newman и в Postman CLI ровно так же, как в приложении, поэтому экспортированная коллекция запускается без изменений. Разобраться нужно только с доступностью: у хостингового раннера свой loopback-адрес, поэтому решатель должен быть в Server mode по адресу, до которого раннер может проложить маршрут.

Есть ли лимит на количество задач в очереди?

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

Когда пора вырастать из Postman

Postman хорошо подходит, чтобы убедиться, что API работает, изучить новый тип капчи и передать коллеге то, что он сможет импортировать и запустить. Для продакшена он не годится, в основном из-за опроса: раннер каждый раз выжидает свою фиксированную задержку, тогда как официальные SDK начинают опрос с 250 миллисекунд и постепенно увеличивают интервал, поэтому быстрое решение возвращается быстро. Кроме того, они превращают строки ошибок в типизированные исключения.

Те же два вызова в shell-скрипте смотрите в разборе на cURL. В любом случае вызовы уходят на вашу собственную машину, и именно поэтому безлимитный сервис распознавания капчи стоит того, чтобы направить на него коллекцию: можно прогнать её двадцать раз, пока подбираете поля, и это не будет стоить ничего, кроме вашего же CPU.