Comparisons

Параллельное и последовательное решение CAPTCHA: компромиссы в производительности

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

Что выбрать: экспресс-чек-лист

Прежде чем читать код, определите объём и требования вашего пайплайна:

  • До 500 решений в день — оставайтесь на последовательной модели, экономите на сложности и отладке.
  • Больше 500 решений в день или пиковые нагрузки — переходите на параллельную обработку, throughput растёт на порядок.
  • Результаты нужны строго по порядку — последовательная модель проще, даже при большом объёме.
  • Несколько сайтов или клиентов одновременно — параллельная модель с пулом соединений экономит время ожидания.

Сравнение в одной таблице

Фактор Последовательно Параллельно
Пропускная способность (reCAPTCHA v2) ~240/час ~10 000+/час
Сложность кода Простая Умеренная–высокая
Обработка ошибок Простая Нужна изоляция
Память Минимальная (~30 МБ) ~100–500 МБ
Стоимость API за решение Такая же Такая же
Отладка Проще Сложнее (гонки, тайминги)
Порядок результатов Сохраняется Нужно отслеживание
Оптимально для < 500 решений/день > 500 решений/день

Дальше — на что каждая цифра влияет в реальном пайплайне: код, память и сценарии, где параллелизм окупается уже на первой неделе.

Пример: сколько потоков нужно агентству

Агентству нужно около 6 000 решений reCAPTCHA v2 в час для нескольких клиентов. Расчёт простой:

  • 6 000 решений/час ÷ 240 (пропускная способность одного потока) ≈ 25 параллельных задач.
  • По таблице выше это уровень ADVANCE ($90/мес, 50 потоков) — с запасом на пиковую нагрузку.
  • Тарифы CaptchaAI в долларах, поэтому бюджет не «плавает» вместе с курсом, даже если счета выставляются в разных странах.

Последовательная обработка CAPTCHA

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

По одной CAPTCHA за раз: отправка → ожидание → опрос → результат → следующая задача. Каждый шаг блокирует поток выполнения, пока не придёт ответ от res.php, поэтому суммарное время растёт линейно с числом задач.

# sequential_solver.py
import os
import time
import requests

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

def solve_sequential(tasks):
    """Solve CAPTCHAs one by one."""
    results = []
    session = requests.Session()

    for task in tasks:
        # Submit
        resp = session.get("https://ocr.captchaai.com/in.php", params={
            "key": API_KEY,
            "method": "userrecaptcha",
            "googlekey": task["sitekey"],
            "pageurl": task["pageurl"],
            "json": "1",
        })
        result = resp.json()
        if result.get("status") != 1:
            results.append({"error": result.get("request")})
            continue

        task_id = result["request"]
        time.sleep(15)

        # Poll
        token = None
        for _ in range(25):
            poll = session.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get",
                "id": task_id, "json": "1",
            })
            poll_result = poll.json()
            if poll_result.get("status") == 1:
                token = poll_result["request"]
                break
            if poll_result.get("request") != "CAPCHA_NOT_READY":
                break
            time.sleep(5)

        results.append({"token": token} if token else {"error": "timeout"})

    return results

# 10 tasks sequentially → ~150 seconds total
tasks = [{"sitekey": "SITEKEY", "pageurl": "https://example.com"}] * 10
start = time.time()
results = solve_sequential(tasks)
print(f"Completed in {time.time() - start:.0f}s")

Когда стоит остаться на последовательной модели

  • Одношаговые сценарии — одна форма за проход, дополнительная сложность не нужна.
  • Отладка — линейный, предсказуемый ход выполнения проще воспроизвести по логам.
  • Небольшой объём — до 500 решений в день параллелизм не окупается: выигрыш по времени меньше, чем расходы на изоляцию ошибок.
  • Зависимость по порядку — следующий шаг требует результата предыдущего, гонки состояний тут ни к чему.

Совет: если объём растёт нерегулярно, переходите на параллельную модель заранее — не дожидайтесь первого затора в очереди.

Параллельная обработка CAPTCHA

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

Все CAPTCHA уходят в API почти одновременно, результаты опрашиваются параллельно — семафор ограничивает число одновременных запросов, чтобы не упереться в ERROR_NO_SLOT_AVAILABLE:

# parallel_solver.py
import os
import asyncio
import aiohttp

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

async def solve_one(session, sitekey, pageurl, semaphore):
    """Solve a single CAPTCHA within concurrency limits."""
    async with semaphore:
        # Submit
        async with session.get("https://ocr.captchaai.com/in.php", params={
            "key": API_KEY, "method": "userrecaptcha",
            "googlekey": sitekey, "pageurl": pageurl, "json": "1",
        }) as resp:
            result = await resp.json(content_type=None)

        if result.get("status") != 1:
            return {"error": result.get("request")}

        task_id = result["request"]
        await asyncio.sleep(15)

        # Poll
        for _ in range(25):
            async with session.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get",
                "id": task_id, "json": "1",
            }) as resp:
                poll_result = await resp.json(content_type=None)

            if poll_result.get("status") == 1:
                return {"token": poll_result["request"]}
            if poll_result.get("request") != "CAPCHA_NOT_READY":
                return {"error": poll_result.get("request")}

            await asyncio.sleep(5)

        return {"error": "timeout"}

async def solve_parallel(tasks, max_concurrent=50):
    """Solve CAPTCHAs in parallel with concurrency control."""
    semaphore = asyncio.Semaphore(max_concurrent)
    connector = aiohttp.TCPConnector(limit=max_concurrent)

    async with aiohttp.ClientSession(connector=connector) as session:
        coros = [
            solve_one(session, t["sitekey"], t["pageurl"], semaphore)
            for t in tasks
        ]
        return await asyncio.gather(*coros)

# 10 tasks in parallel → ~20 seconds total
import time
tasks = [{"sitekey": "SITEKEY", "pageurl": "https://example.com"}] * 10
start = time.time()
results = asyncio.run(solve_parallel(tasks))
print(f"Completed in {time.time() - start:.0f}s")

Пример на JavaScript

Та же логика на Node.js, с ограничением пула соединений через maxSockets:

// parallel_solver.js
const axios = require('axios');
const https = require('https');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const agent = new https.Agent({ keepAlive: true, maxSockets: 50 });
const api = axios.create({ baseURL: 'https://ocr.captchaai.com', httpsAgent: agent });

async function solveOne(sitekey, pageurl) {
  const submit = await api.get('/in.php', {
    params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: '1' },
  });
  if (submit.data.status !== 1) return { error: submit.data.request };

  await new Promise(r => setTimeout(r, 15000));

  for (let i = 0; i < 25; i++) {
    const poll = await api.get('/res.php', {
      params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
    });
    if (poll.data.status === 1) return { token: poll.data.request };
    if (poll.data.request !== 'CAPCHA_NOT_READY') return { error: poll.data.request };
    await new Promise(r => setTimeout(r, 5000));
  }
  return { error: 'timeout' };
}

(async () => {
  const tasks = Array.from({ length: 10 }, () => ({
    sitekey: 'SITEKEY', pageurl: 'https://example.com',
  }));

  const start = Date.now();
  const results = await Promise.all(tasks.map(t => solveOne(t.sitekey, t.pageurl)));
  console.log(`Completed in ${((Date.now() - start) / 1000).toFixed(0)}s`);
  console.log(`Solved: ${results.filter(r => r.token).length}/${tasks.length}`);

  agent.destroy();
})();

Совет: начинайте с max_concurrent=10 и увеличивайте постепенно, проверяя долю ошибок ERROR_NO_SLOT_AVAILABLE на каждом шаге.

Пропускная способность по уровню параллелизма

Параллельных решений Пропускная способность/час Память (Python) Сложность
1 (последовательно) 240 30 МБ Низкая
10 2 400 50 МБ Низкая
25 6 000 80 МБ Средняя
50 10 000+ 120 МБ Средняя
100 18 000+ 200 МБ Высокая

Расчёт для reCAPTCHA v2 со средним временем решения 15 секунд. Для команд, которые размещают воркеры в европейских регионах или в Казахстане, задержка сети добавляет заметный процент к времени опроса — закладывайте это в расчёт max_concurrent.

Гибридная схема: последовательно и параллельно вместе

Рабочий процесс последовательный, а решение CAPTCHA уходит в параллельный батч. Такой шаблон удобен, когда остальная логика — переходы по страницам, заполнение форм — должна остаться предсказуемой, а узкое место именно в ожидании ответа CAPTCHA API:

# Process 10 URLs sequentially, but solve their CAPTCHAs in a parallel batch
urls = get_next_batch()  # 10 URLs
captcha_params = [extract_sitekey(url) for url in urls]  # Sequential extraction
tokens = asyncio.run(solve_parallel(captcha_params, max_concurrent=10))  # Parallel solving
for url, result in zip(urls, tokens):
    submit_form(url, result.get("token"))  # Sequential submission

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

Типичные проблемы и решения

Большинство сбоев при переходе на параллельную модель сводятся к четырём причинам:

Проблема Причина Решение
Параллельный режим медленнее ожидаемого Семафор выставлен слишком строго Увеличьте max_concurrent
Случайные сбои в параллели Общее состояние повреждается Изолируйте состояние на сопрограмму/promise
Результаты не в том порядке asyncio.gather сохраняет порядок Добавьте индекс при необходимости
ERROR_NO_SLOT_AVAILABLE Слишком много одновременных отправок Добавьте задержку между запросами

Частые вопросы

Быстрые ответы на вопросы, которые чаще всего задают при переходе с одного потока на пул задач.

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

Ориентир — поток на 3–5 одновременных решений. 25 задач закрывает ADVANCE (50 потоков); для сотни и выше — CORPORATE ($240/мес, 150 потоков) или ENTERPRISE ($300/мес, 200 потоков).

Параллельная обработка стоит дороже последовательной?

Нет. Тарификация по потокам с неограниченным числом решений — цена та же, результат быстрее.

С какого объёма имеет смысл переходить на параллелизм?

Порог — около 500 решений в день. До него конкурентность выигрывает во времени, а не в деньгах.

Что делать при ERROR_NO_SLOT_AVAILABLE?

Отправки идут слишком плотно. Два быстрых исправления:

  • Добавьте небольшую задержку между вызовами in.php.
  • Снизьте max_concurrent и увеличивайте его постепенно.

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

При нестабильном соединении добавьте повтор с экспоненциальной задержкой на уровне HTTP-клиента — сам API не меняет поведение при параллельных запросах, страдает только транспортный уровень между вашим воркером и ocr.captchaai.com.

Дальнейшие шаги

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