Comparisons

Resolução de CAPTCHA em paralelo ou sequencial: como escolher a estratégia certa

Menos de 500 CAPTCHAs por dia? Resolva um de cada vez — é mais simples de depurar e não exige controle de concorrência.

Acima disso, resolver em paralelo reduz o tempo total de horas para minutos, ao custo de mais complexidade no código. Este guia compara as duas abordagens na prática com CaptchaAI: throughput, uso de memória, tratamento de erros e o ponto exato em que vale a pena migrar de uma para a outra.

Resposta rápida: qual modelo usar

  • Menos de 500 CAPTCHAs por dia: resolução sequencial — código mais simples, depuração direta.
  • Mais de 500 CAPTCHAs por dia: resolução paralela — o throughput sobe de ~240/hora para 10.000+/hora.
  • Depurando um fluxo novo: comece sequencial e migre depois de validar a lógica de resolução.
  • Pipeline de produção em alto volume: combine as duas — orquestração sequencial, resolução em lote paralelo (veja a abordagem híbrida mais abaixo).

Sequencial ou paralelo: comparação direta

Fator Sequencial Paralelo
Throughput (reCAPTCHA v2, mediana de 15 s) ~240/hora ~10.000+/hora
Complexidade do código Simples Moderada a complexa
Tratamento de erros Direto Exige isolamento de erros concorrentes
Uso de memória Mínimo (~30 MB) Escala com a concorrência (~100–500 MB)
Custo de API por resolução Igual Igual
Dificuldade de depuração Fácil Mais difícil (condições de corrida, timing)
Ordem dos resultados Preservada automaticamente Exige rastreamento explícito
Indicado para < 500 resoluções/dia > 500 resoluções/dia

Throughput por nível de concorrência

Resoluções simultâneas Throughput estimado/hora Memória (Python) Complexidade
1 (sequencial) 240 30 MB Baixa
10 2.400 50 MB Baixa
25 6.000 80 MB Média
50 Mais de 10.000 120 MB Média
100 18.000+ 200 MB Alta

Baseado em reCAPTCHA v2 com tempo médio de resolução de 15 s.

Se os seus workers rodam na região sa-east-1 (São Paulo) da AWS para reduzir a distância até seus usuários, vale lembrar: o tempo de resolução do CAPTCHA continua sendo o maior componente do total, não a latência de rede até o endpoint. A proximidade geográfica ajuda no RTT de cada requisição, mas o gargalo real é a concorrência disponível no seu plano — por isso a tabela acima é o ponto de partida certo para dimensionar max_concurrent, não a região do servidor.

Como funciona a resolução sequencial

Um CAPTCHA de cada vez: envia → aguarda → consulta o resultado → segue para o próximo. É o modelo mais fácil de ler em log e o que menos surpreende durante uma depuração.

# 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")

Quando vale a pena usar resolução sequencial

  • Fluxos de página única — preenchendo um formulário por vez
  • Depuração e desenvolvimento — fluxo de execução claro, fácil de acompanhar em log
  • Volume baixo — menos de 500 resoluções/dia não justifica a complexidade do paralelismo
  • Tarefas dependentes de ordem — quando o resultado de uma resolução precisa ser usado antes da próxima

Como funciona a resolução paralela

Envie todos os CAPTCHAs de uma vez e consulte os resultados simultaneamente — o ganho de tempo vem de sobrepor a espera de várias resoluções em vez de somá-las uma a uma:

# 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")

O parâmetro max_concurrent está diretamente ligado às threads do seu plano CaptchaAI: rodar max_concurrent=50 faz sentido em um plano ADVANCE (US$ 90/mês, 50 threads); ir além disso sem subir de plano só enfileira requisições que aguardam um slot livre.

Exemplo em JavaScript

// 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();
})();

Abordagem híbrida: sequencial no fluxo, paralelo na resolução

Use sequencial para orquestrar o fluxo de trabalho e paralelo apenas para a resolução do CAPTCHA:

# 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

Problemas comuns ao rodar em paralelo

Problema Causa Correção
Paralelo mais lento que o esperado Semáforo (semaphore) restritivo demais Aumente max_concurrent
Falhas aleatórias em paralelo Estado compartilhado corrompido Isole o estado por corrotina/promise
Resultados fora de ordem asyncio.gather preserva a ordem de entrada Use rastreamento por índice, se precisar
API retorna ERROR_NO_SLOT_AVAILABLE Envios simultâneos além do limite de threads do plano Adicione um pequeno atraso entre os envios

Ao investigar falhas intermitentes, é comum logar pageurl, sitekey e o resultado de cada resolução para comparar execuções. Se essas URLs carregam dados pessoais de usuários, trate esse log conforme a LGPD (ou o RGPD, para tráfego europeu): defina um período de retenção curto e evite manter esses registros além do necessário para depurar o problema.

Perguntas frequentes

Quantas threads eu preciso para rodar resolução em paralelo?

Depende do max_concurrent que você quer sustentar. O plano BASIC (US$ 15/mês, 5 threads) já permite testar o modelo paralelo em pequena escala; para os exemplos deste guia com max_concurrent=50, o plano ADVANCE (US$ 90/mês, 50 threads) é o ponto de partida — cada thread do plano corresponde a uma resolução em andamento.

A resolução em paralelo custa mais caro que a sequencial?

Não. A CaptchaAI cobra por thread, com resoluções ilimitadas por thread — não por requisição simultânea além do seu plano. O custo de API é o mesmo nos dois modelos; a diferença é o tempo total até terminar o lote.

Preciso reescrever todo o meu código para migrar de sequencial para paralelo?

Não, se a lógica de resolução já estiver isolada em uma função própria. A mudança fica restrita a envolver as chamadas em asyncio.gather (Python) ou Promise.all (JavaScript) — o resto do fluxo pode continuar igual.

Como evito resultados fora de ordem quando resolvo em paralelo?

asyncio.gather e Promise.all já preservam a ordem da lista de entrada nos resultados retornados. Se você despachar tarefas por conta própria (sem essas funções), associe cada resolução ao seu índice ou identificador original antes de enviar, e use isso para remontar a ordem depois.

Vale a pena combinar sequencial e paralelo no mesmo pipeline?

Sim, é o padrão mais comum em produção: orquestrar o fluxo (buscar URLs, extrair sitekeys, submeter formulários) de forma sequencial, e isolar só a resolução do CAPTCHA em um lote paralelo. Isso evita reescrever a orquestração inteira só para ganhar velocidade na parte que realmente é o gargalo.

Qual estratégia escolher agora

Escolha o modelo pelo seu volume diário — obtenha sua chave de API da CaptchaAI e teste as duas abordagens no seu próprio ambiente antes de decidir.

Guias relacionados:

Os comentários estão desativados para este artigo.