API Tutorials

Решите CAPTCHA изображений с помощью Node.js и CaptchaAI

Картинка с искажённым текстом решается двумя HTTP-запросами: вы отправляете изображение на in.php и забираете распознанную строку с res.php. Никакого распознавания на своей стороне, никакой обученной модели и никакого браузерного плагина — только axios и API-ключ. Ниже разобраны оба способа отправки, опрос результата, параметры, которые заметно поднимают точность, и таблица ошибок, на которых обычно застревает первая интеграция.

Такой формат CAPTCHA до сих пор массово живёт там, где интерфейсы не переписывали годами: государственные порталы, старые CRM и биллинги, формы регистрации на региональных площадках, визовые сервисы вроде BLS. Для команд из России, Беларуси и Казахстана это самый частый сценарий — не reCAPTCHA v2 на современном SPA, а старая картинка в форме, которую нужно проходить в автотестах или при легальном сборе открытых данных.


Что понадобится перед стартом

Элемент Значение
CaptchaAI API-ключ Из личного кабинета на captchaai.com
Node.js 14+
Библиотеки axios, fs
Формат изображения JPG, PNG или GIF (100 байт – 100 КБ)

Ключ берётся в панели управления сразу после регистрации, отдельной заявки на доступ к OCR-методу не требуется. Тарификация здесь потоковая, а не за решение: BASIC ($15/мес, 5 потоков) держит пять одновременных задач, ADVANCE ($90/мес, 50 потоков) — пятьдесят. Для парсера, который распознаёт картинки последовательно в один воркер, стартового тарифа хватает с запасом; масштабируется не число решений, а число параллельных запросов. Для агентства, которое выставляет счета в волатильной локальной валюте, фиксированный месячный платёж в USD предсказуемее поштучной оплаты.


Способ А: отправка в base64

Основной вариант, когда картинка уже в памяти — например, вы только что сняли скриншот элемента через Puppeteer и не хотите класть файл на диск.

const axios = require('axios');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// Read and encode the image
const imageB64 = fs.readFileSync('captcha.png').toString('base64');

// Submit to CaptchaAI
const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: API_KEY,
    method: 'base64',
    body: imageB64,
    json: 1,
  },
});

if (submitData.status !== 1) throw new Error(submitData.request);
const taskId = submitData.request;
console.log(`Task submitted: ${taskId}`);

Ответ с status: 1 возвращает ID задачи в поле request — сохраните его, дальше опрос идёт по нему.


Способ Б: загрузка файлом через multipart

Если изображение лежит на диске или пришло потоком, form-data избавляет от лишнего кодирования и заметно уменьшает объём запроса.

const FormData = require('form-data');

const form = new FormData();
form.append('key', API_KEY);
form.append('method', 'post');
form.append('json', '1');
form.append('file', fs.createReadStream('captcha.png'));

const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', form, {
  headers: form.getHeaders(),
});

const taskId = submitData.request;

Опрос res.php: как забрать распознанный текст

Результат не приходит синхронно. Дайте первую паузу примерно в 5 с, затем опрашивайте res.php с тем же интервалом. Ответ CAPCHA_NOT_READY — это норма, а не ошибка: задача ещё в работе. Любая другая строка в request означает отказ, и цикл нужно прерывать, иначе воркер будет молча крутиться до конца лимита итераций.

await sleep(5000);

let captchaText;
for (let i = 0; i < 30; i++) {
  const { data: pollData } = await axios.get('https://ocr.captchaai.com/res.php', {
    params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
  });

  if (pollData.status === 1) {
    captchaText = pollData.request;
    console.log(`CAPTCHA text: ${captchaText}`);
    break;
  }
  if (pollData.request !== 'CAPCHA_NOT_READY') {
    throw new Error(pollData.request);
  }
  await sleep(5000);
}

На нестабильном мобильном канале имеет смысл увеличить общий тайм-аут цикла, а не частоту опроса: короткий интервал не ускоряет решение, зато повышает шанс упереться в ограничение частоты запросов.


Параметры, которые повышают точность

Самая дешёвая оптимизация — заранее сообщить API, что именно изображено на картинке. Если вы знаете, что в форме всегда четыре–шесть цифр, ограничьте алфавит и длину: это снижает долю неверных прочтений на шумных изображениях.

Параметр Значение Назначение
numeric 1 = цифры, 2 = буквы Ограничивает алфавит
min_len / max_len Целое число Ограничения длины
calc 1 Вычисляет математическое выражение
regsense 1 С учётом регистра

Перед тем как выставлять эти параметры, зафиксируйте три вещи по десятку реальных картинок с целевой формы:

  • какой алфавит встречается — только цифры, только буквы или смесь;
  • сохраняется ли длина строки от запроса к запросу;
  • важен ли регистр при проверке на стороне сайта.

Если длина плавает, min_len/max_len лучше не задавать вовсе: слишком узкие границы отбраковывают верное прочтение и задача уходит в повтор.

// Digits only, 4-6 characters
const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: API_KEY,
    method: 'base64',
    body: imageB64,
    numeric: 1,      // digits only
    min_len: 4,       // minimum length
    max_len: 6,       // maximum length
    json: 1,
  },
});

Комментарии в примере оставлены на английском намеренно — так проще сверяться с официальной документацией по параметрам.


Полный рабочий пример: скриншот, решение, отправка формы

Сценарий целиком: Puppeteer открывает страницу регистрации в staging-окружении, снимает элемент с картинкой, отдаёт его в CaptchaAI, дожидается текста и подставляет его в поле формы.

const axios = require('axios');
const puppeteer = require('puppeteer');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function solveImageCaptcha() {
  // 1. Load page and screenshot CAPTCHA
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com/register');

  const captchaEl = await page.$('#captcha-image');
  await captchaEl.screenshot({ path: 'captcha.png' });

  // 2. Encode and submit
  const imageB64 = fs.readFileSync('captcha.png').toString('base64');
  const { data: submit } = await axios.post('https://ocr.captchaai.com/in.php', null, {
    params: { key: API_KEY, method: 'base64', body: imageB64, json: 1 },
  });
  const taskId = submit.request;

  // 3. Poll for text
  await sleep(5000);
  let text;
  for (let i = 0; i < 30; i++) {
    const { data: poll } = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
    });
    if (poll.status === 1) { text = poll.request; break; }
    if (poll.request !== 'CAPCHA_NOT_READY') throw new Error(poll.request);
    await sleep(5000);
  }

  // 4. Type and submit
  await page.type('#captcha-input', text);
  await page.click('form [type="submit"]');
  console.log(`Solved: ${text}`);
  await browser.close();
}

solveImageCaptcha().catch(console.error);

Ожидаемый вывод в консоли:

Solved: ABC123

Если в том же пайплайне попадаются современные проверки — reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3, — логика меняется: там возвращается не строка, а токен, который подставляется в скрытое поле формы перед отправкой. Для картинки же результат распознавания уходит прямо в видимый input, как в примере выше. Собирая данные вместе с формами, помните про 152-ФЗ «О персональных данных»: обрабатывайте только то, что вы вправе обрабатывать, и не сохраняйте лишнего в логах воркера.


Ошибки, на которых спотыкается первая интеграция

Ошибка Причина Что делать
ERROR_WRONG_FILE_EXTENSION Неподдерживаемый формат Используйте JPG, PNG или GIF
ERROR_TOO_BIG_CAPTCHA_FILESIZE Изображение > 100 КБ Сожмите перед отправкой
ERROR_ZERO_CAPTCHA_FILESIZE Изображение < 100 байт Проверьте, что скриншот не пустой
CAPCHA_NOT_READY Задача ещё решается Продолжайте опрос каждые 5 с

Отдельно проверьте селектор элемента: пустой скриншот на 0 байт даёт ERROR_ZERO_CAPTCHA_FILESIZE чаще, чем реальные проблемы с форматом файла.


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

Нужно ли ставить Puppeteer, если картинка доступна по прямой ссылке?

Нет. Скачайте файл обычным axios-запросом и отправьте его способом Б. Браузер нужен только тогда, когда изображение генерируется под конкретную сессию и без cookie не открывается.

Что делать, если текст распознан неверно?

Вызовите https://ocr.captchaai.com/res.php?key=KEY&action=reportbad&id=TASK_ID — задача помечается как неудачная. Заодно проверьте numeric, min_len и regsense: чаще всего проблема в том, что алфавит не ограничен и OCR путает 0 с O.

Сколько потоков нужно для парсера с очередью?

Считайте по числу одновременных задач, а не по объёму за сутки. Один воркер — один поток, восемь параллельных — тариф от восьми потоков и выше. Сами решения внутри тарифа отдельно не тарифицируются.

Какие ещё типы проверок закрывает тот же API-ключ?

Тем же ключом решаются reCAPTCHA v2 и v3, Cloudflare Turnstile и Cloudflare Challenge, GeeTest v3, текстовые и grid-задачи, а в beta-режиме — CaptchaFox (beta), Friendly Captcha (beta) и Lemin (beta). GeeTest v4 заявлен как «скоро». Список поддерживаемых типов стоит сверить перед тем, как закладывать его в архитектуру парсера.


Связанные руководства


Получите API-ключ и распознайте первую картинку CAPTCHA →

Комментарии для этой статьи отключены.