Scarnox · Blog

Een PopularCaptcha oplossen in Playwright en Puppeteer (Node.js)

Browserautomatisering stopt op het moment dat een PopularCaptcha verschijnt. Klikken door een beeldraster vanuit een script is traag en broos. Een token ophalen bij een solver-API en het in de pagina zetten kost één verzoek, en de rest van je script loopt gewoon door.

Deze gids behandelt Playwright en Puppeteer met Node.js. De flow is in beide hetzelfde, en de voorbeelden gebruiken proxyless solves, die Scarnox op het eigen netwerk draait. PopularCaptcha is het type dat Scarnox vandaag oplost. Andere captchatypen komen binnenkort, dus de onderstaande code is alleen voor PopularCaptcha.

De flow

  1. Lees de sitekey van de pagina.
  2. Vraag met één blokkerend POST-verzoek een token bij Scarnox aan.
  3. Zet het token in het antwoordveld, of geef het aan de callback van de site, en verstuur daarna het formulier.

De sleutel instellen

Maak een account aan en begin met gratis proefkrediet, zonder creditcard. Bewaar de sleutel in een omgevingsvariabele, nooit in de testcode:

export SCARNOX_KEY="scarnox_YOURAPIKEY"

Node 18 en later bevatten fetch, dus de API-aanroep heeft geen extra pakketten nodig.

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());

// Zet het token in beide antwoordvelden die de site kan lezen.
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]");

Let op dat site_url uit page.url() komt. Dat is de pagina waar de widget staat, wat de solver nodig heeft. De solve gebeurt nadat de pagina is geladen, dus de sitekey staat dan al in de DOM.

Puppeteer

Puppeteer verschilt alleen in de selector-API. Hergebruik de functie solve hierboven en vervang de paginaaanroepen:

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);

Wanneer de site een callback gebruikt

Sommige sites lezen het verborgen veld niet. Ze wachten op de JavaScript-callback van de widget. Zoek de functie die in het attribuut data-callback van de widget wordt genoemd en roep die aan met het token:

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

Als de site beide gebruikt, zet dan het veld en roep de callback aan. Beide doen is onschadelijk.

Onzichtbare widgets

Een onzichtbare widget heeft geen vinkje. De site start hem vanuit zijn eigen code, meestal wanneer de bezoeker op verzenden drukt. Stuur hetzelfde verzoek met het optionele veld is_invisible op true:

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

De gids over onzichtbare captcha legt uit hoe je deze widgets herkent en hoe je het token teruggeeft.

Tips voor de timing

Je eigen flow testen

Draai het voorbeeld tegen een formulier dat van jou is voordat je het op iets groters richt. Controleer drie dingen: het token wordt geaccepteerd, het formulier wordt verzonden, en de volgende pagina verschijnt. Als de flow van begin tot eind werkt, houd je hem groen in CI met één echte solve per run.

Probeer het gratis

Maak een account aan en begin met gratis proefkrediet, zonder creditcard. Dat is genoeg om de hele flow aan te sluiten en te controleren. Voor wat oplossen op volume kost, zie actuele prijzen.

Een geweigerd token debuggen

Wanneer de site een token weigert, reproduceer de flow dan met één verzoek en één paginalading voordat je iets anders verandert. Controleer in deze volgorde:

Sla de HTML van de pagina op op het moment van de fout met await page.content(). Die HTML vergelijken met een werkende run laat meestal in één oogopslag het verschil zien: een andere sitekey, een ontbrekend veld, of een widget die in een andere modus is weergegeven.

Volgende stappen