Use Cases

Обработка CAPTCHA для автоматизации поиска в публичных записях

Сайт суда, реестр недвижимости или картотека арбитражных дел почти никогда не защищены современной CAPTCHA. За устаревшим интерфейсом почти всегда скрывается что-то простое: искажённый текст, математическая задачка или CAPTCHA собственной разработки — наследие систем, которые писали ещё до reCAPTCHA и Cloudflare Turnstile. Для автоматизации это хорошая новость: такие капчи решаются предсказуемо, без сложной настройки headless-браузера — через обычный image/OCR API.

Ниже — рабочий процесс на Python и JavaScript, который решает CAPTCHA на разных типах порталов публичных записей: от поиска судебных дел до выписок по объектам недвижимости и юрлицам.

Какие CAPTCHA стоят на порталах публичных записей

Тип CAPTCHA обычно зависит не от штата или ведомства, а от того, когда портал в последний раз переписывали. Типичная картина по категориям:

Категория портала Типичная CAPTCHA Пример задачи
Поиск судебных дел CAPTCHA собственной разработки (текст) Искажённые буквы и цифры, 5–6 символов
Записи об объектах недвижимости (округ) Математическая CAPTCHA «Сколько будет 4 + 7?»
Поиск юрлиц и ИП Image CAPTCHA (текст) Искажённые буквы с линейным шумом
Записи актов гражданского состояния reCAPTCHA v2 Выбор нужных изображений в сетке
Разрешения на строительство Простая текстовая CAPTCHA 4-значный цифровой код
Документы по обеспечительным обязательствам (UCC) CAPTCHA собственной разработки (OCR) Буквы разного регистра на зашумлённом фоне

Что передавать в API для государственных порталов

Прежде чем идти в код: у image/OCR метода CaptchaAI есть несколько параметров, которые стоит выставить осознанно уже на первом вызове — иначе первые тесты уйдут на подбор base64 против post и угадывание language.

Параметр Значение Когда использовать
method base64 Изображение уже загружено в байтах
method post Отправляете файл изображения напрямую
language 0 Латинский/цифровой текст CAPTCHA
numeric 1 CAPTCHA состоит только из цифр
min_len / max_len зависит от портала Когда известна длина кода
textinstructions произвольная инструкция Математические CAPTCHA или нестандартный формат

Поиск по судебным делам с решением CAPTCHA

Базовый сценарий — класс на Python, который сначала загружает страницу поиска, чтобы получить сессионные cookie и, если нужно, картинку CAPTCHA, а затем решает её и отправляет форму:

import requests
import base64
import time
from urllib.parse import urljoin

class PublicRecordsSearcher:
    def __init__(self, api_key):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def search_court_records(self, portal_url, case_number):
        """Search court records, solving image CAPTCHAs as needed."""
        # Load the search page
        page = self.session.get(f"{portal_url}/search")

        # Extract CAPTCHA image
        captcha_img_url = self._extract_captcha_url(page.text, portal_url)
        if not captcha_img_url:
            # No CAPTCHA on this page
            return self._submit_search(portal_url, case_number)

        # Download and solve CAPTCHA
        img_response = self.session.get(captcha_img_url)
        captcha_text = self._solve_image_captcha(img_response.content)

        # Submit search with solved CAPTCHA
        return self._submit_search(portal_url, case_number, captcha_text)

    def _extract_captcha_url(self, html, base_url):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        # Look for common CAPTCHA image patterns
        captcha_img = (
            soup.find("img", {"id": "captchaImage"}) or
            soup.find("img", {"class": "captcha"}) or
            soup.find("img", attrs={"src": lambda s: s and "captcha" in s.lower()})
        )

        if captcha_img and captcha_img.get("src"):
            return urljoin(base_url, captcha_img["src"])
        return None

    def _solve_image_captcha(self, image_bytes):
        img_base64 = base64.b64encode(image_bytes).decode("utf-8")

        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key,
            "method": "base64",
            "body": img_base64,
            "json": 1
        })
        task_id = resp.json()["request"]

        for _ in range(30):
            time.sleep(3)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            })
            data = result.json()
            if data["status"] == 1:
                return data["request"]

        raise TimeoutError("CAPTCHA solve timed out")

    def _submit_search(self, portal_url, case_number, captcha_text=None):
        form_data = {"caseNumber": case_number}
        if captcha_text:
            form_data["captcha"] = captcha_text

        response = self.session.post(
            f"{portal_url}/search/results",
            data=form_data
        )
        return response.text

# Usage
searcher = PublicRecordsSearcher("YOUR_API_KEY")
results = searcher.search_court_records(
    "https://courts.example.gov",
    "2024-CV-12345"
)

Если на портале капчи нет вовсе, _extract_captcha_url вернёт None, и класс отправит поиск без лишнего шага. Про это стоит подумать заранее: часть порталов включает CAPTCHA только при подозрительном трафике, а не на каждый запрос, и жёстко ожидать картинку на каждой странице — ошибка.

Математические CAPTCHA: тот же движок, другой промпт

Часть окружных порталов вместо искажённого текста показывает арифметику — как правило, это тот же самый image CAPTCHA механизм, просто с картинкой вида «4 + 7 = ?». CaptchaAI решает такие задачи как обычное распознавание текста, если явно указать это в textinstructions:

def solve_math_captcha(self, image_bytes):
    """Solve math CAPTCHAs like '4 + 7 = ?'"""
    img_base64 = base64.b64encode(image_bytes).decode("utf-8")

    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": self.api_key,
        "method": "base64",
        "body": img_base64,
        "textinstructions": "solve the math equation and return only the number",
        "json": 1
    })
    task_id = resp.json()["request"]

    # Poll for result
    for _ in range(30):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": self.api_key,
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]

    raise TimeoutError("Math CAPTCHA solve timed out")

Агрегация поиска по нескольким порталам (JavaScript)

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

class RecordsAggregator {
  constructor(apiKey) {
    this.apiKey = apiKey;
  }

  async searchAcrossPortals(query, portals) {
    const results = [];

    for (const portal of portals) {
      try {
        const data = await this.searchPortal(portal, query);
        results.push({ portal: portal.name, records: data });
      } catch (error) {
        results.push({ portal: portal.name, error: error.message });
      }
    }

    return results;
  }

  async searchPortal(portal, query) {
    const pageResponse = await fetch(portal.searchUrl);
    const html = await pageResponse.text();

    // Check for image CAPTCHA
    const captchaMatch = html.match(/captcha[^"]*\.(?:png|jpg|gif)/i);
    let captchaAnswer = null;

    if (captchaMatch) {
      const imgUrl = new URL(captchaMatch[0], portal.searchUrl).href;
      const imgData = await fetch(imgUrl);
      const buffer = await imgData.arrayBuffer();
      const base64 = Buffer.from(buffer).toString('base64');

      captchaAnswer = await this.solveImageCaptcha(base64);
    }

    // Submit search
    const formData = new URLSearchParams({ q: query });
    if (captchaAnswer) formData.append('captcha', captchaAnswer);

    const response = await fetch(portal.searchUrl, {
      method: 'POST',
      body: formData
    });

    return response.text();
  }

  async solveImageCaptcha(base64Image) {
    const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
      method: 'POST',
      body: new URLSearchParams({
        key: this.apiKey,
        method: 'base64',
        body: base64Image,
        json: '1'
      })
    });

    const { request: taskId } = await submitResp.json();

    for (let i = 0; i < 30; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const result = await fetch(
        `https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const data = await result.json();
      if (data.status === 1) return data.request;
    }

    throw new Error('CAPTCHA solve timed out');
  }
}

// Usage
const aggregator = new RecordsAggregator('YOUR_API_KEY');
const results = await aggregator.searchAcrossPortals('Smith LLC', [
  { name: 'State Business Registry', searchUrl: 'https://sos.example.gov/search' },
  { name: 'County Court Records', searchUrl: 'https://courts.example.gov/search' }
]);

Обратите внимание на try/catch вокруг каждого searchPortal: один упавший портал не должен останавливать проверку остальных — в массив результатов просто попадёт запись об ошибке вместо разорванного Promise.all.

Локальный пример: поиск по российским реестрам

Логика из примеров выше не завязана на американские county-порталы. С тем же подходом парсятся kad.arbitr.ru (картотека арбитражных дел), выписки из ЕГРЮЛ/ЕГРИП на сайте ФНС и сервисы Росреестра: везде встречается либо искажённый текст, либо математическая CAPTCHA, либо reCAPTCHA v2 на старых формах. Разница обычно в разметке HTML страницы и в том, куда конкретно подставлять решённый токен, — сам вызов in.php/res.php не меняется.

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

Типичные проблемы при работе с CAPTCHA на госпорталах

Большая часть проблем в проде связана не с точностью распознавания, а с сессиями и повторными запросами:

Проблема Причина Решение
Картинка CAPTCHA возвращает 403 Нет сессионной cookie Сначала загрузите страницу поиска, затем запрашивайте картинку в той же сессии
CaptchaAI вернул неверный ответ Низкое качество изображения Предобработайте картинку: увеличьте контраст, уберите шум
CAPTCHA обновляется при отправке формы Истёк токен формы Извлекайте скрытые поля формы вместе с картинкой CAPTCHA и отправляйте их одним запросом
После решения CAPTCHA поиск возвращает пустую страницу Редирект после POST потерял cookie Используйте allow_redirects=True и один и тот же requests.Session() на весь сценарий
На одном портале чередуются разные типы CAPTCHA Портал переключает вендора или включает reCAPTCHA только при подозрительном трафике Определяйте тип CAPTCHA на странице перед вызовом API, а не жёстко привязывайтесь к одному методу

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

Почему на государственных порталах до сих пор стоит капча из нулевых?

Большинство таких систем разрабатывались до reCAPTCHA и Cloudflare Turnstile, и с тех пор их почти не трогали: бюджет на переписывание legacy-системы государственный заказчик выделяет нечасто. Поэтому вместо современной защиты на форме остаётся тот же image CAPTCHA или математическая задачка, что и десять лет назад.

Сколько времени занимает решение CAPTCHA на портале суда или реестра?

Для image CAPTCHA типичное время решения — менее 0,5 секунды. Цикл из 30 попыток по 3 секунды в примере выше — это запас на сетевые задержки и очередь на стороне API, а не реальное время ответа; на практике результат почти всегда приходит на первом-втором опросе res.php.

Нужно ли предобрабатывать изображение CAPTCHA перед отправкой?

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

Что делать, если после решения CAPTCHA поиск возвращает пустую страницу?

Почти всегда это потерянная сессия: редирект после POST ушёл без сохранённых cookie. Держите один объект сессии (в Python — requests.Session()) на весь сценарий, от загрузки страницы поиска до получения результатов, и явно указывайте allow_redirects=True.

Можно ли использовать один API-ключ CaptchaAI для нескольких порталов одновременно?

Да — один ключ обслуживает все порталы сразу, а число одновременных CAPTCHA ограничено только количеством потоков в тарифе. План BASIC даёт 5 потоков за $15/мес с неограниченным числом решений на поток; для параллельного опроса десятков реестров подойдут более высокие тарифы, вплоть до VIP-3 (5 000 потоков, $7,500/мес).

Итог: с чего начать

Автоматизация поиска по публичным записям — это в первую очередь дисциплина сессий и повторных запросов, а решение самой CAPTCHA сводится к одному вызову API. Получите API-ключ CaptchaAI и подключите его к пайплайну поиска по нужным вам порталам.


Материалы по теме

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