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
- Como resolver o retorno de chamada do Recaptcha V2 usando API
- Torniquete Recaptcha V2 no mesmo local
- Mecanismo de retorno de chamada Recaptcha V2
Próximas etapas
Resolva reCAPTCHAs carregados dinamicamente em qualquer SPA –obtenha sua chave API CaptchaAI.