Tutoriais

reCAPTCHA em aplicativos de página única: padrões de carregamento dinâmico

Aplicativos de página única carregam o reCAPTCHA dinamicamente: o widget não existe no HTML inicial. Um formulário de login do React renderiza o CAPTCHA somente quando o componente é montado. Uma página de checkout do Vue carrega o reCAPTCHA depois que o usuário clica em "Fazer pedido". A extração tradicional da fonte da página perde isso completamente. Veja como detectar e resolver reCAPTCHAs carregados dinamicamente.

Por que os SPAs são diferentes

Página tradicional SPA
script reCAPTCHA no HTML inicial Script injetado após mudança de rota
O widget é renderizado no carregamento da página Widget é renderizado na montagem do componente
Chave do site na origem da página Chave do site no pacote JavaScript
Formulário enviado via POST Formulário enviado via XHR/fetch

Em um SPA, o widget reCAPTCHA pode não existir até que uma ação específica do usuário o acione. Sua automação deve aguardar o widget aparecer.

Detectando o widget reCAPTCHA

Espere pelo elemento

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("https://staging.example.com/qa-login")

    # SPA may need a click or navigation to trigger reCAPTCHA
    page.click("#show-login-form")

    # Wait for reCAPTCHA iframe to appear
    recaptcha_frame = page.wait_for_selector(
        "iframe[src*='recaptcha']",
        timeout=15000
    )
    print("reCAPTCHA detected:", recaptcha_frame.get_attribute("src"))

Extraia a chave do site

A chave do site pode estar em vários locais em um SPA:

# Method 1: From the iframe src
iframe_src = recaptcha_frame.get_attribute("src")
# src contains: ...?k=6LcR_RsTAAAAADge...
import re
match = re.search(r'[?&]k=([^&]+)', iframe_src)
site_key = match.group(1) if match else None

# Method 2: From data-sitekey attribute
site_key = page.eval_on_selector(
    "[data-sitekey]",
    "el => el.getAttribute('data-sitekey')"
)

# Method 3: From JavaScript bundle (last resort)
site_key = page.evaluate("""
    () => {
        // Check for grecaptcha render calls
        const scripts = document.querySelectorAll('script');
        for (const s of scripts) {
            const match = s.textContent.match(/sitekey['":\s]+(['"])(6L[^'"]+)\\1/);
            if (match) return match[2];
        }
        return null;
    }
""")

Resolvendo o reCAPTCHA

Assim que tiver a chave do site, envie para CaptchaAI:

import requests
import time

def solve_recaptcha(site_key, page_url):
    # Submit task
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    # Poll for result
    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]
    raise TimeoutError("Solve timed out")

Injetando o Token em um SPA

SPAs não enviam formulários tradicionais. Você precisa injetar o token onde o aplicativo espera.

Método 1: definir a área de texto oculta

token = solve_recaptcha(site_key, "https://staging.example.com/qa-login")

# Inject into the g-recaptcha-response textarea
page.evaluate(f"""
    document.getElementById('g-recaptcha-response').value = '{token}';
""")

# Submit the form
page.click("#login-button")

Método 2: acionar o retorno de chamada

Muitas implementações de SPA reCAPTCHA usam uma função de retorno de chamada:

# Find the callback function name
callback = page.evaluate("""
    () => {
        const widget = document.querySelector('.g-recaptcha');
        return widget?.getAttribute('data-callback') || null;
    }
""")

# Call it with the token
if callback:
    page.evaluate(f"window['{callback}']('{token}')")
else:
    # Try the default grecaptcha callback
    page.evaluate(f"""
        if (window.grecaptcha) {{
            // Set the response and trigger verification
            document.getElementById('g-recaptcha-response').value = '{token}';
            // Find and call any registered callbacks
            const form = document.querySelector('form');
            if (form) form.dispatchEvent(new Event('submit'));
        }}
    """)

Método 3: interceptar o XHR (JavaScript / Puppeteer)

const puppeteer = require('puppeteer');

async function solveRecaptchaInSPA() {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  // Intercept the API call and inject the token
  await page.setRequestInterception(true);
  page.on('request', request => {
    if (request.url().includes('/api/login')) {
      const postData = JSON.parse(request.postData() || '{}');
      // Token already set via page.evaluate — let it pass
      request.continue();
    } else {
      request.continue();
    }
  });

  await page.goto('https://staging.example.com/qa-login');
  await page.waitForSelector('[data-sitekey]');

  const siteKey = await page.$eval(
    '[data-sitekey]',
    el => el.getAttribute('data-sitekey')
  );

  // Solve with CaptchaAI (implementation omitted — same as Python)
  const token = await solveCaptcha(siteKey, page.url());

  // Inject token and trigger callback
  await page.evaluate((t) => {
    document.getElementById('g-recaptcha-response').value = t;
    const callback = document.querySelector('.g-recaptcha')
      ?.getAttribute('data-callback');
    if (callback && window[callback]) {
      window[callback](t);
    }
  }, token);
}

Padrões específicos da estrutura

Reagir (react-google-recaptcha)

Os aplicativos React geralmente usam o pacote react-google-recaptcha. O componente é renderizado de forma assíncrona:

# Wait for React to mount the component
page.wait_for_selector(".g-recaptcha", state="attached")

# The sitekey is in the rendered div's data attribute
site_key = page.eval_on_selector(
    ".g-recaptcha", "el => el.dataset.sitekey"
)

Vue (vue-recaptcha)

# Vue may use v-if to conditionally render
# Navigate or interact to trigger the condition
page.click("#proceed-to-checkout")
page.wait_for_selector("iframe[src*='recaptcha']")

Angular

# Angular apps may lazy-load reCAPTCHA modules
# Wait for the specific Angular component
page.wait_for_selector("re-captcha, app-recaptcha, [data-sitekey]")

Lidando com alterações de rota

SPAs reutilizam a página – a navegação acontece sem recargas completas:

# Listen for reCAPTCHA appearing after SPA navigation
page.goto("https://example.com")

# Navigate within the SPA
page.click("a[href='/login']")

# The URL changed but no page reload happened
# Wait for the CAPTCHA to render in the new "page"
page.wait_for_selector("iframe[src*='recaptcha']", timeout=10000)

Solução de problemas

Problema Causa Correção
Área de texto g-recaptcha-response não encontrada SPA ainda não renderizou o widget Use wait_for_selector com tempo limite adequado
Token injetado, mas o formulário não é enviado Retorno de chamada não acionado Encontre e chame a função data-callback
A chave do site não está na origem da página Empacotado em JS ou carregado dinamicamente Aguarde a renderização do widget e extraia do iframe src
O token funciona uma vez e depois falha Componente de re-renderização do SPA, token de compensação Injetar token imediatamente antes do envio do formulário
Widget é renderizado novamente após envio controlado ao endpoint QA A reatividade React/Vue limpa o valor Use a abordagem data-callback em vez de definir a área de texto

Perguntas frequentes

Posso pular o navegador e resolver SPA reCAPTCHAs apenas com solicitações HTTP?

Se você puder extrair a chave do site e replicar a chamada da API de envio de formulário, sim. Muitos formulários SPA são enviados por meio de um endpoint da API REST - você pode chamá-lo diretamente com o token, mas primeiro você precisa de um navegador para descobrir a chave do site e o endpoint da API.

Como lidar com o reCAPTCHA que aparece apenas em determinadas ações do usuário?

Automatize a ação de acionamento (clique em um botão, preencha um formulário, navegue até uma rota) e aguarde o elemento reCAPTCHA aparecer. Use wait_for_selector com um tempo limite razoável.

CaptchaAI se importa se o reCAPTCHA está em um SPA?

Não. CaptchaAI precisa apenas da chave do site e do URL da página. A forma como o widget é carregado na página não afeta a solução.

Artigos relacionados

Próximas etapas

Resolva reCAPTCHAs carregados dinamicamente em qualquer SPA –obtenha sua chave API CaptchaAI.

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