Tutorials

Carregamento dinâmico de CAPTCHA: detectando CAPTCHAs de carregamento lento

O seletor do CAPTCHA não aparece na automação, mas o site exige um desafio? O widget provavelmente carrega sob demanda — só depois de um clique, um foco de campo, uma rolagem ou um temporizador. Este guia mostra como detectar e aguardar CAPTCHAs de carregamento dinâmico antes de extrair a sitekey e enviar o desafio para resolução.


O que costuma disparar o carregamento do CAPTCHA

Gatilho Exemplo Como reproduzir
Clique no botão O clique em "Enviar" injeta o reCAPTCHA no formulário Clique no botão antes de procurar o widget
Foco no campo O CAPTCHA aparece quando o campo é focado Foque o campo de e-mail ou senha
Posição de rolagem O CAPTCHA carrega quando a seção fica visível Role a página até o formulário
Temporizador O CAPTCHA carrega depois de 3 segundos Aguarde o atraso configurado
Condição JavaScript O CAPTCHA carrega após uma resposta AJAX Dispare a requisição que precede o carregamento

Padrão comum em checkouts brasileiros: o reCAPTCHA só carrega depois que o campo de CPF perde o foco. Times de QA com workers na região sa-east-1 devem medir a latência até ocr.captchaai.com antes de culpar o solver por um timeout.

Como confirmar qual gatilho está em jogo:

  • Abra o DevTools, ative "Preserve log" e recarregue a página observando a aba Network.
  • Teste com e sem bloqueador de anúncios ou rastreadores — alguns bloqueiam por engano o script do CAPTCHA.
  • Repita o teste em uma aba anônima para eliminar cache local antes de assumir que o seletor está errado.

Dica: se o CAPTCHA só aparece depois de um evento de negócio (como a validação do CPF), simule esse evento diretamente via JavaScript na sua suíte de testes, em vez de depender só do clique real — isso reduz flakiness no pipeline de CI.


Método 1: monitore o DOM com MutationObserver

Detecte a inserção do elemento CAPTCHA no DOM assim que ela acontecer:

Puppeteer

const puppeteer = require('puppeteer');

const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://staging.example.com/qa-login');

// Set up MutationObserver before triggering the CAPTCHA
const captchaInfo = await page.evaluate(() => {
  return new Promise((resolve) => {
    // Check if already present
    const existing = document.querySelector('.g-recaptcha, .cf-turnstile, .h-captcha');
    if (existing) {
      resolve({
        type: existing.className,
        sitekey: existing.getAttribute('data-sitekey'),
      });
      return;
    }

    // Watch for new elements
    const observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType !== 1) continue;

          const captcha = node.matches?.('.g-recaptcha, .cf-turnstile, .h-captcha')
            ? node
            : node.querySelector?.('.g-recaptcha, .cf-turnstile, .h-captcha');

          if (captcha) {
            observer.disconnect();
            resolve({
              type: captcha.className,
              sitekey: captcha.getAttribute('data-sitekey'),
            });
            return;
          }
        }
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });

    // Timeout after 30 seconds
    setTimeout(() => {
      observer.disconnect();
      resolve(null);
    }, 30000);
  });
});

console.log('Detected CAPTCHA:', captchaInfo);

Como disparar o carregamento manualmente

// Click the submit button to trigger CAPTCHA
await page.click('#submit-btn');

// Or focus the input
await page.focus('#email');

// Or scroll to the form
await page.evaluate(() => {
  document.querySelector('#signup-form').scrollIntoView();
});

Método 2: aguarde o script da biblioteca carregar

O CAPTCHA depende de sua biblioteca JavaScript. Espere por ela antes de extrair parâmetros:

// Wait for reCAPTCHA script to load
await page.waitForFunction(() => {
  return typeof window.grecaptcha !== 'undefined' 
    && typeof window.grecaptcha.render === 'function';
}, { timeout: 30000 });

// Now extract parameters
const sitekey = await page.evaluate(() => {
  const el = document.querySelector('.g-recaptcha');
  return el?.getAttribute('data-sitekey');
});

Para o Cloudflare Turnstile

O mesmo padrão vale para o Turnstile:

await page.waitForFunction(() => {
  return typeof window.turnstile !== 'undefined';
}, { timeout: 30000 });

const sitekey = await page.evaluate(() => {
  const el = document.querySelector('.cf-turnstile');
  return el?.getAttribute('data-sitekey');
});

O Turnstile costuma renderizar mais rápido que o reCAPTCHA, mas em conexões móveis mais lentas o script ainda pode levar alguns segundos extras para expor window.turnstile — mantenha o mesmo timeout de 30 s dos outros métodos.


Método 3: intercepte a chamada de render da biblioteca

Substitua o método render da biblioteca antes que a página o chame, para capturar a sitekey no momento exato:

// Inject before page scripts run
await page.evaluateOnNewDocument(() => {
  window.__captchaDetected = null;

  // Hook grecaptcha.render
  let _grecaptcha;
  Object.defineProperty(window, 'grecaptcha', {
    set(val) {
      _grecaptcha = val;
      const origRender = val.render;
      val.render = function(container, params) {
        window.__captchaDetected = {
          type: 'recaptcha',
          sitekey: params.sitekey,
          callback: params.callback?.name,
          container: typeof container === 'string' ? container : container.id,
        };
        return origRender.apply(this, arguments);
      };
    },
    get() { return _grecaptcha; },
  });
});

await page.goto('https://example.com/signup');

// Trigger the CAPTCHA (click, scroll, etc.)
await page.click('#show-form');

// Wait for detection
await page.waitForFunction(() => window.__captchaDetected !== null, {
  timeout: 30000,
});

const detected = await page.evaluate(() => window.__captchaDetected);
console.log('Detected:', detected);
// { type: 'recaptcha', sitekey: '6Le-wvkS...', callback: 'onCaptcha', container: 'recaptcha-box' }

O objeto capturado traz tudo que você precisa para montar a chamada ao solver:

  • type: o método de CAPTCHA identificado (recaptcha, turnstile ou hcaptcha).
  • sitekey: o valor a enviar no parâmetro correspondente da API.
  • callback: o nome da função que o site executa após a resolução — útil para confirmar que o token foi aceito.

Selenium (Python): aguardando CAPTCHAs de carregamento lento

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

driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")

# Trigger the CAPTCHA loading
submit = driver.find_element(By.ID, "submit-btn")
submit.click()

# Wait for CAPTCHA to appear
try:
    captcha_el = WebDriverWait(driver, 30).until(
        EC.presence_of_element_located((
            By.CSS_SELECTOR,
            ".g-recaptcha, .cf-turnstile, .h-captcha"
        ))
    )
    sitekey = captcha_el.get_attribute("data-sitekey")
    captcha_class = captcha_el.get_attribute("class")

    if "g-recaptcha" in captcha_class:
        captcha_type = "recaptcha"
    elif "cf-turnstile" in captcha_class:
        captcha_type = "turnstile"
    else:
        captcha_type = "hcaptcha"

    print(f"Type: {captcha_type}, Sitekey: {sitekey}")
except Exception:
    print("No CAPTCHA appeared within 30 seconds")

Aguardando o iframe (reCAPTCHA)

# reCAPTCHA loads an iframe even when the div exists but the script is still loading
WebDriverWait(driver, 30).until(
    EC.presence_of_element_located((
        By.CSS_SELECTOR,
        "iframe[src*='recaptcha'], iframe[src*='challenges.cloudflare.com']"
    ))
)
print("CAPTCHA iframe loaded")

Fluxo completo: detectar, resolver e injetar o token

import requests
import time

def detect_and_solve(driver, api_key, trigger_action=None):
    """Detect a lazy-loaded CAPTCHA, solve it, and inject the token."""

    # 1. Trigger the CAPTCHA
    if trigger_action:
        trigger_action(driver)

    # 2. Wait for it to appear
    captcha_el = WebDriverWait(driver, 30).until(
        EC.presence_of_element_located((
            By.CSS_SELECTOR,
            ".g-recaptcha, .cf-turnstile, .h-captcha"
        ))
    )

    sitekey = captcha_el.get_attribute("data-sitekey")
    page_url = driver.current_url
    captcha_class = captcha_el.get_attribute("class")

    # 3. Determine type and method
    if "g-recaptcha" in captcha_class:
        method, key_param, token_field = "userrecaptcha", "googlekey", "g-recaptcha-response"
    elif "cf-turnstile" in captcha_class:
        method, key_param, token_field = "turnstile", "sitekey", "cf-turnstile-response"
    else:
        method, key_param, token_field = "hcaptcha", "sitekey", "h-captcha-response"

    # 4. Solve with CaptchaAI
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key, "method": method,
        key_param: sitekey, "pageurl": page_url, "json": "1",
    }).json()

    task_id = resp["request"]
    for _ in range(24):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get", "id": task_id, "json": "1"
        }).json()
        if result["status"] == 1:
            token = result["request"]
            break

    # 5. Inject
    driver.execute_script(f"""
        const el = document.querySelector('textarea[name="{token_field}"], input[name="{token_field}"]');
        if (el) el.value = arguments[0];
    """, token)

    return token

Perguntas frequentes

Como sei se um CAPTCHA está carregando de forma dinâmica?

Abra o código-fonte da página (Ctrl+U) no primeiro carregamento. Se a div ou o script do CAPTCHA não aparecem ali, mas surgem depois de um clique, foco de campo ou rolagem, o carregamento é dinâmico.

O MutationObserver deixa minha automação mais lenta?

Não de forma perceptível — o observer reage a mudanças no DOM, sem polling ativo. O maior custo é o timeout definido para o caso de o CAPTCHA nunca aparecer; 20 a 30 segundos costuma bastar.

O que fazer se o CAPTCHA nunca aparecer, mesmo com o gatilho certo?

Confirme se o site não está servindo uma versão sem CAPTCHA para o navegador de automação antes de assumir que o seletor está errado.

Funciona em navegador headless?

Sim, com ressalvas. Alguns sites só carregam o CAPTCHA em modo headless quando percebem sinais atípicos de automação — nesses casos, use headless: 'new' no Puppeteer ou prefira um navegador completo em QA autorizado.

Preciso testar a partir de uma região específica, como sa-east-1?

Não é obrigatório, mas medir a partir da mesma região onde seu worker roda ajuda a isolar se o atraso vem do carregamento do CAPTCHA ou da latência até ocr.captchaai.com. Rode o mesmo teste localmente e a partir da nuvem antes de abrir um chamado de suporte.

O que fazer se dois CAPTCHAs carregarem dinamicamente na mesma página?

Trate cada widget separadamente: identifique o elemento correto antes de extrair a sitekey e evite reaproveitar o mesmo seletor genérico para os dois — veja o guia de múltiplos CAPTCHAs na mesma página para os seletores específicos de cada tipo.


Diagnóstico rápido: problemas frequentes

CAPTCHA nunca aparece

O gatilho usado não é o correto. Inspecione a página para descobrir o que realmente dispara o carregamento — abra o DevTools, ative "Preserve log" e repita as ações candidatas (clique, foco, rolagem) uma de cada vez.

Sitekey retorna nulo

O elemento existe no DOM, mas o script da biblioteca ainda não rodou. Aguarde o iframe do CAPTCHA aparecer, não apenas a div — o atributo data-sitekey só é populado depois que o script termina de inicializar o widget.

O observer não captura nada

O CAPTCHA já estava presente antes de o observer ser criado. Verifique elementos existentes antes de configurar o MutationObserver, como no primeiro bloco do Método 1.

Timeout constante

O CAPTCHA só carrega para navegadores com sinal realista de automação. Use um navegador completo com sinal de navegador realista e confirme que nenhuma extensão de bloqueio de scripts está interferindo no carregamento do widget.

Se nenhum desses ajustes resolver, capture uma gravação da sessão (vídeo ou HAR) e escale para o time de infraestrutura antes de repetir o teste manualmente várias vezes — isso costuma revelar problemas de rede que não aparecem no ambiente local.


Resumo rápido antes de enviar ao solver

Antes de chamar a API de resolução, confirme:

  • A sitekey não está null — se estiver, você capturou o elemento antes do script terminar de inicializar.
  • Você aguardou o iframe do CAPTCHA aparecer, não apenas a div do widget.
  • O gatilho certo foi realmente disparado (clique, foco, rolagem ou temporizador) antes da checagem.
  • Em checkouts com CPF, o campo perdeu o foco — é esse evento que costuma acionar o carregamento.

Resolva o CAPTCHA certo, no momento certo, com a CaptchaAI

Depois de detectar a sitekey de um CAPTCHA de carregamento lento, é só enviar o desafio para resolução automática. Obtenha sua chave de API em captchaai.com.


Guias relacionados

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