API Tutorials

Решите BLS CAPTCHA с помощью Node.js и CaptchaAI

Ответ на BLS CAPTCHA — это не токен, а список номеров ячеек, и от этого зависит вся схема интеграции. Со страницы снимаются девять картинок сетки в base64 и числовой код инструкции, всё уходит на in.php, результат забирается с res.php, а клики по возвращённым ячейкам выполняет уже ваш браузерный код. Рабочий скрипт на Node.js и CaptchaAI, который проходит этот путь от начала до конца без собственного разбора изображений, — ниже.

Отдельный метод bls нужен именно из-за формы ответа. У reCAPTCHA или Cloudflare Turnstile решение подставляется в скрытое поле формы одной строкой; здесь такого поля нет, а приходит массив индексов, который надо ещё «проиграть» кликами по сетке.


Как устроена сетка BLS

Девять изображений нумеруются слева направо и сверху вниз:

1 | 2 | 3
---------
4 | 5 | 6
---------
7 | 8 | 9

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

По внутренним метрикам само распознавание сетки укладывается в <1 с. Остальное время в примере ниже — это пауза перед первым опросом и интервал между попытками.


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

Элемент Значение
CaptchaAI API-ключ из личного кабинета на captchaai.com
Node.js 14+
Библиотека axios (npm install axios)

Puppeteer здесь отвечает только за браузер: открыть страницу, вытащить картинки и кликнуть. Если у вас уже настроен Playwright, меняются вызовы автоматизации, а часть с API остаётся дословно той же.


Шаг 1. Соберите изображения сетки

Со страницы снимаются код инструкции и девять src ячеек. Часть порталов отдаёт картинки сразу в data:-URL, часть — обычными ссылками, поэтому второй случай догружается через axios и переводится в base64 вручную.

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

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/bls-form');

// Get instruction code
const instruction = await page.$eval('.bls-instruction', (el) => el.textContent.trim());

// Get all 9 cell image URLs and convert to base64
const cellImages = await page.$$eval('.bls-grid img', (imgs) =>
  imgs.map((img) => img.src)
);

const images = [];
for (const src of cellImages) {
  if (src.startsWith('data:')) {
    images.push(src);
  } else {
    const { data } = await axios.get(src, { responseType: 'arraybuffer' });
    const b64 = Buffer.from(data).toString('base64');
    images.push(`data:image/png;base64,${b64}`);
  }
}

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


Шаг 2. Отправьте задачу в CaptchaAI

Изображения уходят как image_base64_1image_base64_9, код инструкции — в поле instructions, метод — bls. Флаг json: '1' включает разбираемый ответ вместо текстовой строки.

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

const params = new URLSearchParams({
  key: API_KEY,
  method: 'bls',
  instructions: instruction,
  json: '1',
});

// Add all 9 images
images.forEach((img, i) => {
  params.append(`image_base64_${i + 1}`, img);
});

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

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

Ответ с status: 1 содержит ID задачи в поле request — его и передают дальше. API-ключ держите в переменной окружения, а не в исходнике: девять base64-картинок в теле запроса делают такие POST-запросы заметными в логах прокси и CI.


Шаг 3. Опросите результат

await sleep(5000);

let selectedCells;
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) {
    selectedCells = JSON.parse(pollData.request);
    console.log('Selected cells:', selectedCells);
    break;
  }
  if (pollData.request !== 'CAPCHA_NOT_READY') {
    throw new Error(pollData.request);
  }
  await sleep(5000);
}

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


Шаг 4. Кликните по нужным ячейкам

// Click each identified cell
const gridCells = await page.$$('.bls-grid img');
for (const cellNum of selectedCells) {
  await gridCells[cellNum - 1].click();
}

// Submit the form
await page.click('.bls-submit');
console.log(`Solved: clicked cells ${JSON.stringify(selectedCells)}`);
await browser.close();

Индексы приходят с единицы, а массив элементов в браузере — с нуля, отсюда и cellNum - 1. В консоли успешный прогон выглядит так:

Selected cells: [1, 4, 7, 8]
Solved: clicked cells [1,4,7,8]

Ошибки API и что с ними делать

Ошибка Причина Что делать
ERROR_BAD_PARAMETERS не хватает изображений или кода инструкции отправьте все девять картинок и поле instructions
CAPCHA_NOT_READY задача ещё обрабатывается продолжайте опрос с интервалом 5 с
ERROR_ZERO_BALANCE на аккаунте нет средств пополните баланс CaptchaAI

Отдельно стоит логировать пару «код инструкции → возвращённые ячейки»: если форма поменяет разметку сетки, это первое место, где расхождение станет видно.


Сколько потоков заложить

Тарификация у CaptchaAI идёт по потокам, а не по количеству решений: поток — это одна задача «в полёте», и как только она завершилась, он берёт следующую. Одиночному скрипту, который обрабатывает одну форму за раз, хватает BASIC ($15/мес, 5 потоков). Команде, которая параллельно прогоняет несколько окружений QA, ближе STANDARD ($30/мес, 15 потоков).

Практический ориентир: очередь из 200 форм в час при полном цикле около 10 с загружает меньше одного потока — упирается всё обычно не в решение, а в скорость самой страницы. Фиксированная месячная сумма в USD при этом удобна тем, кто планирует расходы заранее: счёт не меняется от того, сколько сеток вы разобрали за месяц.


Сценарий: ночной регрессионный прогон формы записи

Чаще всего этот скрипт оседает в CI у команды, которая сопровождает форму записи в визовый центр: после каждого релиза надо проверить вёрстку на мобильном разрешении, валидацию полей и поведение при тайм-ауте. Сетка BLS — единственный шаг такой формы, который не автоматизируется штатными средствами Playwright или Puppeteer, и именно он ломает ночной регрессионный прогон.

Рабочая схема выглядит так:

  1. Поднимите стенд staging с той же разметкой (.bls-grid, .bls-instruction), что и боевая форма.
  2. Прогоните по нему сценарий из этого руководства и сохраните пары «код инструкции → выбранные ячейки».
  3. Сравните долю успешных решений между релизами — падение почти всегда означает изменение разметки, а не проблему на стороне API.

Персональные данные заявителей в такие логи не пишите: храните только технические поля. Требования 152-ФЗ «О персональных данных» распространяются и на тестовые контуры, если в них попадают реальные записи, — это вопрос вашей внутренней процедуры, а не настроек сервиса решения CAPTCHA.


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

Что передавать в instructions, если код многозначный?

Передавайте код целиком, одной строкой, ровно так, как он показан на странице: 664, а не три отдельные цифры. Разбивать его не нужно.

Приходит ERROR_BAD_PARAMETERS, хотя все картинки отправлены. Почему?

Чаще всего одна из ячеек ушла пустой строкой: src подгружался лениво, и на момент чтения картинка ещё не появилась. Дождитесь всех девяти элементов и проверьте длину массива перед отправкой.

Можно ли отправить ссылки на изображения вместо base64?

Нет, метод bls работает с содержимым картинок. Ячейки внутри приватной сессии портала недоступны стороннему серверу по прямой ссылке — поэтому их и переводят в base64 на той же странице.

Что делать, если на том же портале стоит hCaptcha?

Через CaptchaAI такую проверку пройти не получится: hCaptcha не входит в список поддерживаемых типов. Доступны reCAPTCHA v2 и v3, Cloudflare Turnstile и Cloudflare Challenge, GeeTest v3, а также image/OCR, grid-image и BLS.

Как ускорить полный цикл?

Сократите интервал опроса и уберите фиксированную паузу перед первой попыткой там, где она не нужна: распознавание сетки укладывается в <1 с, а большая часть времени в примере — это ожидание между запросами к res.php.


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


Подключите CaptchaAI и разберите сетку BLS в своём скрипте →

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