Quer parar de travar em CAPTCHAs durante scripts Puppeteer? O caminho é simples: extraia a sitekey do DOM, envie para a API da CaptchaAI, receba o token e injete no formulário — sem intervenção manual. Este guia mostra o módulo solver, a configuração do Puppeteer e o fluxo completo para reCAPTCHA v2 e Cloudflare Turnstile.
Pré-requisitos
- Node.js 16 ou mais recente, com npm instalado.
- Puppeteer — instale com
npm install puppeteer. - Axios — instale com
npm install axiospara chamar a API da CaptchaAI. - Chave de API da CaptchaAI — crie uma conta em captchaai.com e copie a chave no painel.
Como funciona o fluxo de resolução
- O Puppeteer navega até a página que exibe o CAPTCHA
- Seu script extrai a sitekey do CAPTCHA a partir do DOM
- A CaptchaAI resolve o desafio do lado do servidor
- Seu script injeta o token na página e envia o formulário
Etapa 1: crie o módulo solver
O módulo abaixo concentra toda a comunicação com a API da CaptchaAI: envia a tarefa, faz o polling do resultado e devolve o token pronto para uso. Ele serve tanto para reCAPTCHA v2 quanto para Cloudflare Turnstile — a única diferença entre as duas funções é o parâmetro method.
// solver.js
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
const POLL_INTERVAL = 5000;
const MAX_ATTEMPTS = 60;
async function solveRecaptchaV2(siteKey, pageUrl) {
// Submit task
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: siteKey,
pageurl: pageUrl,
},
});
if (!submitResp.data.startsWith("OK|")) {
throw new Error(`Submit failed: ${submitResp.data}`);
}
const taskId = submitResp.data.split("|")[1];
console.log(`Task submitted: ${taskId}`);
// Poll for result
for (let i = 0; i < MAX_ATTEMPTS; i++) {
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) {
return result.data.split("|")[1];
}
throw new Error(`Solve failed: ${result.data}`);
}
throw new Error("Solve timed out");
}
async function solveTurnstile(siteKey, pageUrl) {
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "turnstile",
sitekey: siteKey,
pageurl: pageUrl,
},
});
if (!submitResp.data.startsWith("OK|")) {
throw new Error(`Submit failed: ${submitResp.data}`);
}
const taskId = submitResp.data.split("|")[1];
for (let i = 0; i < MAX_ATTEMPTS; i++) {
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) return result.data.split("|")[1];
throw new Error(`Solve failed: ${result.data}`);
}
throw new Error("Solve timed out");
}
module.exports = { solveRecaptchaV2, solveTurnstile };
Etapa 2: configure o Puppeteer para QA de navegador
Antes de resolver qualquer CAPTCHA, vale configurar o navegador com um perfil de QA comum — isso evita que o site bloqueie a página por sinais óbvios de automação antes mesmo do desafio aparecer, deixando o solver da CaptchaAI cuidar do resto.
const puppeteer = require("puppeteer");
async function createBrowser() {
const browser = await puppeteer.launch({
headless: "new",
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-blink-features=AutomationControlled",
],
});
const page = await browser.newPage();
await page.setUserAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
);
// Hide automation indicators
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "webdriver", { get: () => false });
});
return { browser, page };
}
Etapa 3: resolva o reCAPTCHA na página
Com o solver e o navegador prontos, o resto do script segue direto: navegue até a página, extraia a sitekey, aguarde o token da CaptchaAI e injete-o no campo de resposta antes de enviar o formulário.
const { solveRecaptchaV2 } = require("./solver");
async function scrapeWithCaptcha(url) {
const { browser, page } = await createBrowser();
try {
await page.goto(url, { waitUntil: "networkidle2" });
// Extract site key
const siteKey = await page.$eval(
".g-recaptcha",
(el) => el.getAttribute("data-sitekey")
);
console.log("Site key:", siteKey);
// Solve with CaptchaAI
const token = await solveRecaptchaV2(siteKey, url);
console.log("Token received:", token.substring(0, 50));
// Inject token
await page.evaluate((token) => {
document.getElementById("g-recaptcha-response").innerHTML = token;
document.getElementById("g-recaptcha-response").style.display = "";
}, token);
// Submit the form
await page.click('button[type="submit"]');
await page.waitForNavigation({ waitUntil: "networkidle2" });
// Scrape the content
const content = await page.content();
console.log("Page loaded successfully");
return content;
} finally {
await browser.close();
}
}
Etapa 4: trate callbacks JavaScript do CAPTCHA
Alguns sites disparam a validação por callback do JavaScript em vez de um envio de formulário tradicional:
// Trigger the reCAPTCHA callback
await page.evaluate((token) => {
// Method 1: Direct callback
if (typeof ___grecaptcha_cfg !== "undefined") {
const clients = ___grecaptcha_cfg.clients;
Object.keys(clients).forEach((key) => {
const client = clients[key];
// Find the callback function
const findCallback = (obj) => {
for (const prop in obj) {
if (typeof obj[prop] === "function") {
obj[prop](token);
return true;
}
if (typeof obj[prop] === "object" && obj[prop] !== null) {
if (findCallback(obj[prop])) return true;
}
}
return false;
};
findCallback(client);
});
}
}, token);
Exemplo completo e funcional
Reunindo as etapas anteriores em um único script: este exemplo resolve o reCAPTCHA de uma página de QA e segue a automação normalmente depois do envio do formulário.
const puppeteer = require("puppeteer");
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
async function solveCaptcha(siteKey, pageUrl) {
const submit = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: siteKey,
pageurl: pageUrl,
},
});
const taskId = submit.data.split("|")[1];
while (true) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) return result.data.split("|")[1];
throw new Error(result.data);
}
}
(async () => {
const browser = await puppeteer.launch({
headless: "new",
args: ["--disable-blink-features=AutomationControlled"],
});
const page = await browser.newPage();
try {
await page.goto("https://staging.example.com/qa-login", {
waitUntil: "networkidle2",
});
// Get the site key
const siteKey = await page.$eval(".g-recaptcha", (el) =>
el.getAttribute("data-sitekey")
);
// Solve
const token = await solveCaptcha(siteKey, page.url());
// Inject and submit
await page.evaluate((t) => {
document.getElementById("g-recaptcha-response").innerHTML = t;
}, token);
await page.click("#submit-btn");
await page.waitForNavigation();
console.log("Done:", page.url());
} finally {
await browser.close();
}
})();
Rodando isso em produção no Brasil
Três pontos costumam pegar equipes brasileiras de surpresa quando esse fluxo vai para produção:
- Latência. Workers rodando na região
sa-east-1(São Paulo) da AWS tendem a receber o token mais rápido do que workers na Virgínia ou na Europa — vale medir o RTT real no seu ambiente antes de fixar oPOLL_INTERVAL. - Conformidade com a LGPD. Se o formulário coleta dados pessoais, revise as obrigações da LGPD antes de armazenar o que o Puppeteer captura: registre só o necessário e evite logar CPFs, e-mails ou tokens de sessão em texto puro.
- Limite de requisições. Rodar várias instâncias do Puppeteer em paralelo multiplica as chamadas a
in.php/res.php; distribua o polling entre workers para não estourar o limite de requisições do seu plano.
Solução de problemas comuns
| Problema | Causa | Correção |
|---|---|---|
page.$eval falha |
O CAPTCHA carrega depois da renderização inicial | Use page.waitForSelector('.g-recaptcha') |
| O token não funciona | Expirou antes do envio | Injete o token imediatamente após recebê-lo |
| O site detecta o Puppeteer | Faltou configuração de QA de navegador | Use a configuração padrão mostrada na Etapa 2 |
Navigation timeout |
A página não navegou após o envio | Verifique se o site usa AJAX em vez de postagem de formulário |
Erro de conexão ao chamar in.php/res.php |
Instabilidade de rede entre o worker e a API | Adicione retentativa com backoff exponencial antes de lançar o erro |
Perguntas frequentes
Quanto tempo leva para a CaptchaAI devolver o token no Puppeteer?
Geralmente poucos segundos, variando com o tipo de CAPTCHA e a fila do momento. O POLL_INTERVAL de 5 s e o limite de 60 tentativas do exemplo cobrem os cenários normais sem travar o script.
Dá para resolver Cloudflare Turnstile com Puppeteer usando a CaptchaAI?
Sim. Extraia o data-sitekey da div .cf-turnstile e chame solveTurnstile, que usa method=turnstile na API. O resto do fluxo é igual ao do reCAPTCHA.
Por que o token do reCAPTCHA para de funcionar antes do envio do formulário?
Tokens têm validade curta. Se o script busca o token e só injeta minutos depois, ele expira — injete assim que ele chegar e envie o formulário na sequência.
Preciso combinar proxy com a CaptchaAI no Puppeteer?
Não necessariamente. A CaptchaAI resolve o desafio via API, independente de como sua requisição HTTP chega ao site-alvo; proxy é uma decisão separada de infraestrutura.
Como tratar mais de um CAPTCHA na mesma página com Puppeteer?
Extraia cada sitekey separadamente e resolva-as em paralelo:
- capture a sitekey de cada elemento
.g-recaptchaou.cf-turnstilena página; - chame
solveRecaptchaV2ousolveTurnstileconforme o tipo de cada CAPTCHA; - aguarde todas as promises com
Promise.all()antes de injetar os tokens e enviar o formulário.
Guias relacionados
- Como tratar CAPTCHA no Selenium com Python
- Como tratar CAPTCHA no Playwright
- Automação com Node.js em sites com CAPTCHA