Scarnox · Blog

Resolvendo um PopularCaptcha no Playwright e no Puppeteer (Node.js)

A automação de navegador para no momento em que um PopularCaptcha aparece. Clicar em uma grade de imagens a partir de um script é lento e frágil. Obter um token de uma API de resolução e colocá-lo na página leva uma única requisição, e o restante do seu script segue normalmente.

Este guia cobre o Playwright e o Puppeteer com Node.js. O fluxo é o mesmo nos dois, e os exemplos usam resoluções proxyless, que a Scarnox executa na própria rede. O PopularCaptcha é o tipo que a Scarnox resolve hoje. Outros tipos de captcha estão em breve, então o código abaixo serve apenas para o PopularCaptcha.

O fluxo

  1. Leia o sitekey da página.
  2. Peça um token à Scarnox com uma única requisição POST bloqueante.
  3. Coloque o token no campo de resposta, ou entregue-o ao callback do site, e envie o formulário.

Configure a chave

Crie uma conta e comece com crédito de teste grátis, sem cartão. Guarde a chave em uma variável de ambiente, nunca no código de teste:

export SCARNOX_KEY="scarnox_YOURAPIKEY"

O Node 18 e versões posteriores incluem fetch, então a chamada à API não precisa de pacotes extras.

Playwright

import { chromium } from "playwright";

const API_KEY = process.env.SCARNOX_KEY;

async function solve(sitekey, url) {
  const r = await fetch("https://api.scarnox.com/api/tasks/create", {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ captcha_type: "PopularCaptchaTaskProxyless", site_key: sitekey, site_url: url }),
  });
  const data = await r.json();
  if (data.errorId !== 0) throw new Error(`${data.errorCode}: ${data.errorDescription}`);
  return data.solution.token;
}

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://example.com/signup");

const sitekey = await page.getAttribute("[data-sitekey]", "data-sitekey");
const token = await solve(sitekey, page.url());

// Coloque o token nos dois campos de resposta que o site pode ler.
await page.evaluate((t) => {
  for (const name of ["h-captcha-response", "g-recaptcha-response"]) {
    document.querySelectorAll(`[name="${name}"]`).forEach((el) => { el.value = t; });
  }
}, token);

await page.fill("#email", "you@example.com");
await page.click("button[type=submit]");

Observe que o site_url vem de page.url(). Essa é a página em que o widget está, que é o que o solver precisa. A resolução acontece depois que a página carregou, então o sitekey já está no DOM.

Puppeteer

O Puppeteer difere apenas na API de seletores. Reutilize a função solve acima e substitua as chamadas de página:

const sitekey = await page.$eval("[data-sitekey]", (el) => el.dataset.sitekey);
const token = await solve(sitekey, page.url());

await page.evaluate((t) => {
  document.querySelectorAll('[name="h-captcha-response"],[name="g-recaptcha-response"]')
    .forEach((el) => { el.value = t; });
}, token);

Quando o site usa um callback

Alguns sites não leem o campo oculto. Eles aguardam o callback em JavaScript do widget. Encontre a função nomeada no atributo data-callback do widget e chame-a com o token:

await page.evaluate((t) => {
  const name = document.querySelector("[data-sitekey]")?.dataset.callback;
  if (name && typeof window[name] === "function") window[name](t);
}, token);

Se o site usa os dois, defina o campo e chame o callback. Fazer as duas coisas não causa problema.

Widgets invisíveis

Um widget invisível não tem caixa de marcação. O site o inicia pelo próprio código, normalmente quando o visitante pressiona enviar. Envie a mesma requisição com o campo opcional is_invisible definido como true:

body: JSON.stringify({
  captcha_type: "PopularCaptchaTaskProxyless",
  site_key: sitekey,
  site_url: url,
  is_invisible: true,
}),

O guia de captcha invisível explica como identificar esses widgets e como devolver o token.

Dicas de tempo

Testando o seu próprio fluxo

Rode o exemplo em um formulário seu antes de apontá-lo para algo maior. Confira três coisas: o token é aceito, o formulário é enviado e a página seguinte aparece. Quando o fluxo funcionar de ponta a ponta, mantenha-o verde no CI com uma única resolução real por execução.

Teste grátis

Crie uma conta e comece com crédito de teste grátis, sem cartão. Isso basta para montar e verificar o fluxo completo. Para saber quanto custa a resolução em volume, veja a página de preços.

Depurando um token rejeitado

Quando o site rejeita um token, reproduza o fluxo com uma requisição e um carregamento de página antes de mudar qualquer outra coisa. Verifique nesta ordem:

Salve o HTML da página no momento da falha com await page.content(). Comparar esse HTML com uma execução que funcionou costuma mostrar a diferença de imediato: um sitekey diferente, um campo ausente ou um widget renderizado em outro modo.

Próximos passos