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 │ │ │
└────────────┘ └──────────────┘ └────────────┘ └──────────────┘
- Carrega o formulário no navegador controlado pelo Selenium.
- Preenche os campos de texto, seleção e senha com os valores esperados.
- Detecta o tipo de CAPTCHA presente e resolve pela API da CaptchaAI.
- 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.