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
- Lees de sitekey van de pagina.
- Vraag met één blokkerend POST-verzoek een token bij Scarnox aan.
- 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
- Haal het token vlak voor je verzendt. Tokens verlopen binnen ongeveer twee minuten.
- Geef het solve-verzoek een lange timeout, ongeveer 185 seconden. Het blokkeert tot het token klaar is.
- Mislukte solves zijn gratis, dus een retry kost alleen tijd. Probeer tijdelijke fouten opnieuw met backoff, zoals beschreven in foutafhandeling en backoff.
- Meerdere browsers tegelijk? Beperk het aantal gelijktijdige solves, zodat één trage batch de rest van de run niet ophoudt.
- Sluit browsers in een
finally-blok. Een gecrashte test die Chromium aan laat staan, vertraagt elke latere solve op dezelfde machine.
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:
- De sitekey die je hebt gestuurd, komt overeen met de widget op de pagina die je hebt geladen.
site_urlis de pagina waar de widget staat, niet een redirectdoel.- Het token is in het veld gezet dat de pagina leest, of de callback is aangeroepen.
- Het verzenden gebeurde binnen ongeveer twee minuten na de solve.
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.