Risolvere un PopularCaptcha con Playwright e Puppeteer (Node.js)
L'automazione del browser si blocca nel momento in cui compare un PopularCaptcha. Cliccare attraverso una griglia di immagini da uno script è lento e fragile. Ottenere un token da un'API di risoluzione e inserirlo nella pagina richiede una sola richiesta, e il resto dello script prosegue normalmente.
Questa guida copre Playwright e Puppeteer con Node.js. Il flusso è lo stesso per entrambi, e gli esempi usano risoluzioni proxyless, che Scarnox esegue sulla propria rete. PopularCaptcha è il tipo che Scarnox risolve oggi. Gli altri tipi di captcha sono in arrivo, quindi il codice qui sotto è solo per PopularCaptcha.
Il flusso
- Leggi la sitekey dalla pagina.
- Chiedi a Scarnox un token con una sola richiesta POST bloccante.
- Inserisci il token nel campo di risposta, oppure passalo alla callback del sito, poi invia il modulo.
Configura la chiave
Crea un account e inizia con il credito di prova gratuito, senza carta. Tieni la chiave in una variabile d'ambiente, mai nel codice di test:
export SCARNOX_KEY="scarnox_YOURAPIKEY"
Node 18 e versioni successive includono fetch, quindi la chiamata API non richiede pacchetti aggiuntivi.
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());
// Inserisci il token in entrambi i campi di risposta che il sito potrebbe leggere.
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]");
Nota che site_url viene da page.url(). È la pagina su cui vive il widget, che è ciò di cui il solver ha bisogno. La risoluzione avviene dopo che la pagina è stata caricata, quindi la sitekey è già nel DOM.
Puppeteer
Puppeteer differisce solo nell'API dei selettori. Riusa la funzione solve qui sopra e sostituisci le chiamate alla pagina:
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 il sito usa una callback
Alcuni siti non leggono il campo nascosto. Attendono invece la callback JavaScript del widget. Trova la funzione indicata nell'attributo data-callback del widget e chiamala con il token:
await page.evaluate((t) => {
const name = document.querySelector("[data-sitekey]")?.dataset.callback;
if (name && typeof window[name] === "function") window[name](t);
}, token);
Se il sito usa entrambi, imposta il campo e chiama la callback. Fare entrambe le cose è innocuo.
Widget invisibili
Un widget invisibile non ha una casella di spunta. Il sito lo avvia dal proprio codice, di solito quando il visitatore preme invio. Invia la stessa richiesta con il campo opzionale is_invisible impostato a true:
body: JSON.stringify({
captcha_type: "PopularCaptchaTaskProxyless",
site_key: sitekey,
site_url: url,
is_invisible: true,
}),
La guida ai captcha invisibili spiega come riconoscere questi widget e come restituire il token.
Consigli sulla tempistica
- Richiedi il token subito prima di inviare. I token scadono entro circa due minuti.
- Dai alla richiesta di risoluzione un timeout lungo, di circa 185 secondi. La chiamata si blocca finché il token è pronto.
- I solve falliti sono gratuiti, quindi un nuovo tentativo costa solo tempo. Ritenta gli errori transitori con backoff, come descritto in gestione degli errori e backoff.
- Stai eseguendo molti browser in parallelo? Limita il numero di risoluzioni simultanee, così un lotto lento non blocca il resto dell'esecuzione.
- Chiudi i browser in un blocco
finally. Un test che va in crash e lascia Chromium in esecuzione rallenterà tutte le risoluzioni successive sulla stessa macchina.
Testare il tuo flusso
Esegui l'esempio su un modulo di tua proprietà prima di puntarlo verso qualcosa di più grande. Verifica tre cose: il token viene accettato, il modulo si invia e compare la pagina successiva. Quando il flusso funziona dall'inizio alla fine, mantienilo verde in CI con una sola risoluzione reale per esecuzione.
Prova gratuita
Crea un account e inizia con il credito di prova gratuito, senza carta. È sufficiente per configurare e verificare l'intero flusso. Per il costo delle risoluzioni a volume, consulta le tariffe attuali.
Eseguire il debug di un token rifiutato
Quando il sito rifiuta un token, riproduci il flusso con una richiesta e un caricamento di pagina prima di cambiare altro. Verifica questi punti in ordine:
- La sitekey che hai inviato corrisponde al widget della pagina che hai caricato.
site_urlè la pagina su cui vive il widget, non la destinazione di un reindirizzamento.- Il token è stato inserito nel campo che la pagina legge, oppure la callback è stata chiamata.
- L'invio è avvenuto entro circa due minuti dalla risoluzione.
Salva l'HTML della pagina nel momento del fallimento con await page.content(). Confrontarlo con un'esecuzione funzionante di solito mostra la differenza a colpo d'occhio: una sitekey diversa, un campo mancante o un widget renderizzato in una modalità diversa.