Use Cases

Envio automatizado de formulário com tratamento CAPTCHA

O robô preenche todos os campos certinho — e trava no botão de enviar, porque o formulário exige um desafio CAPTCHA que o Selenium sozinho não resolve. Este guia junta detecção automática de reCAPTCHA v2, Cloudflare Turnstile e CAPTCHA de imagem com a API da CaptchaAI em um único fluxo, para que testes de QA, cadastros e formulários de contato sejam enviados sem intervenção manual.


Por que o CAPTCHA trava a automação de formulários

Formulários protegidos por CAPTCHA são o ponto onde a maior parte das automações em Selenium falha: o script simula cliques e digitação sem problema, mas não tem como "ver" um desafio visual e devolver a resposta no formato que o backend espera. Isso afeta qualquer fluxo que dependa de envios repetidos — testes de regressão em formulários de contato, criação de contas de teste, cadastros validados em ambiente de staging.

No Brasil, esse cenário aparece com frequência em times de QA que testam formulários de cadastro com campos sensíveis (CPF, e-mail, endereço) antes de liberar para produção. Vale lembrar que a LGPD trata esses campos como dados pessoais mesmo em ambiente de teste — use sempre dados fictícios e endpoints internos (staging), nunca informações reais de terceiros.

A solução não é simular alguém digitando devagar: é combinar a detecção do tipo de CAPTCHA presente na página com uma chamada à API de resolução, injetando o token correto antes do envio.


Arquitetura do fluxo

O processo segue quatro etapas, do carregamento da página ao envio confirmado:

┌────────────┐     ┌──────────────┐     ┌────────────┐     ┌──────────────┐
│ Load Form  │────▶│ Fill Fields  │────▶│ Detect &   │────▶│ Submit Form  │
│ (Selenium) │     │              │     │ Solve      │     │              │
│            │     │              │     │ CAPTCHA    │     │              │
└────────────┘     └──────────────┘     └────────────┘     └──────────────┘
  1. Carrega o formulário no navegador controlado pelo Selenium.
  2. Preenche os campos de texto, seleção e senha com os valores esperados.
  3. Detecta o tipo de CAPTCHA presente e resolve pela API da CaptchaAI.
  4. Injeta o token e envia o formulário, validando a URL de retorno.

Peças que compõem a automação

A automação se divide em três componentes que se complementam: um solver que fala com a API da CaptchaAI, um detector que identifica qual CAPTCHA está na página, e um automator que orquestra o preenchimento e o envio do formulário.

Resolver o CAPTCHA. Essa classe cuida do ciclo completo: envia o desafio para in.php, aguarda o tempo inicial de processamento e consulta res.php a cada 5 segundos até receber o token ou estourar o tempo limite.

import time
import requests


class FormCaptchaSolver:
    BASE = "https://ocr.captchaai.com"

    def __init__(self, api_key):
        self.api_key = api_key

    def solve(self, params, initial_wait=10):
        params["key"] = self.api_key
        params["json"] = 1
        resp = requests.post(f"{self.BASE}/in.php", data=params).json()
        if resp["status"] != 1:
            raise Exception(f"Submit error: {resp['request']}")

        task_id = resp["request"]
        time.sleep(initial_wait)

        for _ in range(60):
            result = requests.get(
                f"{self.BASE}/res.php",
                params={"key": self.api_key, "action": "get", "id": task_id, "json": 1},
            ).json()
            if result["request"] == "CAPCHA_NOT_READY":
                time.sleep(5)
                continue
            if result["status"] == 1:
                return result["request"]
            raise Exception(f"Solve error: {result['request']}")
        raise TimeoutError("CAPTCHA solve timed out")

Detectar o tipo de CAPTCHA. O detector varre o HTML renderizado à procura de três assinaturas: o seletor .cf-turnstile (Turnstile), o atributo data-sitekey combinado com a palavra "recaptcha" no HTML (reCAPTCHA v2) e uma tag <img> com "captcha" no src ou na classe (CAPTCHA de imagem).

import re
from selenium.webdriver.common.by import By


class CaptchaDetector:
    def __init__(self, driver):
        self.driver = driver

    def detect(self):
        """Detect CAPTCHA type on current page."""
        html = self.driver.page_source

        # Turnstile
        turnstile = self.driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile, [data-sitekey]")
        for el in turnstile:
            if "cf-turnstile" in (el.get_attribute("class") or ""):
                return "turnstile", el.get_attribute("data-sitekey")

        # reCAPTCHA
        recaptcha = self.driver.find_elements(By.CSS_SELECTOR, "[data-sitekey]")
        if recaptcha:
            sitekey = recaptcha[0].get_attribute("data-sitekey")
            if "recaptcha" in html.lower():
                return "recaptcha_v2", sitekey

        # Image CAPTCHA
        img = self.driver.find_elements(By.CSS_SELECTOR, "img[src*='captcha'], img.captcha")
        if img:
            return "image", img[0].get_attribute("src")

        return "none", None

Orquestrar o preenchimento e o envio. O FormAutomator amarra as duas classes anteriores: preenche os campos, chama o detector, resolve o CAPTCHA identificado e injeta o token via execute_script antes de clicar em enviar.

import base64
import requests as req
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class FormAutomator:
    def __init__(self, api_key):
        self.solver = FormCaptchaSolver(api_key)
        self.driver = webdriver.Chrome()
        self.detector = CaptchaDetector(self.driver)

    def fill_field(self, selector, value):
        field = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, selector))
        )
        field.clear()
        field.send_keys(value)

    def select_option(self, selector, value):
        from selenium.webdriver.support.ui import Select
        select = Select(self.driver.find_element(By.CSS_SELECTOR, selector))
        select.select_by_value(value)

    def solve_captcha(self):
        captcha_type, data = self.detector.detect()
        page_url = self.driver.current_url

        if captcha_type == "recaptcha_v2":
            token = self.solver.solve({
                "method": "userrecaptcha",
                "googlekey": data,
                "pageurl": page_url,
            })
            self.driver.execute_script(
                f'document.querySelector("[name=g-recaptcha-response]").value = "{token}";'
            )
            return True

        if captcha_type == "turnstile":
            token = self.solver.solve({
                "method": "turnstile",
                "sitekey": data,
                "pageurl": page_url,
            })
            self.driver.execute_script(
                f'document.querySelector("[name=cf-turnstile-response]").value = "{token}";'
            )
            return True

        if captcha_type == "image":
            img_data = req.get(data).content
            img_b64 = base64.b64encode(img_data).decode()
            text = self.solver.solve({"method": "base64", "body": img_b64})
            captcha_input = self.driver.find_element(
                By.CSS_SELECTOR, "input[name*='captcha']"
            )
            captcha_input.clear()
            captcha_input.send_keys(text)
            return True

        return False  # No CAPTCHA detected

    def submit_form(self, url, fields, submit_selector="button[type='submit']"):
        """
        fields: list of (selector, value) tuples
        """
        self.driver.get(url)

        for selector, value in fields:
            self.fill_field(selector, value)

        self.solve_captcha()

        submit = self.driver.find_element(By.CSS_SELECTOR, submit_selector)
        submit.click()

        return self.driver.current_url

    def close(self):
        self.driver.quit()

Exemplo prático: um formulário de contato do início ao fim

Com as três classes prontas, o uso no dia a dia fica assim:

automator = FormAutomator("YOUR_API_KEY")

try:
    result_url = automator.submit_form(
        url="https://example.com/contact",
        fields=[
            ("#name", "John Doe"),
            ("#email", "john@example.com"),
            ("#subject", "Sales inquiry"),
            ("#message", "I'd like to learn more about your services."),
        ],
        submit_selector="#submit-btn",
    )
    print(f"Form submitted. Redirected to: {result_url}")
finally:
    automator.close()

Adaptando para outros tipos de formulário

O mesmo FormAutomator atende login, cadastro e busca — só muda a lista de campos e o seletor de envio. Para envios via AJAX, a lógica é parecida, mas em vez de preencher um campo oculto você intercepta a requisição XHR e inclui o token do CAPTCHA direto no payload enviado ao backend.

Autenticação (login).

result = automator.submit_form(
    url="https://staging.example.com/qa-login",
    fields=[
        ("#username", "testuser"),
        ("#password", "testpass123"),
    ],
    submit_selector="#login-btn",
)

Cadastro de novo usuário.

result = automator.submit_form(
    url="https://example.com/register",
    fields=[
        ("#first-name", "Jane"),
        ("#last-name", "Smith"),
        ("#email", "jane@example.com"),
        ("#password", "SecurePass!123"),
        ("#confirm-password", "SecurePass!123"),
    ],
    submit_selector="#register-btn",
)

Busca com CAPTCHA.

result = automator.submit_form(
    url="https://example.com/search",
    fields=[
        ("#query", "python developer"),
        ("#location", "San Francisco"),
    ],
    submit_selector="#search-btn",
)

Erros comuns e como resolver

Problema Causa Correção
Token rejeitado O token expirou antes do envio Resolva o CAPTCHA por último e envie imediatamente
Campo não encontrado Carregamento dinâmico da página Adicione esperas explícitas (WebDriverWait)
Tipo errado de CAPTCHA detectado Vários elementos de CAPTCHA na mesma página Revise a ordem de verificação no detector
Formulário recarrega após o envio Falha na validação do lado do servidor Confira se todos os campos obrigatórios foram preenchidos
Callback do reCAPTCHA não disparado É preciso chamar a função de callback manualmente Use grecaptcha.execute() depois de injetar o token

Perguntas frequentes

Preciso de um navegador para resolver o CAPTCHA do formulário?

Depende do tipo. Para reCAPTCHA e Turnstile, dá para resolver o desafio e enviar o token direto via requisição HTTP, sem abrir navegador algum. Já formulários com validação em JavaScript no lado do cliente exigem um navegador real (ou headless) para o envio funcionar.

O token expira se eu demorar para enviar o formulário?

Sim. Os tokens de reCAPTCHA e Turnstile têm validade curta e ficam presos à sessão da página. Resolva o CAPTCHA como a última etapa antes do clique em enviar — se algum preenchimento de campo vier depois, o token pode expirar e o envio será rejeitado.

Como tratar formulários que só exibem o CAPTCHA depois da primeira tentativa?

É um padrão comum: o CAPTCHA aparece somente depois que a primeira submissão falha na validação. A saída é chamar solve_captcha() novamente a cada nova tentativa de envio, tratando-o como uma etapa condicional, não fixa.

Esse mesmo fluxo serve para reCAPTCHA v3 (invisível)?

Sim, com ajuste. Para v3 o method continua userrecaptcha, mas você envia version: "v3" e o parâmetro action (o mesmo valor usado na chamada grecaptcha.execute() do site) em vez de esperar um clique. Vale criar um branch específico no detector, já que o v3 não expõe um elemento visível na página.

Quanto tempo leva, na prática, para resolver o CAPTCHA de um formulário?

Varia por tipo: CAPTCHA de imagem normalmente resolve em menos de 0,5 s e o Cloudflare Turnstile em menos de 10 s, enquanto o reCAPTCHA v2 pode levar até 60 s em picos de demanda — por isso o initial_wait do solver começa em 10 s e o polling continua a cada 5 s até o token chegar ou o tempo limite estourar.


Guias relacionados


Automatize qualquer formulário — resolva CAPTCHAs com a CaptchaAI.

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