Use Cases

Como resolver CAPTCHA em web scraping com Python

Para resolver CAPTCHA em scraping Python sem navegador, envie o desafio a uma API de resolução como a CaptchaAI e reaproveite o token na requisição seguinte. Este guia monta o fluxo com requests e BeautifulSoup, da sitekey ao tratamento de erros, e mostra quando trocar requests por Selenium.

Rodar perto do público-alvo (sa-east-1 da AWS para tráfego brasileiro) reduz a latência do polling em jobs longos. Se a coleta envolve dados pessoais, considere a LGPD — este guia assume ambiente de teste próprio ou uso autorizado.

Neste guia você vai:

  • Montar a classe CaptchaSolver reutilizável em Python.
  • Raspar um formulário protegido por reCAPTCHA, com paginação e retentativa.
  • Tratar CAPTCHA de imagem e saber quando trocar requests por Selenium.

O que você precisa antes de começar

Requisito Detalhes
Python 3.7+ Com pip
requests pip install requests
beautifulsoup4 pip install beautifulsoup4
Chave de API da CaptchaAI Obtenha em captchaai.com

Use um site próprio, um ambiente de staging ou uma coleta expressamente autorizada. Este guia não cobre automação contra sites de terceiros sem permissão.

requests ou Selenium: qual usar no scraping com CAPTCHA

  • Fique com requests + BeautifulSoup quando o formulário do site aceita um POST simples — é o caminho mais rápido e mais leve em recursos de servidor.
  • Troque para Selenium só quando o site renderiza o formulário via JavaScript pesado ou exige um clique real antes de exibir o desafio.
  • Os dois caminhos reaproveitam a mesma classe CaptchaSolver abaixo — muda apenas como você extrai a sitekey e envia o token de volta.

Classe auxiliar para resolver CAPTCHA em Python

Encapsule as chamadas à API da CaptchaAI em uma classe reutilizável:

import requests
import time

class CaptchaSolver:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base = "https://ocr.captchaai.com"

    def _submit(self, params):
        params["key"] = self.api_key
        resp = requests.get(f"{self.base}/in.php", params=params)
        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit error: {resp.text}")
        return resp.text.split("|")[1]

    def _poll(self, task_id, timeout=300):
        deadline = time.time() + timeout
        while time.time() < deadline:
            time.sleep(5)
            resp = requests.get(f"{self.base}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id
            })
            if resp.text == "CAPCHA_NOT_READY":
                continue
            if resp.text.startswith("OK|"):
                return resp.text.split("|")[1]
            raise Exception(f"Solve error: {resp.text}")
        raise TimeoutError("Solve timed out")

    def solve_recaptcha_v2(self, site_key, page_url):
        task_id = self._submit({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url
        })
        return self._poll(task_id)

    def solve_recaptcha_v3(self, site_key, page_url, action="verify"):
        task_id = self._submit({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "version": "v3",
            "action": action
        })
        return self._poll(task_id)

    def solve_turnstile(self, site_key, page_url):
        task_id = self._submit({
            "method": "turnstile",
            "sitekey": site_key,
            "pageurl": page_url
        })
        return self._poll(task_id)

    def solve_image(self, image_base64):
        task_id = self._submit({
            "method": "base64",
            "body": image_base64
        })
        return self._poll(task_id)

Como raspar um formulário protegido por reCAPTCHA

O fluxo tem cinco passos:

  1. Carregar a página com o formulário protegido.
  2. Extrair a sitekey do elemento g-recaptcha.
  3. Resolver o desafio com a classe CaptchaSolver.
  4. Enviar o formulário com o token no campo g-recaptcha-response.
  5. Ler e tratar o resultado retornado.
from bs4 import BeautifulSoup
import requests

solver = CaptchaSolver("YOUR_API_KEY")
session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})

# Step 1: Load the page
url = "https://example.com/search"
page = session.get(url)
soup = BeautifulSoup(page.text, "html.parser")

# Step 2: Extract the site key
recaptcha_div = soup.find("div", class_="g-recaptcha")
site_key = recaptcha_div["data-sitekey"]

# Step 3: Solve the CAPTCHA
token = solver.solve_recaptcha_v2(site_key, url)

# Step 4: Submit the form with the token
form_data = {
    "q": "search term",
    "g-recaptcha-response": token
}
result = session.post(url, data=form_data)

# Step 5: Parse the results
result_soup = BeautifulSoup(result.text, "html.parser")
items = result_soup.find_all("div", class_="result-item")
for item in items:
    print(item.text.strip())

Paginação: raspando várias páginas atrás de CAPTCHA

A maioria dos sites reemite um desafio a cada página. Resolva o CAPTCHA por página, com pausa entre requisições:

def scrape_all_pages(base_url, site_key, max_pages=10):
    solver = CaptchaSolver("YOUR_API_KEY")
    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    })
    all_results = []

    for page_num in range(1, max_pages + 1):
        page_url = f"{base_url}?page={page_num}"

        # Solve CAPTCHA for each page if needed
        token = solver.solve_recaptcha_v2(site_key, page_url)

        resp = session.get(page_url, params={
            "g-recaptcha-response": token,
            "page": page_num
        })

        soup = BeautifulSoup(resp.text, "html.parser")
        items = soup.find_all("div", class_="item")

        if not items:
            break

        all_results.extend([item.text.strip() for item in items])
        print(f"Page {page_num}: {len(items)} items")

        time.sleep(2)  # Polite delay

    return all_results

Como resolver CAPTCHAs de imagem no scraping

Para texto em imagem (OCR): baixe a imagem, converta para base64 e envie ao endpoint base64:

import base64

def scrape_with_image_captcha(url):
    solver = CaptchaSolver("YOUR_API_KEY")
    session = requests.Session()

    page = session.get(url)
    soup = BeautifulSoup(page.text, "html.parser")

    # Find the CAPTCHA image
    captcha_img = soup.find("img", {"id": "captcha-image"})
    captcha_url = captcha_img["src"]

    # Download and encode the image
    img_resp = session.get(captcha_url)
    img_base64 = base64.b64encode(img_resp.content).decode()

    # Solve
    captcha_text = solver.solve_image(img_base64)

    # Submit
    form_data = {
        "captcha": captcha_text,
        "username": "user"
    }
    result = session.post(url, data=form_data)
    return result.text

Retentativas e backoff para scrapers em produção

Scrapers de produção falham por rede, não só por CAPTCHA. Adicione retentativa antes do volume real:

def solve_with_retry(solver, site_key, page_url, max_retries=3):
    for attempt in range(max_retries):
        try:
            return solver.solve_recaptcha_v2(site_key, page_url)
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
            time.sleep(2)

Para volumes maiores, um fluxo assíncrono com aiohttp ajuda — veja a integração aiohttp com a API da CaptchaAI.

Exemplo real: testando o checkout antes da Black Friday

Times de QA em e-commerce brasileiro costumam rodar esse mesmo script contra o próprio ambiente de staging (https://staging.example.com/checkout) nas semanas antes da Black Friday, quando o time de segurança normalmente reforça o reCAPTCHA e o Cloudflare Turnstile no checkout para conter picos de bots. O objetivo é confirmar que o fluxo de compra segue funcionando com o desafio ativo, antes que o tráfego real chegue.

Pontos que fazem diferença nesse cenário:

  • Rode os workers perto do público-alvo (sa-east-1 da AWS) para não somar latência de rede ao tempo de resolução.
  • Teste a lógica com o plano BASIC (US$ 15/mês, 5 threads); suba para ADVANCE (US$ 90/mês, 50 threads) só quando for simular o volume real do pico.
  • Se o ambiente de staging usa dados de clientes reais copiados de produção, trate isso como dado pessoal sob a LGPD e restrinja o acesso ao mínimo necessário.

Erros comuns e como corrigir

Estes são os problemas que mais aparecem assim que o scraper vai para produção, com causa provável e correção:

Problema Causa Correção
ERROR_WRONG_USER_KEY Chave inválida Confira a chave no painel
ERROR_ZERO_BALANCE Sem saldo Recarregue os créditos
Formulário volta para a página do CAPTCHA Token expirado ou campo errado Use o token na hora; confira os nomes dos campos
ConnectionError Falha de rede Retentativa com backoff exponencial
Resultado vazio após o envio Site exige cookies/sessão Use requests.Session()

Perguntas frequentes

Preciso de Selenium para raspar sites com CAPTCHA em Python?

Nem sempre. Com POST comum, requests + CaptchaAI é mais leve que um navegador; use Selenium só quando o site exige JavaScript.

Quantas páginas dá para raspar em paralelo com um plano CaptchaAI?

Depende do plano, não do número de CAPTCHAs: BASIC (US$ 15/mês, 5 threads) cobre 5 páginas; ADVANCE (US$ 90/mês, 50 threads) escala isso sem custo extra.

hCaptcha está entre os tipos suportados pela CaptchaAI?

Não. O hCaptcha não está na lista de tipos suportados — este guia cobre reCAPTCHA v2/v3, Cloudflare Turnstile e CAPTCHA de imagem.

Preciso me preocupar com a LGPD ao raspar dados com CAPTCHA?

Se a coleta envolve dados pessoais, sim: colete só o necessário e restrinja o acesso ao que for extraído.

Preciso trocar de plano CaptchaAI antes de um pico como a Black Friday?

Depende do volume por minuto, não do número de páginas: threads limitam quantos desafios você resolve em paralelo. Migre para um plano com mais threads alguns dias antes do pico, nunca durante ele.

Como evito bloqueio por limite de requisições no scraping com CAPTCHA?

Adicione intervalos (time.sleep(2-5)), rotacione proxies e use cabeçalhos realistas. Veja rotação de proxy para scraping com CAPTCHA.

Guias relacionados

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