So lösen Sie CaptchaFox in Node.js mit dem richtigen User-Agent

Um CaptchaFox in Node.js zu lösen, rufen Sie die Methode captchafox des Pakets capskip mit dem sitekey und der Seiten-URL auf und senden den zurückgegebenen Token dann im Feld cf-captcha-response ab. Schicken Sie dabei drei Dinge mit, die fetch nicht von selbst hinzufügt: die Cookies, die die Seite gesetzt hat, einen Referer und den User-Agent, den CapSkip zusammen mit dem Token zurückgegeben hat. Der User-Agent ist die Falle, in die der meiste Node-Code tappt. Das eingebaute fetch von Node teilt jedem Server mit, sein User-Agent sei node, und eine Website, die den Token von dem Browser erwartet, der ihn ausgestellt hat, lehnt dieses Absenden ab, ganz gleich, wie gut das Lösen war. CapSkip hat CaptchaFox in Version 1.4.0 hinzugefügt. Dieser Leitfaden behandelt das Auslesen des Schlüssels, den Aufruf, das Absenden, Wiederholungsversuche und das gleichzeitige Ausführen vieler Registrierungen.
Was Sie brauchen
- CapSkip 1.4.0 oder neuer auf einem Windows-Rechner. Dieses Release hat CaptchaFox hinzugefügt, zusammen mit Friendly Captcha und Capy Puzzle.
- Node.js 20.18.1 oder neuer, das älteste Release, das cheerio unterstützt. Verwenden Sie in einem neuen Projekt 22 oder 24, denn der Support für Node 20 ist im April 2026 ausgelaufen.
- Version 1.3.0 oder neuer des Pakets capskip, das erste Release mit einer Methode captchafox, dazu cheerio zum Auslesen des HTML.
- Die URL der Seite, die das Widget anzeigt. Der sitekey, die Formularfelder und der Build des Widgets stammen alle von dieser Seite.
- Eine Adresse für den Solver. Im Local-Modus antwortet CapSkip auf 127.0.0.1 nur für dieses Gerät; im Server-Modus lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, damit ein Skript auf einem anderen Rechner es über die API aufrufen kann. Eingestellt werden beide Modi unter Verbindungseinstellungen.
# capskip 1.3.0 added captchafox(); cheerio reads the HTML npm install capskip cheerio
Die Beispiele sind ES-Module mit Top-Level-await. Speichern Sie sie mit der Endung .mjs oder setzen Sie das Feld type in Ihrer package.json auf module, denn npm init trägt dort inzwischen commonjs ein. Das Paket capskip lässt sich auch mit require laden, wenn Ihr Projekt CommonJS verwendet.
Schritt 1: Die Seite abrufen, ihre Cookies behalten und den sitekey auslesen
Die fetch-Funktion von Node behält zwischen zwei Aufrufen keine Cookies. Das Session-Cookie, das die Seite setzt, ist also verschwunden, wenn Sie das Formular absenden. Eine Website, die ihren CSRF-Token an diese Session bindet, lehnt das Absenden dann ab. Führen Sie die Cookies deshalb selbst mit, und zwar mit Headers.getSetCookie(), das jede Set-Cookie-Zeile einzeln zurückgibt. Der Leitfaden zum Lösen von Friendly Captcha in Node.js erklärt dieses kleine Cookie-Jar ausführlicher.
// npm install capskip cheerio
import * as cheerio from "cheerio";
const PAGE_URL = "https://example.com/signup";
// fetch() keeps no cookies between calls, so carry the page's own.
const jar = new Map();
const page = await fetch(PAGE_URL);
for (const line of page.headers.getSetCookie()) {
const pair = line.split(";")[0];
const eq = pair.indexOf("=");
jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
const html = await page.text();
const $ = cheerio.load(html);Der sitekey ist öffentlich, für jeden Besucher identisch und beginnt üblicherweise mit sk_. Die meisten Seiten tragen ihn am Container des Widgets, einem div mit der Klasse captchafox und einem Attribut data-sitekey. Eine Seite, die das Widget aus ihrem eigenen Skript aufbaut, übergibt den Schlüssel stattdessen an einen Aufruf von captchafox.render. Wenn keins von beiden im abgerufenen HTML steht, öffnen Sie die DevTools: Der Schlüssel ist das Pfadsegment nach /captcha/ in einer Anfrage an api.captchafox.com, auf MAM-Seiten an mam-api.captchafox.com.
Prüfen Sie bei der Gelegenheit auch, welcher Build das Widget lädt. Die meisten Websites laden es von cdn.captchafox.com und erwarten einen einfachen Token. Manche Plattformen binden stattdessen ein Paket von s.uicdn.com/mampkg/ ein, und diese Websites erwarten einen Token, der mit MAM_ beginnt. Eine MAM-Seite kann den Schlüssel in der Adresse dieses Skripts selbst tragen, nach ?key=. Dasselbe CDN liefert auch andere, nicht verwandte Pakete aus. Gleichen Sie deshalb den Paketpfad von captchafox ab und nicht den Host.
// The container, matched on the whole class name.
const widget = $(".captchafox").first();
// MAM pages may carry the key in the package's script src instead.
const mamSrc = $('script[src*="mampkg/@mamdev/core.frontend.libs.captchafox"]').attr("src");
const sitekey = widget.attr("data-sitekey")
?? (mamSrc && new URL(mamSrc, PAGE_URL).searchParams.get("key"))
?? html.match(/captchafox\.render\([\s\S]{0,300}?sitekey\s*:\s*["']([^"']+)/)?.[1];
const form = widget.length ? widget.closest("form") : $("form").first();
console.log(sitekey, Boolean(mamSrc)); // a MAM page needs a MAM_ tokenDer Klassenselektor von cheerio vergleicht ganze Klassennamen, ein Wrapper-Element mit einer Klasse wie captchafox-wrapper wird also nicht versehentlich erfasst. Der letzte Fallback liest einen Schlüssel aus, der inline an den render-Aufruf übergeben wird. Ein Schlüssel, der in einer gebündelten Skriptdatei steckt, steht gar nicht im HTML, und dann finden Sie ihn über die DevTools. Ohne Container nimmt der Code das erste Formular der Seite. Hat die Seite außerdem ein Suchfeld oder ein zweites Formular, wählen Sie das Registrierungsformular stattdessen über seine id oder seine action aus.
Schritt 2: Der Aufruf von captchafox
Um CaptchaFox in Node.js zu lösen, brauchen Sie eine einzige Methode. Sie nimmt den sitekey, die Seiten-URL und ein optionales Options-Objekt entgegen, und für das Standard-Widget braucht sie nur die ersten beiden.
import { CapSkip } from "capskip";
const solver = new CapSkip({ host: "127.0.0.1", port: 8080 });
// Copied from the script tag on MAM pages.
const MAM_PACKAGE = "https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/";
const result = await solver.captchafox(sitekey, PAGE_URL, {
// undefined is dropped, so the standard widget sends nothing extra.
apiServer: mamSrc ? MAM_PACKAGE : undefined,
});
console.log(result.token.slice(0, 24)); // goes in cf-captcha-response
console.log(result.userAgent); // the browser that minted itresult.token und result.code enthalten dieselbe Zeichenfolge, und result.captchaId ist die ID, unter der CapSkip den Auftrag führt. result.userAgent ist die Identität des Browsers, der den Token ausgestellt hat. Das ist der eigene Browser von CapSkip und nichts, was Sie gesendet haben. Der Wert ist nur gesetzt, wenn das Lösen einen User-Agent gemeldet hat. Prüfen Sie ihn also, bevor Sie ihn verwenden.
Die Seiten-URL muss die Seite sein, auf der das Widget wirklich läuft. CaptchaFox registriert jeden Schlüssel für eine Liste erlaubter Domains und prüft den Host, bevor es irgendetwas ausstellt. Eine Seiten-URL auf einer anderen Domain wird bei jedem Versuch abgelehnt. Ein Kurzlink, ein Suchergebnis oder eine Weiterleitung von einer anderen Domain reicht also nicht.
Optionen, die die Methode annimmt
apiServer ist ein Alias für api_server, also für den Build des Widgets, für den gelöst wird. Die Methode verwirft jede Option, deren Wert undefined oder null ist, deshalb kann die Zeile oben sie bei jedem Aufruf übergeben. Außerdem nimmt sie ein proxy-Objekt mit den Schlüsseln type und uri an sowie useragent, das akzeptiert und bewusst nicht angewendet wird, denn CapSkip löst in einem echten Browser mit dessen eigener Identität. Ein timeout pro Aufruf in Sekunden ersetzt für diesen einen Lösevorgang das recaptchaTimeout des Clients. Auch die Obergrenze für das Abfrageintervall lässt sich pro Aufruf setzen, allerdings nur als polling_interval: Die camelCase-Schreibweise pollingInterval gehört zum Konstruktor und löst, pro Aufruf übergeben, eine ValidationException aus. Ein leerer sitekey oder eine leere URL, eine unbekannte Option und ein anderer Proxy-Typ als HTTP, HTTPS, SOCKS5 oder SOCKS5H lösen ebenfalls eine ValidationException aus, bevor überhaupt eine Anfrage Ihren Rechner verlässt.
In TypeScript beschreiben die mitgelieferten Typen token noch als optional und für ALTCHA gedacht, im Strict Mode brauchen Sie daher eine Non-Null-Assertion auf result.token. Der Wert ist bei jedem CaptchaFox-Lösevorgang vorhanden.
Schritt 3: Mit dem User-Agent absenden, der den Token ausgestellt hat
Wenn Sie CaptchaFox in Node.js lösen, entscheidet dieser Schritt darüber, ob die Website den Token annimmt. Ohne weitere Einstellung sendet das fetch von Node einen User-Agent-Header, der nur das Wort node enthält, unter Node 20 genauso wie unter Node 25. Das ist nicht der Browser, der den Token erzeugt hat. Übernehmen Sie also den zurückgegebenen Wert in die Anfrage, die den Token trägt:
// The form's own fields, hidden CSRF token included.
const body = new URLSearchParams(
form.serializeArray().map((f) => [f.name, f.value]),
);
body.set("email", "YOUR_EMAIL");
body.set("cf-captcha-response", result.token);
const headers = {
cookie: [...jar].map(([k, v]) => `${k}=${v}`).join("; "),
// Browsers send the page as Referer; strict servers want one.
referer: PAGE_URL,
};
// Without this line, fetch announces itself as user-agent: node.
if (result.userAgent) headers["user-agent"] = result.userAgent;
const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
method: "POST",
headers,
body,
});
console.log(res.status);serializeArray sammelt die Felder, die ein Browser senden würde, einschließlich des versteckten CSRF-Tokens und jeder angehakten Checkbox, Sie müssen sie also nie einzeln benennen. Den Absende-Button lässt es weg. Prüft der Server, ob der Name des Buttons mitkommt, fügen Sie ihn mit body.set hinzu. Der Referer ist genauso wichtig wie das Cookie. fetch sendet weder einen Referer noch einen Origin-Header, und manche Frameworks, darunter Django, lehnen einen HTTPS-Formular-POST ohne beides ab, selbst mit dem richtigen Cookie und Token.
Zwei weitere Regeln ergeben sich daraus, wie CaptchaFox den Token aufseiten der Website prüft. Jeder Token lässt sich nur einmal und nur für kurze Zeit verifizieren. Lösen Sie also erst, wenn Sie bereit sind, und senden Sie dann sofort ab. Und behandeln Sie den Token als opak: Kürzen Sie ihn nie und kodieren Sie ihn nie um. Manche Websites senden ihn aus ihrem eigenen Skript in einem JSON-Body. Senden Sie das Formular also einmal von Hand mit geöffneten DevTools ab und bilden Sie nach, was die Seite sendet. CaptchaFox zeigt in seiner Dokumentation zum Einstieg das Markup des Widgets und die serverseitige Prüfung.
Schritt 4: Wiederholungen, Challenge-Typen und Timeouts
Welche Challenge CaptchaFox ausspielt, können Sie nicht wählen. Bei den meisten Lösevorgängen wird gar nichts angezeigt, weil die Indizien des Browsers von selbst ausreichen, und wenn ein Slider-Puzzle erscheint, erledigt CapSkip es meistens. Bildauswahl- und Audio-Challenges sind selten und werden nicht gelöst: Sie kommen als ApiException zurück, deren Meldung ERROR_CAPTCHA_UNSOLVABLE enthält, und zwar innerhalb von Sekunden statt erst am Ende des Timeouts. Ein neuer Versuch bringt meist eine andere Challenge, Wiederholen lohnt sich also, entweder in CapSkip oder in Ihrem Code.
import { ApiException } from "capskip";
async function solveCaptchaFox(sitekey, url, options = {}, attempts = 3) {
for (let attempt = 1; ; attempt++) {
try {
return await solver.captchafox(sitekey, url, options);
} catch (err) {
// Select, audio and unreadable slide draws come back unsolvable;
// redraw. Anything else, such as a wrong API key, is final.
const redraw = err instanceof ApiException && err.message.includes("UNSOLVABLE");
if (!redraw || attempt === attempts) throw err;
}
}
}Die Meldung der ApiException ist die rohe Antwort von CapSkip, die Prüfung liest den Fehlercode also direkt daraus. Ein Haken: Ein Schlüssel, den die Website ablehnt, zum Beispiel auf der falschen Domain, erscheint ebenfalls als unlösbar und wird deshalb auch wiederholt. Er scheitert bei jedem Versuch innerhalb von Sekunden und gelingt nie, und genau daran erkennen Sie ihn. Der andere Weg ist die Einstellung Retries in der Zeile CaptchaFox der CapSkip-Einstellungen. Sie nimmt Werte von 0 bis 3 an und steht standardmäßig auf 0. CapSkip wiederholt dann nicht unterstützte Challenges, Versuche mit Zeitüberschreitung und Browserfehler von selbst, aber nie einen abgelehnten Schlüssel.
captchafox pollt mit dem recaptchaTimeout des Clients, standardmäßig 300 Sekunden, denn es handelt sich um eine echte Browser-Sitzung. CapSkip hat zusätzlich eigene Zeitlimits: Eine Aufgabe kann bis zu 250 Sekunden (Wait Timeout) auf einen der 10 CaptchaFox-Threads (Max. Threads) warten, und ein einzelner Versuch bekommt 150 Sekunden (Row Timeout). Ist ein Thread frei, erreicht Sie ein gescheiterter Versuch als ApiException, lange bevor die 300 Sekunden des SDK abgelaufen sind. Nach langer Wartezeit in der Warteschlange kann das SDK sein Limit zuerst erreichen und eine TimeoutException werfen, während CapSkip noch arbeitet. Übergeben Sie also ein längeres timeout, wenn Sie viele Lösevorgänge in die Warteschlange stellen oder Retries erhöhen, denn jede Wiederholung kann ein weiteres Row Timeout hinzufügen. Das SDK pollt außerdem über Verbindungsfehler hinweg weiter. Stoppt CapSkip mitten im Lösen, endet der Aufruf deshalb mit einer TimeoutException, nicht mit einer NetworkException.
Vollständiges lauffähiges Beispiel
Hier ist der gesamte Ablauf in einer Datei. signUp ruft die Seite ab, liest den Schlüssel aus, löst mit Wiederholungen und sendet das Formular mit seinen Cookies, einem Referer und dem User-Agent ab, der den Token ausgestellt hat.
// npm install capskip cheerio
// signup.mjs: run with node signup.mjs
import * as cheerio from "cheerio";
import {
CapSkip, ApiException, CapSkipError, TimeoutException, ValidationException,
} from "capskip";
const PAGE_URL = "https://example.com/signup";
// Copied from the script tag on MAM pages.
const MAM_PACKAGE = "https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/";
const solver = new CapSkip({
apiKey: process.env.CAPSKIP_API_KEY ?? "capskip",
host: process.env.CAPSKIP_HOST ?? "127.0.0.1",
port: Number(process.env.CAPSKIP_PORT ?? 8080),
});
async function solveCaptchaFox(sitekey, url, options = {}, attempts = 3) {
for (let attempt = 1; ; attempt++) {
try {
return await solver.captchafox(sitekey, url, options);
} catch (err) {
// Select, audio and unreadable slide draws come back unsolvable.
const redraw = err instanceof ApiException && err.message.includes("UNSOLVABLE");
if (!redraw || attempt === attempts) throw err;
console.log(`attempt ${attempt}: ${err.message}`);
}
}
}
async function signUp(pageUrl, email) {
// One jar per run, so runs side by side never share a session.
const jar = new Map();
const page = await fetch(pageUrl);
for (const line of page.headers.getSetCookie()) {
const pair = line.split(";")[0];
const eq = pair.indexOf("=");
jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
const html = await page.text();
const $ = cheerio.load(html);
const widget = $(".captchafox").first();
const mamSrc = $('script[src*="mampkg/@mamdev/core.frontend.libs.captchafox"]').attr("src");
const sitekey = widget.attr("data-sitekey")
?? (mamSrc && new URL(mamSrc, pageUrl).searchParams.get("key"))
?? html.match(/captchafox\.render\([\s\S]{0,300}?sitekey\s*:\s*["']([^"']+)/)?.[1];
if (!sitekey) throw new Error("No sitekey in the HTML; find it in DevTools.");
const form = widget.length ? widget.closest("form") : $("form").first();
const result = await solveCaptchaFox(sitekey, pageUrl, {
apiServer: mamSrc ? MAM_PACKAGE : undefined,
});
const body = new URLSearchParams(form.serializeArray().map((f) => [f.name, f.value]));
body.set("email", email);
body.set("cf-captcha-response", result.token);
const headers = {
cookie: [...jar].map(([k, v]) => `${k}=${v}`).join("; "),
referer: pageUrl,
};
if (result.userAgent) headers["user-agent"] = result.userAgent;
const action = new URL(form.attr("action") || pageUrl, pageUrl);
return fetch(action, { method: "POST", headers, body });
}
try {
const res = await signUp(PAGE_URL, "YOUR_EMAIL");
console.log(res.status, (await res.text()).slice(0, 80));
} catch (err) {
if (err instanceof ValidationException) console.error(`not sent: ${err.message}`);
else if (err instanceof TimeoutException) console.error("gave up after recaptchaTimeout");
else if (err instanceof CapSkipError) console.error(`solve failed: ${err.name}: ${err.message}`);
else throw err;
process.exitCode = 1;
}Wenn jeder Durchlauf innerhalb von Sekunden scheitert, prüfen Sie zuerst die Seiten-URL, denn ein Schlüssel, der außerhalb seiner registrierten Domains verwendet wird, scheitert jedes Mal auf dieselbe Weise. Wirft die Prüfung des sitekey eine Exception, baut die Seite das Widget aus einem gebündelten Skript auf, und der Schlüssel steht im Network-Tab, wie in Schritt 1 beschrieben. Dokumentiert ist der rohe Endpunkt hinter dieser Methode im CaptchaFox-Abschnitt der API-Referenz.
Viele Registrierungen parallel ausführen
Jede CapSkip-Methode gibt in Node ein Promise zurück, und AsyncCapSkip ist nur ein anderer Name für dieselbe Klasse, parallele Lösevorgänge sind also ganz normales JavaScript. Was Sie brauchen, ist eine Obergrenze. CapSkip führt standardmäßig 10 CaptchaFox-Lösevorgänge gleichzeitig aus und lässt die übrigen auf einen freien Thread warten. Ein kleiner Pool, der zehn gleichzeitig in Arbeit hält, passt also genau dazu. Setzen Sie ihn an die Stelle des try-Blocks am Ende des vollständigen Beispiels:
// At most `limit` calls in flight; a failure becomes that item's Error.
async function mapLimited(items, limit, fn) {
const results = new Array(items.length);
let next = 0;
async function worker() {
while (next < items.length) {
const i = next++;
results[i] = await fn(items[i]).catch((err) => err);
}
}
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
return results;
}
const emails = ["YOUR_EMAIL_1", "YOUR_EMAIL_2", "YOUR_EMAIL_3"];
const results = await mapLimited(emails, 10, (email) => signUp(PAGE_URL, email));
for (const r of results) console.log(r instanceof Error ? r.message : r.status);signUp ist die Funktion aus dem vollständigen Beispiel. Sie legt bei jedem Aufruf ein eigenes Cookie-Jar an, und genau das macht sie parallel sicher: Ein Jar, das sich zehn Durchläufe teilen, würde ihre Sessions und CSRF-Tokens durcheinanderbringen. Außerdem hält sie jeden Token bis zum Absenden mit seinem eigenen User-Agent zusammen. Für einen größeren Pool erhöhen Sie Max. Threads in den CaptchaFox-Einstellungen von CapSkip. CaptchaFox bewertet neben dem Browser auch das Netzwerk, sodass viele Lösevorgänge von einer einzigen Adresse über einen langen Durchlauf die Challenge-Rate steigen lassen. Richten Sie Proxys über den Button Configure Proxies in derselben Zeile ein, oder übergeben Sie bei jedem Aufruf ein proxy-Objekt.
Den Löser auf einer anderen Maschine betreiben
127.0.0.1 ist richtig, solange das Skript und CapSkip auf demselben Windows-PC laufen. Sobald der Node-Code auf einen VPS, in einen Container, auf einen CI-Runner oder in eine Serverless-Funktion umzieht, zeigt Loopback auf den falschen Rechner, und der erste Lösevorgang wirft eine NetworkException mit ECONNREFUSED. Schalten Sie CapSkip in den Server-Modus, dann lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, sodass jede dieser Umgebungen es über dieselbe API aufrufen kann. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, aktivieren Sie die Validierung des API-Schlüssels und beschränken Sie den Port mit einer Regel in der Windows Firewall auf die Adressen, die Sie erwarten. Der Solver bleibt Ihr eigener Windows-Rechner, und abgerechnet wird pro Lösung weiterhin nichts.
Der Client liest Umgebungsvariablen nicht von selbst. Lesen Sie CAPSKIP_HOST, CAPSKIP_PORT und CAPSKIP_API_KEY in Ihrem Code aus und übergeben Sie die Werte an den Konstruktor, wie es das vollständige Beispiel tut. So läuft dasselbe Skript an Ihrem Schreibtisch und auf einem Server.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| Die Website lehnt einen Token ab, den CapSkip als gelöst zurückgegeben hat | Das Absenden ging mit user-agent: node hinaus, dem Standardwert von fetch, nicht als der Browser, der den Token ausgestellt hat | Senden Sie result.userAgent als User-Agent-Header mit dieser Anfrage |
| Abgelehnt, obwohl der User-Agent stimmt | Die Seite lädt das MAM-Paket, und das Lösen hat das Standard-Widget verwendet, oder umgekehrt | Lesen Sie das Script-Tag und setzen Sie apiServer passend dazu |
| Ein 403 beim Absenden, obwohl Token und User-Agent gültig sind | Das Session-Cookie wurde nicht mitgesendet, sodass das CSRF-Feld nicht mehr passt, oder das Absenden erfolgte ohne Referer | Führen Sie die Cookies vom Abruf der Seite mit und senden Sie die Seiten-URL als Referer |
| result.userAgent ist undefined | Das Lösen hat keinen User-Agent gemeldet, also sendet fetch seinen eigenen | Behalten Sie die if-Prüfung bei; lehnt die Website node ab, senden Sie selbst den User-Agent eines aktuellen Desktop-Browsers |
| Eine ApiException innerhalb von Sekunden, jedes Mal, bei einem bestimmten Schlüssel | Die Seiten-URL liegt außerhalb der Domains, für die der Schlüssel registriert ist, oder der Schlüssel selbst ist falsch | Senden Sie die Seite, auf der das Widget läuft, keine Weiterleitung und kein Suchergebnis, und prüfen Sie den Schlüssel erneut |
| Gelegentlich eine ApiException mit ERROR_CAPTCHA_UNSOLVABLE | CaptchaFox hat eine Bildauswahl- oder Audio-Challenge ausgespielt oder einen Slider, den CapSkip nicht lesen konnte | Wiederholen Sie im Code, oder erhöhen Sie Retries in den CaptchaFox-Einstellungen von CapSkip |
| Ein Token, der einmal funktioniert hat, scheitert beim zweiten Mal | Jeder Token lässt sich nur einmal verifizieren und läuft schnell ab | Lösen Sie für jedes Absenden einen neuen Token und senden Sie sofort ab |
| ValidationException, bevor überhaupt etwas gesendet wird | Ein leerer sitekey oder eine leere URL, eine Option, die die Methode nicht annimmt, etwa ein pollingInterval pro Aufruf, oder ein Proxy-Typ, den CapSkip nicht akzeptiert | Prüfen Sie, ob der sitekey gefunden wurde, und korrigieren oder entfernen Sie die Option, die die Meldung nennt |
| TimeoutException nach fünf Minuten | Die Wartezeit auf einen freien CaptchaFox-Thread hat zusammen mit dem Lösen selbst die 300 Sekunden des SDK überschritten, oder CapSkip hat mitten im Lösen gestoppt | Begrenzen Sie den Pool auf Max. Threads, oder übergeben Sie für diesen Aufruf ein längeres timeout |
| NetworkException mit ECONNREFUSED oder ETIMEDOUT beim ersten Aufruf | CapSkip läuft nicht, Host oder Port sind falsch, oder eine Firewall verwirft die Verbindung | Starten Sie CapSkip und prüfen Sie dann, ob es im Local-Modus oder im Server-Modus laufen soll |
FAQ
Umgehen axios oder got das Problem mit dem User-Agent?
Nein. Sie ändern nur den Namen. axios sendet axios, gefolgt von seiner Versionsnummer, und got sendet got, gefolgt von der Adresse seines GitHub-Repositorys. Keiner von beiden behält Cookies oder sendet einen Referer, sofern Sie das nicht einrichten, es gelten also dieselben drei Header. Egal welcher Client: Setzen Sie den User-Agent aus result.userAgent auf die Anfrage, die den Token trägt.
Kann ich den Token stattdessen in Puppeteer oder Playwright verwenden?
Ja, solange die Seite, die ihn absendet, unter dem User-Agent läuft, den CapSkip zurückgegeben hat. Lösen Sie zuerst und öffnen Sie dann einen Playwright-Kontext mit browser.newContext({ userAgent: ua }), oder rufen Sie in Puppeteer page.setUserAgent(ua) auf, bevor die Seite lädt. Laden Sie das Formular, schreiben Sie den Token in das Feld cf-captcha-response und senden Sie ab. Wenn Ihre Automatisierung sonst keinen Browser braucht, ist der oben gezeigte Weg mit fetch schlanker.
Worin unterscheidet sich das von der Python-Version?
Der Aufruf und die Regeln sind dieselben, ebenso die wichtigste Falle: Der HTTP-Client sendet seinen eigenen Namen als User-Agent. In Python lautet dieser Name python-requests oder python-httpx, und eine requests.Session behält die Cookies für Sie. In Node lautet er node, und Sie führen die Cookies selbst mit. Die Python-Seite behandelt der Leitfaden zum Lösen von CaptchaFox in Python.
Die Kurzfassung
Um CaptchaFox in Node.js zu lösen, rufen Sie die Seite ab und behalten ihre Cookies, lesen den sitekey aus dem Container mit der Klasse captchafox, aus dem render-Aufruf oder aus dem MAM-Skript und achten darauf, welchen Build die Seite lädt. Rufen Sie solver.captchafox() mit dem sitekey und der echten Seiten-URL auf und fügen Sie apiServer nur für MAM-Seiten hinzu. Senden Sie result.token in cf-captcha-response mit den Cookies der Seite, der Seite als Referer und result.userAgent als User-Agent-Header ab, einmal und sofort. Versuchen Sie es bei unlösbaren Challenges erneut, halten Sie nicht mehr Lösevorgänge gleichzeitig in Arbeit als Max. Threads (standardmäßig 10), und wechseln Sie in den Server-Modus, sobald das Skript den Rechner des Solvers verlässt.
- Wie CaptchaFox funktioniert und was der Solver abdeckt: die Solver-Seite für CaptchaFox.
- Alle Captcha-Typen, die das Node.js-Paket verarbeitet: die Node.js-Captcha-Solver-Seite.
Eine neu ausgespielte Challenge ist meist die einzige Wiederholung, die dieser Typ braucht, und da ein unbegrenzter Captcha-Löser auf Ihrem eigenen Rechner läuft, kostet jede neue Challenge nur ein paar Sekunden statt einer weiteren abgerechneten Lösung.
