Tutorials

Lidando com vários CAPTCHAs em uma única página

Login com reCAPTCHA de um lado, cadastro de newsletter com outro reCAPTCHA do outro, e às vezes mais um desafio no meio do checkout: quando a mesma página tem mais de um CAPTCHA, a resposta certa não é resolver um de cada vez e esperar o token antes de atacar o próximo. É detectar todos os widgets de uma só vez, enviar cada um em paralelo para a CaptchaAI e injetar o token de volta no callback correto. O erro mais comum nessa rotina é tratar dois CAPTCHAs como se tivessem a mesma sitekey — cada widget carrega a sua, e confundir uma com a outra é a causa número um de token caindo no formulário errado.

Antes de sair copiando código, vale alinhar o que muda em relação a resolver um único CAPTCHA:

  • Detecção: você precisa mapear todos os widgets da página, não só o primeiro que aparece no DOM.
  • Envio: as tarefas vão para a CaptchaAI em paralelo, não em fila sequencial — isso é o que mantém o tempo total baixo.
  • Injeção: cada token só pode voltar para o widget de onde a sitekey saiu; misturar isso quebra o formulário.
  • Tipos: reCAPTCHA e Turnstile na mesma tela usam method diferentes na API — nunca envie os dois com o mesmo parâmetro.

Como detectar todos os CAPTCHAs da página

Antes de resolver, mapeie quantos widgets existem na página e qual sitekey cada um usa.

Python com Selenium

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

driver = webdriver.Chrome()
driver.get("https://example.com/multi-captcha-page")

# Find all reCAPTCHA iframes
captcha_iframes = driver.find_elements(
    By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]'
)

# Extract sitekeys from each iframe's src
import re
captchas = []
for i, iframe in enumerate(captcha_iframes):
    src = iframe.get_attribute("src")
    match = re.search(r"k=([A-Za-z0-9_-]+)", src)
    if match:
        captchas.append({
            "index": i,
            "sitekey": match.group(1),
            "iframe": iframe
        })

print(f"Found {len(captchas)} CAPTCHAs on page")
for c in captchas:
    print(f"  [{c['index']}] sitekey: {c['sitekey']}")

Resultado esperado:

Found 2 CAPTCHAs on page
  [0] sitekey: 6LcXyzABCDEF-login
  [1] sitekey: 6LcAbcDEFGHI-signup

JavaScript com Puppeteer

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto('https://example.com/multi-captcha-page', {
    waitUntil: 'networkidle2'
  });

  // Extract all reCAPTCHA widgets from the page
  const captchas = await page.evaluate(() => {
    const widgets = document.querySelectorAll('.g-recaptcha');
    return Array.from(widgets).map((el, i) => ({
      index: i,
      sitekey: el.getAttribute('data-sitekey'),
      elementId: el.id || `captcha-${i}`,
      callbackName: el.getAttribute('data-callback') || null
    }));
  });

  console.log(`Found ${captchas.length} CAPTCHAs`);
  captchas.forEach(c => console.log(`  [${c.index}] ${c.sitekey}`));
})();

Enviando e consultando todos os CAPTCHAs em paralelo

  1. Envie tudo de uma vez — todos os CAPTCHAs vão para a CaptchaAI sem esperar a resposta de um para enviar o próximo, nunca em fila sequencial.
  2. Consulte (poll) cada tarefa até o token voltar pronto para aquele widget específico.

Python

import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

API_KEY = "YOUR_API_KEY"
PAGE_URL = "https://example.com/multi-captcha-page"

def submit_captcha(sitekey):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": PAGE_URL,
        "json": "1"
    })
    result = resp.json()
    if result["status"] != 1:
        raise Exception(f"Submit error: {result['request']}")
    return result["request"]

def poll_result(task_id, timeout=120):
    deadline = time.time() + timeout
    while time.time() < deadline:
        time.sleep(5)
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": "1"
        })
        result = resp.json()
        if result["status"] == 1:
            return result["request"]
        if result["request"] != "CAPCHA_NOT_READY":
            raise Exception(f"Poll error: {result['request']}")
    raise TimeoutError(f"Task {task_id} timed out")

def solve_all(captchas):
    # Submit all in parallel
    task_ids = {}
    with ThreadPoolExecutor(max_workers=len(captchas)) as pool:
        futures = {
            pool.submit(submit_captcha, c["sitekey"]): c["index"]
            for c in captchas
        }
        for future in as_completed(futures):
            idx = futures[future]
            task_ids[idx] = future.result()
            print(f"[captcha-{idx}] Submitted → task {task_ids[idx]}")

    # Poll all in parallel
    tokens = {}
    with ThreadPoolExecutor(max_workers=len(task_ids)) as pool:
        futures = {
            pool.submit(poll_result, tid): idx
            for idx, tid in task_ids.items()
        }
        for future in as_completed(futures):
            idx = futures[future]
            tokens[idx] = future.result()
            print(f"[captcha-{idx}] Solved")

    return tokens

# Example usage
captchas = [
    {"index": 0, "sitekey": "6LcXyzABCDEF-login"},
    {"index": 1, "sitekey": "6LcAbcDEFGHI-signup"}
]

tokens = solve_all(captchas)

Resultado esperado:

[captcha-0] Submitted → task 71823456
[captcha-1] Submitted → task 71823457
[captcha-1] Solved
[captcha-0] Solved

Como injetar cada token no widget certo

  • Cada widget reCAPTCHA tem sua própria textarea g-recaptcha-response, aninhada dentro do contêiner daquele widget específico.
  • O mapeamento entre token e widget é por índice, na ordem em que os widgets foram detectados — nunca por suposição de qual veio primeiro.

Python (Selenium)

def inject_tokens(driver, captchas, tokens):
    for c in captchas:
        idx = c["index"]
        token = tokens[idx]

        # Find the textarea within the widget's container
        container = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")[idx]
        textarea = container.find_element(
            By.CSS_SELECTOR, 'textarea[name="g-recaptcha-response"]'
        )
        driver.execute_script(
            "arguments[0].value = arguments[1];", textarea, token
        )

        # Trigger the callback if defined
        callback = c.get("callback")
        if callback:
            driver.execute_script(f"{callback}('{token}');")

        print(f"[captcha-{idx}] Token injected")

inject_tokens(driver, captchas, tokens)

JavaScript (Puppeteer)

async function injectTokens(page, captchas, tokens) {
  for (const captcha of captchas) {
    const token = tokens[captcha.index];

    await page.evaluate((idx, tkn, callbackName) => {
      const widgets = document.querySelectorAll('.g-recaptcha');
      const textarea = widgets[idx].querySelector(
        'textarea[name="g-recaptcha-response"]'
      );
      textarea.value = tkn;

      if (callbackName && typeof window[callbackName] === 'function') {
        window[callbackName](tkn);
      }
    }, captcha.index, token, captcha.callbackName);

    console.log(`[captcha-${captcha.index}] Token injected`);
  }
}

await injectTokens(page, captchas, tokens);

Quando a página mistura tipos de CAPTCHA (reCAPTCHA + Turnstile)

  • Login com um provedor e o restante do formulário com outro é o cenário mais comum de mistura de tipos.
  • Detecte cada tipo separadamente pelo seletor CSS antes de enviar qualquer coisa para a CaptchaAI:
def detect_all_captchas(driver):
    detected = []

    # reCAPTCHA
    recaptchas = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")
    for i, el in enumerate(recaptchas):
        detected.append({
            "type": "userrecaptcha",
            "sitekey": el.get_attribute("data-sitekey"),
            "label": f"recaptcha-{i}"
        })

    # Turnstile
    turnstiles = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
    for i, el in enumerate(turnstiles):
        detected.append({
            "type": "turnstile",
            "sitekey": el.get_attribute("data-sitekey"),
            "label": f"turnstile-{i}"
        })

    return detected
  1. reCAPTCHA (.g-recaptcha) vai com method=userrecaptcha.
  2. Cloudflare Turnstile (.cf-turnstile) vai com method=turnstile.
  3. Nunca misture os dois no mesmo envio — mandar a sitekey do Turnstile com method=userrecaptcha devolve erro da API em vez de um token.

Exemplo: cadastro com reCAPTCHA e Turnstile na mesma tela

Esse padrão é comum em fintechs e e-commerces no Brasil: a etapa de cadastro roda reCAPTCHA v2 no formulário principal e Cloudflare Turnstile na verificação de e-mail, os dois na mesma página. Em QA, isso costuma ser testado contra um ambiente de staging (staging.example.com) com workers hospedados na região sa-east-1 da AWS.

Alguns pontos que ajudam a dimensionar o teste:

  • Cloudflare Turnstile normalmente resolve em menos de 10 segundos; o reCAPTCHA v2 tem teto de até 60 segundos sob alta concorrência. Resolver os dois em paralelo, e não em sequência, evita que o tempo total do teste some os dois tetos.
  • Cada widget conta como uma tarefa independente — não existe desconto de tempo por estarem na mesma página, então a única forma de manter o pipeline de QA rápido é o envio em paralelo.
  • Para volumes típicos de QA (poucos CAPTCHAs por execução), o plano BASIC (US$ 15/mês, 5 threads) já cobre o cenário sem fila de espera.

Boas práticas para páginas com vários CAPTCHAs

  • Trate sitekey como a chave primária do widget, nunca o índice de posição — a ordem dos elementos no DOM pode mudar entre deploys do front-end.
  • Envie tudo para a CaptchaAI em paralelo com ThreadPoolExecutor (Python) ou Promise.all (JavaScript); resolver em sequência multiplica o tempo total sem necessidade.
  • Reescaneie a página depois de cada envio de formulário — um CAPTCHA que só renderiza após o passo anterior é comum em fluxos de várias etapas.
  • Separe o method por tipo de CAPTCHA antes de montar a requisição; nunca assuma que todos os widgets da página são do mesmo tipo.
  • Registre o task_id de cada envio junto com o índice do widget — isso facilita depurar qual token pertence a qual CAPTCHA quando algo dá errado.

Perguntas frequentes

Cada widget de CAPTCHA na mesma página pode usar uma sitekey diferente?

Sim, é a regra. Extraia a sitekey por widget — nunca presuma que dois CAPTCHAs compartilham uma só.

Preciso de uma thread da CaptchaAI para cada CAPTCHA que resolvo ao mesmo tempo?

Sim, cada um ocupa uma thread até o token voltar:

  • BASIC (US$ 15/mês, 5 threads) já cobre páginas com dois ou três desafios simultâneos.
  • STANDARD (US$ 30/mês, 15 threads) dá mais margem para volumes maiores de QA.

O token foi injetado, mas o formulário continua bloqueado. O que verificar primeiro?

Se o callback foi disparado — muitos formulários só liberam o envio quando a função de data-callback é chamada; preencher a textarea sozinho não basta.

Um CAPTCHA que só aparece depois do primeiro envio do formulário muda alguma coisa no fluxo?

Sim: reescaneie a página após cada envio, com WebDriverWait (Selenium) ou page.waitForSelector (Puppeteer), para pegar o widget recém-renderizado antes de tentar resolvê-lo.


Solução de problemas

Problema Causa Correção
Token injetado, mas formulário continua bloqueado Callback não foi acionado Verifique data-callback e chame a função com o token
Só o primeiro CAPTCHA é detectado Segundo widget carrega de forma lenta (lazy) Aguarde todos os iframes/widgets antes de escanear a página
Token aparece no widget errado Índice trocado entre CAPTCHA e token Mapeie tokens por sitekey, nunca por posição/índice
ERROR_WRONG_GOOGLEKEY Sitekey extraída errada Confira a sitekey no atributo src do iframe ou em data-sitekey

Resolva vários CAPTCHAs por página sem trocar de integração

Obtenha sua chave de API em captchaai.com e use a mesma integração para reCAPTCHA e Turnstile, mesmo quando aparecem juntos na mesma página.


Guias relacionados

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