Scarnox · Blog

PopularCaptcha in Playwright und Puppeteer lösen (Node.js)

Browser-Automatisierung bleibt in dem Moment stehen, in dem ein PopularCaptcha erscheint. Sich aus einem Skript heraus durch ein Bildraster zu klicken ist langsam und fragil. Ein Token von einer Lösungs-API zu holen und in die Seite einzufügen braucht einen einzigen Aufruf, und der Rest deines Skripts läuft normal weiter.

Dieser Leitfaden behandelt Playwright und Puppeteer mit Node.js. Der Ablauf ist in beiden gleich, und die Beispiele verwenden proxyless-Lösungen, die Scarnox in seinem eigenen Netzwerk ausführt. PopularCaptcha ist der Typ, den Scarnox heute löst. Andere Captcha-Typen kommen demnächst, daher gilt der Code unten nur für PopularCaptcha.

Der Ablauf

  1. Den Sitekey von der Seite lesen.
  2. Mit einer blockierenden POST-Anfrage ein Token bei Scarnox anfordern.
  3. Das Token in das Antwortfeld schreiben oder dem Callback der Seite übergeben und dann das Formular absenden.

Den Schlüssel einrichten

Erstelle ein Konto und starte mit kostenlosem Testguthaben, ohne Karte. Halte den Schlüssel in einer Umgebungsvariable, niemals im Testcode:

export SCARNOX_KEY="scarnox_YOURAPIKEY"

Node 18 und neuer enthalten fetch, daher braucht der API-Aufruf keine zusätzlichen Pakete.

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

// Das Token in beide Antwortfelder schreiben, die die Seite lesen könnte.
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]");

Beachte, dass site_url aus page.url() kommt. Das ist die Seite, auf der das Widget liegt, und genau das braucht der Solver. Die Lösung passiert, nachdem die Seite geladen ist, also steht der Sitekey bereits im DOM.

Puppeteer

Puppeteer unterscheidet sich nur in der Selektor-API. Verwende die Funktion solve von oben wieder und ersetze die Seitenaufrufe:

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

Wenn die Seite einen Callback verwendet

Manche Seiten lesen das versteckte Feld nicht. Sie warten stattdessen auf den JavaScript-Callback des Widgets. Finde die Funktion, die im Attribut data-callback des Widgets genannt wird, und rufe sie mit dem Token auf:

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

Verwendet die Seite beides, setze das Feld und rufe den Callback auf. Beides zu tun schadet nicht.

Unsichtbare Widgets

Ein unsichtbares Widget hat kein Kästchen. Die Seite startet es aus ihrem eigenen Code, meist wenn der Besucher auf Absenden drückt. Sende dieselbe Anfrage mit dem optionalen Feld is_invisible auf true:

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

Der Leitfaden unsichtbare Captchas erklärt, wie du diese Widgets erkennst und wie du das Token zurückgibst.

Tipps zum Timing

Den eigenen Ablauf testen

Führe das Beispiel gegen ein Formular aus, das dir gehört, bevor du es auf etwas Größeres richtest. Prüfe drei Dinge: Das Token wird akzeptiert, das Formular wird abgesendet und die nächste Seite erscheint. Wenn der Ablauf von Anfang bis Ende funktioniert, halte ihn in der CI grün, mit einer einzigen echten Lösung pro Lauf.

Kostenlos ausprobieren

Erstelle ein Konto und starte mit kostenlosem Testguthaben, ohne Karte. Das reicht, um den ganzen Ablauf einzurichten und zu prüfen. Was das Lösen im größeren Umfang kostet, steht auf der Preisseite.

Einen abgelehnten Token debuggen

Wenn die Seite ein Token ablehnt, reproduziere den Ablauf mit einer Anfrage und einem Seitenaufruf, bevor du etwas anderes änderst. Prüfe der Reihe nach:

Speichere das Seiten-HTML im Moment des Fehlers mit await page.content(). Der Vergleich dieses HTMLs mit einem funktionierenden Lauf zeigt den Unterschied meist auf einen Blick: ein anderer Sitekey, ein fehlendes Feld oder ein Widget, das in einem anderen Modus gerendert wurde.

Nächste Schritte