Integrations

Integração HTTPX + CaptchaAI

Se o seu scraper em Python já roda sobre o HTTPX, não é preciso trocar de biblioteca para lidar com reCAPTCHA e outros desafios: basta chamar a API da CaptchaAI dentro do mesmo cliente — síncrono ou assíncrono — sem sair do ecossistema HTTP/2.

Neste guia você encontra:

  • O cliente síncrono completo, com envio, polling e consulta de saldo
  • A versão assíncrona para resolver várias tarefas em paralelo com asyncio
  • Como ativar HTTP/2 e por que isso reduz o tempo de resposta no polling
  • Um exemplo de scraping que só aciona o solver quando o CAPTCHA realmente aparece

Pré-requisitos para resolver CAPTCHA com HTTPX

Antes de copiar os exemplos, confirme que o ambiente tem o essencial:

Requisito Detalhes
Python 3.8+
httpx 0.24+
Chave de API da CaptchaAI Pegue uma aqui

Dica: antes de rodar os exemplos, teste a chave com uma chamada rápida a getbalance — isso confirma que a autenticação está correta sem gastar créditos com um CAPTCHA de verdade.

pip install httpx

Cliente síncrono: resolver CAPTCHA com HTTPX passo a passo

A classe abaixo cobre o ciclo completo: envia a tarefa para o endpoint in.php, faz o polling em res.php até o token sair e ainda expõe o saldo da conta. Enquanto o desafio não é processado, a API responde CAPCHA_NOT_READY — o loop apenas aguarda e consulta de novo.

import httpx
import time
import os


class CaptchaAISync:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://ocr.captchaai.com"
        self.client = httpx.Client(timeout=30)

    def solve(self, params, timeout=300):
        params["key"] = self.api_key

        # Submit
        resp = self.client.get(f"{self.base_url}/in.php", params=params)
        text = resp.text

        if not text.startswith("OK|"):
            raise Exception(f"Submit failed: {text}")

        task_id = text.split("|")[1]

        # Poll
        deadline = time.time() + timeout
        poll_params = {"key": self.api_key, "action": "get", "id": task_id}

        while time.time() < deadline:
            time.sleep(5)
            result = self.client.get(
                f"{self.base_url}/res.php", params=poll_params
            )

            if result.text == "CAPCHA_NOT_READY":
                continue
            if result.text.startswith("OK|"):
                return result.text.split("|", 1)[1]
            raise Exception(f"Solve failed: {result.text}")

        raise TimeoutError(f"Task {task_id} timed out")

    def get_balance(self):
        resp = self.client.get(f"{self.base_url}/res.php", params={
            "key": self.api_key, "action": "getbalance"
        })
        return float(resp.text)

    def close(self):
        self.client.close()


# Usage
solver = CaptchaAISync(os.environ["CAPTCHAAI_API_KEY"])

token = solver.solve({
    "method": "userrecaptcha",
    "googlekey": "6Le-wvkS...",
    "pageurl": "https://example.com",
})
print(f"Token: {token[:50]}...")
solver.close()

Cliente assíncrono: resolvendo vários CAPTCHAs em paralelo

A versão assíncrona reaproveita a mesma lógica de envio e consulta, mas troca httpx.Client por httpx.AsyncClient — o que permite disparar várias tarefas ao mesmo tempo com asyncio.gather em vez de resolver um CAPTCHA de cada vez.

import httpx
import asyncio
import os


class CaptchaAIAsync:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://ocr.captchaai.com"
        self.client = httpx.AsyncClient(timeout=30)

    async def solve(self, params, timeout=300):
        params["key"] = self.api_key

        # Submit
        resp = await self.client.get(
            f"{self.base_url}/in.php", params=params
        )
        text = resp.text

        if not text.startswith("OK|"):
            raise Exception(f"Submit failed: {text}")

        task_id = text.split("|")[1]

        # Poll
        deadline = asyncio.get_event_loop().time() + timeout
        poll_params = {"key": self.api_key, "action": "get", "id": task_id}

        while asyncio.get_event_loop().time() < deadline:
            await asyncio.sleep(5)
            result = await self.client.get(
                f"{self.base_url}/res.php", params=poll_params
            )

            if result.text == "CAPCHA_NOT_READY":
                continue
            if result.text.startswith("OK|"):
                return result.text.split("|", 1)[1]
            raise Exception(f"Solve failed: {result.text}")

        raise TimeoutError(f"Task {task_id} timed out")

    async def get_balance(self):
        resp = await self.client.get(f"{self.base_url}/res.php", params={
            "key": self.api_key, "action": "getbalance"
        })
        return float(resp.text)

    async def close(self):
        await self.client.aclose()


# Usage
async def main():
    solver = CaptchaAIAsync(os.environ["CAPTCHAAI_API_KEY"])

    # Solve multiple concurrently
    tasks = [
        solver.solve({
            "method": "userrecaptcha",
            "googlekey": "6Le-wvkS...",
            "pageurl": f"https://example.com/page{i}",
        })
        for i in range(5)
    ]

    results = await asyncio.gather(*tasks, return_exceptions=True)
    for i, r in enumerate(results):
        if isinstance(r, Exception):
            print(f"Page {i}: FAILED - {r}")
        else:
            print(f"Page {i}: solved ({len(r)} chars)")

    await solver.close()

asyncio.run(main())

HTTP/2 no HTTPX: menos overhead ao consultar o resultado

O HTTPX suporta HTTP/2 nativamente, o que reduz a sobrecarga de conexão:

pip install httpx[http2]
client = httpx.AsyncClient(http2=True, timeout=30)

Com HTTP/2, várias requisições são multiplexadas em uma única conexão — ganho real quando o worker envia e consulta vários CAPTCHAs ao mesmo tempo. Se os workers rodam longe do tráfego que estão atendendo, a diferença aparece no tempo de polling: para tráfego brasileiro, por exemplo, um worker na região AWS sa-east-1 (São Paulo) reduz o RTT de cada chamada e deixa o loop de consulta mais responsivo, principalmente em volume alto.

Do scraping ao CAPTCHA resolvido: fluxo completo com detecção automática

O exemplo a seguir junta as duas pontas. Em três passos:

  1. Busca a página com o AsyncClient
  2. Procura a sitekey do reCAPTCHA no HTML — só aciona o solver se ela existir
  3. Reenvia o formulário com o token no campo g-recaptcha-response

Isso importa porque, em ambiente de QA ou staging, a mesma rota às vezes exibe o CAPTCHA e às vezes não.

import httpx
import re
import os

async def scrape_with_captcha(url, solver):
    async with httpx.AsyncClient() as client:
        # Fetch page
        resp = await client.get(url)
        html = resp.text

        # Check for reCAPTCHA
        match = re.search(
            r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', html
        )
        if not match:
            return html

        site_key = match.group(1)
        token = await solver.solve({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": url,
        })

        # Submit form with token
        resp = await client.post(url, data={
            "g-recaptcha-response": token,
        })
        return resp.text


async def main():
    solver = CaptchaAIAsync(os.environ["CAPTCHAAI_API_KEY"])
    content = await scrape_with_captcha("https://example.com", solver)
    print(f"Got {len(content)} chars")
    await solver.close()

asyncio.run(main())

Esse padrão evita gastar créditos resolvendo desafios que nem apareceram.

Se os dados coletados incluírem informação pessoal, trate a base com a mesma atenção que a LGPD exige de qualquer outro processamento — rode esse fluxo em ambientes autorizados (QA, staging, monitoramento com permissão).

HTTPX vs requests vs aiohttp: qual cliente usar para resolver CAPTCHA

Na prática, a escolha depende de onde está o gargalo: overhead de conexão, paralelismo ou compatibilidade com código já existente.

Recurso httpx (síncrono) httpx (assíncrono) requests aiohttp
Suporte assíncrono ❌ ✅ ❌ ✅
HTTP/2 ✅ ✅ ❌ ❌
Pool de conexões ✅ ✅ ✅ ✅
Compatibilidade de API semelhante ao requests semelhante ao requests — diferente
Mais adequado para substituição direta do requests código assíncrono moderno scripts rápidos alta concorrência

Perguntas frequentes

HTTPX no lugar do requests: vale a pena trocar?

Sim, principalmente se o projeto já lida com várias tarefas ao mesmo tempo: a API do httpx é praticamente idêntica à do requests, então a migração do código de envio e polling é rápida, e você ganha suporte assíncrono e HTTP/2 sem reescrever a lógica de resolução.

O cliente assíncrono do HTTPX resolve CAPTCHA mais rápido que o aiohttp?

Depende da carga de trabalho: o aiohttp tem overhead menor em cargas 100% assíncronas, mas o httpx leva vantagem em conexões HTTP/2 e é mais prático quando o mesmo projeto mistura chamadas síncronas e assíncronas — cenário comum em pipelines de scraping.

Quantas tarefas simultâneas eu posso enviar com o AsyncClient?

Isso depende do plano contratado na CaptchaAI, não do httpx. Cada plano libera um número fixo de threads simultâneas, por exemplo:

  • BASIC — US$ 15/mês, 5 threads
  • ADVANCE — US$ 90/mês, 50 threads

O asyncio.gather pode disparar mais tarefas do que isso, mas as excedentes ficam na fila até uma thread liberar.

Faz diferença rodar o worker perto da região do meu tráfego?

Sim. Como o polling em res.php envolve várias requisições HTTP curtas, rodar o worker perto da origem do tráfego (por exemplo, um servidor em São Paulo, região AWS sa-east-1) reduz o RTT de cada chamada e deixa o loop de consulta mais responsivo — o ganho fica mais visível em volume alto.

Preciso me preocupar com a LGPD ao usar esse fluxo em scraping?

Se os dados coletados incluírem informação pessoal, sim — a LGPD trata a coleta automatizada como qualquer outro processamento de dados. Use esse fluxo em ambientes autorizados (QA, staging, monitoramento de preços com permissão) e trate a base coletada com o mesmo cuidado que daria a qualquer outro dado pessoal.

Guias relacionados

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