So lösen Sie Capy Puzzle in Node.js und behalten die Session

Um Capy Puzzle in Node.js zu lösen, rufen Sie die Seite ab und lesen den PUZZLE_-Schlüssel aus dem Widget-Skript aus. Übergeben Sie ihn zusammen mit der Seiten-URL an die Methode capy des Pakets capskip. Senden Sie dann die drei zurückgegebenen Werte in den Feldern capy_captchakey, capy_challengekey und capy_answer ab, zusammen und sofort, als Teil der Session, die die Seite abgerufen hat. Genau an dieser letzten Stelle scheitert Node-Code meistens. Das eingebaute fetch von Node behält keine Cookies und sendet keinen Referer. Der Formular-POST kommt dann ohne die Session an, die die Seite eröffnet hat, und die Website lehnt ihn ab, ganz gleich, wie korrekt die Antwort ist. CapSkip hat Capy Puzzle in Version 1.4.0 hinzugefügt. Dieser Leitfaden behandelt den Schlüssel, den Aufruf, die zweisekündige Haltezeit, das Absenden, Wiederholungen und das gleichzeitige Ausführen vieler Logins.
Was Sie brauchen
- CapSkip 1.4.0 oder neuer auf einem Windows-Rechner. Dieses Release hat Capy Puzzle hinzugefügt, zusammen mit CaptchaFox und Friendly Captcha.
- 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 capy, dazu cheerio zum Auslesen des Formulars.
- Die URL der Seite, die das Puzzle anzeigt. Der Schlüssel, der Capy-Host und die Formularfelder 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 capy(); cheerio reads the form 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 neuere Versionen von npm init tragen dort commonjs ein, und eine package.json ganz ohne Feld type gilt ebenfalls als CommonJS. 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 Schlüssel finden
Capy-Formulare liegen meist hinter einer Login- oder Registrierungsseite, die ein Session-Cookie setzt und einen CSRF-Token in das Formular schreibt. Die fetch-Funktion von Node behält zwischen zwei Aufrufen keine Cookies. Das Cookie ist also verschwunden, wenn Sie absenden, und der Token passt zu nichts mehr. Führen Sie die Cookies selbst mit. Headers.getSetCookie() gibt jede Set-Cookie-Zeile einzeln zurück, und mehr braucht der kleine Helfer addCookies unten nicht; MDN dokumentiert die Methode unter Headers.getSetCookie().
// npm install capskip cheerio
import * as cheerio from "cheerio";
const PAGE_URL = "https://example.com/login";
// fetch() keeps no cookies between calls, so carry them yourself.
const jar = new Map();
function addCookies(res) {
for (const line of res.headers.getSetCookie()) {
const pair = line.split(";")[0];
const eq = pair.indexOf("=");
if (eq > 0) jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
const page = await fetch(PAGE_URL);
addCookies(page);
const html = await page.text();
const $ = cheerio.load(html);
// The widget script's k= parameter, or a capy_captchakey input.
const key = html.match(/PUZZLE_[A-Za-z0-9_-]+/)?.[0];
// Everything before /puzzle/get_js/ is the Capy host.
const apiServer = html.match(/(https:\/\/[^"'\s<>]+?)\/puzzle\/get_js\//)?.[1];
// The form that holds the widget script or a capy_captchakey input.
const form = $("form")
.has('script[src*="/puzzle/get_js/"], input[name="capy_captchakey"]').first();
console.log(key, apiServer ?? "default host", form.length);Der Schlüssel ist öffentlich und für jeden Besucher identisch, und ein Puzzle-Schlüssel beginnt mit PUZZLE_. Die meisten Seiten tragen ihn im Parameter k der URL des Widget-Skripts, und diese URL steht im HTML, das fetch herunterlädt. Manche rendern außerdem ein verstecktes Eingabefeld capy_captchakey im Formular, und sobald das Widget in einem Browser gelaufen ist, existiert dieses Feld immer. Dort zeigen ihn auch die DevTools an. Ein einziger regulärer Ausdruck findet den Schlüssel an beiden Stellen.
Notieren Sie bei der Gelegenheit auch den Host des Skripts. Alles in der Skript-URL vor /puzzle/get_js/ ist die Capy API, hinter der der Schlüssel liegt, und CapSkip nennt sie api_server. Standardwert ist https://jp.api.capy.me, wo der Live-Dienst läuft. Sie brauchen den Wert also nur, wenn eine Seite das Widget von woanders lädt. Beispiele, die für andere Solver geschrieben wurden, zeigen oft api.capy.me ohne das regionale Präfix, und dieser Host wird nicht mehr aufgelöst. Lassen Sie ihn also weg, falls Sie ein solches Beispiel kopiert haben. Die Regex erwartet eine https://-URL; beginnt das src-Attribut stattdessen mit //, setzen Sie https: vor den Host, bevor Sie ihn übergeben.
Die form-Zeile wählt das Formular, das das Widget-Skript oder ein Eingabefeld capy_captchakey enthält. Lädt eine Seite das Skript im head oder unterhalb des Formulars und steht im Formular kein Eingabefeld capy_captchakey, bleibt form.length bei 0. Wählen Sie das Formular dort stattdessen über seine id oder seine action aus.
Schritt 2: Der Aufruf von solver.capy()
Um Capy Puzzle in Node.js zu lösen, brauchen Sie eine einzige Methode. Sie nimmt den Schlüssel, die Seiten-URL und ein optionales Options-Objekt entgegen, und für eine Seite auf dem Standard-Host braucht sie nur die ersten beiden.
// npm install capskip
import { CapSkip } from "capskip";
const solver = new CapSkip({
host: "127.0.0.1",
port: 8080,
// Capy answers after a two second hold; poll often enough to catch it.
pollingInterval: 0.5,
});
// undefined (no widget script in the HTML) is dropped, so CapSkip
// uses its default host.
const result = await solver.capy(key, PAGE_URL, { apiServer });
console.log(result.captchakey); // goes in capy_captchakey
console.log(result.challengekey); // goes in capy_challengekey
console.log(result.answer); // goes in capy_answerDas Ergebnis ist ein einfaches Objekt. Lesen Sie die drei benannten Felder aus. result.code enthält die gesamte Antwort des Servers, ein Objekt mit allen vier Feldern, das sich gut zum Loggen eignet, und result.respKey ist ein leerer String, der nur zur Kompatibilität mit anderen Diensten existiert. Die Antwort ist ein langer String, der ungefähr so beginnt: 0xax8ex0xax84x. Das ist der Ziehpfad, den das Widget beim Bewegen des Teils aufgezeichnet hätte, keine Koordinate.
Optionen, die die Methode annimmt
apiServer ist ein Alias für api_server, und die Methode verwirft jede Option, deren Wert undefined oder null ist. Deshalb kann der Aufruf oben das Ergebnis der Regex jedes Mal übergeben. Außerdem nimmt sie ein proxy-Objekt mit den Schlüsseln type und uri an sowie useragent, das bei der einzigen Anfrage mitgesendet wird, die CapSkip zum Abrufen des Puzzles stellt. Es gibt auch eine Option version, aber nur puzzle wird akzeptiert. Die andere Familie von Capy, avatar, ist eine andere Challenge hinter einem anderen Endpunkt, daher lehnt das SDK sie ab, statt eine Antwort zu liefern, die die Website zurückweisen würde.
Zwei Optionen pro Aufruf bringen viele ins Stolpern. Ein timeout in Sekunden ersetzt für diesen einen Lösevorgang das defaultTimeout 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, an solver.capy() übergeben, eine ValidationException aus, die die Option beim Namen nennt. TypeScript warnt Sie nicht, denn der Options-Typ akzeptiert jeden Schlüssel. Ein leerer Schlüssel 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.
Warum ein Capy-Lösevorgang etwa zwei Sekunden dauert
Die Erkennung selbst ist schnell. CapSkip ruft das Puzzle-Bild ab, findet die Lücke mit etwas Pixel-Mathematik und baut den Ziehpfad, ganz ohne Browser und ohne Modell. Dann wartet es, und zwar absichtlich.
Capy misst die Zeit zwischen der Ausgabe eines Puzzles und dem Eintreffen der Antwort und lehnt alles ab, was schneller ankommt, als ein Mensch das Teil hätte ziehen können. Die eigenen Tests von CapSkip gegen Capy setzen diese Untergrenze bei etwa einer Sekunde an, und die Ablehnung trägt dieselbe Meldung wie eine falsche Antwort. Eine korrekte, aber zu früh gelieferte Lösung sieht deshalb genau aus wie ein kaputter Solver. CapSkip hält daher jedes Capy-Ergebnis zurück, bis seit dem Abruf des Puzzles zwei Sekunden vergangen sind. Das Warten ist ein Sleep, es kostet also Latenz und keine CPU.
Mit den Standardeinstellungen dauert ein Aufruf eher vier Sekunden, und der Grund ist das Polling. Das SDK fragt das Ergebnis ab, sobald der Job eingegangen ist, dann erneut nach einer Viertelsekunde. Danach verdoppelt es den Abstand immer weiter bis zu seinem pollingInterval, standardmäßig 5 Sekunden. Die Abfragen fallen auf 0, 0.25, 0.75, 1.75 und 3.75 Sekunden, eine Antwort nach zwei Sekunden wird also bei der fünften Abfrage abgeholt. Mit pollingInterval: 0.5 im Konstruktor, wie oben, kehrte der Aufruf in unseren Tests etwa 2.3 Sekunden nach dem Senden des Jobs zurück. Bauen Sie vor dem Absenden keine eigene Verzögerung ein, und versuchen Sie nicht, die Haltezeit einzusparen: Genau sie sorgt dafür, dass die Antwort durchgeht.
Schritt 3: Alle drei Werte mit den Cookies der Seite absenden
Die drei Werte gehören in die Felder, die das Widget selbst in das Formular geschrieben hätte. Senden Sie sie zusammen ab, in einer einzigen Anfrage, mit den Cookies aus Schritt 1 und der Seite als Referer. Setzen Sie beim Absenden außerdem redirect auf manual, damit die Session, die Ihnen die Website zurückgibt, bei Ihnen bleibt.
// The form's own fields, hidden CSRF token included.
const body = new URLSearchParams(
form.serializeArray().map((f) => [f.name, f.value]),
);
// Use the page's own field names, password included.
body.set("username", "YOUR_USERNAME");
body.set("capy_captchakey", result.captchakey);
body.set("capy_challengekey", result.challengekey);
body.set("capy_answer", result.answer);
const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
method: "POST",
headers: {
cookie: [...jar].map(([k, v]) => `${k}=${v}`).join("; "),
// fetch sends no Referer; strict servers refuse a post without one.
referer: PAGE_URL,
},
body,
// A login answers 302 with a new session cookie; keep it.
redirect: "manual",
});
addCookies(res);
console.log(res.status, res.headers.get("location"));serializeArray sammelt die Felder, die ein Browser senden würde, einschließlich des versteckten CSRF-Tokens, Sie müssen sie also nie einzeln benennen. Den Absende-Button lässt es weg. Prüft der Server den Namen des Buttons, fügen Sie ihn deshalb mit body.set hinzu. body.set ersetzt außerdem ein Eingabefeld capy_captchakey, das die Seite selbst gerendert hat, sodass der Schlüssel nie doppelt hinausgeht. Der Referer ist genauso wichtig wie das Cookie. fetch sendet weder Referer noch Origin, und manche Frameworks, darunter Django, lehnen einen HTTPS-Formular-POST ab, der keins von beiden hat, selbst mit dem richtigen Cookie, dem richtigen Token und der richtigen Antwort.
Die Einstellung redirect erhält die Session nach einem erfolgreichen Login. Ein Login antwortet meist mit 302 und einem frischen Session-Cookie, denn Frameworks wie Django erneuern an dieser Stelle die Session. Sich selbst überlassen, folgt fetch der Weiterleitung ohne dieses Cookie, landet wieder auf der Login-Seite und meldet 200. Mit redirect auf manual erhalten Sie die 302-Antwort selbst, und addCookies legt das neue Cookie für die folgenden Anfragen in das Cookie-Jar.
Senden Sie die Antwort genau so ab, wie sie zurückkam. Das Backend der Website prüft sie gegen das ausgegebene Puzzle, daher macht jedes Kürzen oder Neuzusammensetzen sie ungültig. URLSearchParams kodiert den Body per Prozentkodierung, und das ist in Ordnung, denn der Server dekodiert ihn vor der Prüfung. Senden Sie dann zügig ab. CapSkip erzeugt für jeden Lösevorgang einen frischen Challenge-Key, und Capy bindet das Puzzle, das es ausgibt, an diesen Key. Der Key ist also nur einmal verwendbar und kurzlebig, und eine Lösung deckt ein Absenden ab. Manche Seiten senden über ihr eigenes Skript mit einem JSON-Body statt als Formular-POST. Senden Sie einmal von Hand mit geöffneten DevTools ab und setzen Sie die drei Werte dort ein, wo die Seite sie einsetzt.
Schritt 4: Wiederholungen, wenn ein Lösevorgang scheitert
Wenn Sie Capy Puzzle in Node.js in nennenswertem Umfang lösen, werden einige Lösungsversuche scheitern. Ein gescheiterter Lösevorgang kommt als ApiException an, deren Meldung ERROR_CAPTCHA_UNSOLVABLE enthält, und über res.php, den Endpunkt, den das SDK abfragt, deckt dieser eine Code zwei verschiedene Situationen ab. Eine verfehlte Lücke kommt gelegentlich vor, und der nächste Versuch bekommt ein ganz neues Puzzle auf einem anderen Foto, er gelingt daher meist. Ein Schlüssel, den die Capy API ablehnt, scheitert bei jedem Versuch, und der Zähler Bad Keys auf der Capy-Karte im Monitor-Tab von CapSkip steigt. Wiederholen Sie im ersten Fall und beheben Sie den zweiten.
import { ApiException } from "capskip";
async function solveCapy(key, url, options = {}, attempts = 3) {
for (let attempt = 1; ; attempt++) {
try {
return await solver.capy(key, url, options);
} catch (err) {
// A missed hole reads as unsolvable, and the next try draws a
// new puzzle. Anything else, such as a wrong API key, is final.
const miss = err instanceof ApiException && err.message.includes("UNSOLVABLE");
if (!miss || attempt === attempts) throw err;
}
}
}Die Meldung der ApiException ist die rohe Antwort von CapSkip, daher liest die Prüfung den Fehlercode direkt daraus. Ein abgelehnter Schlüssel erscheint ebenfalls als unlösbar und wird hier deshalb auch wiederholt, und er scheitert bei jedem Versuch auf dieselbe Weise. Genau daran erkennen Sie ihn. Der andere Weg ist die Einstellung Retries im Capy-Bereich der CapSkip-Einstellungen, die Werte von 0 bis 3 annimmt und standardmäßig auf 0 steht. CapSkip wiederholt eine verfehlte Lücke dann von selbst, einen abgelehnten Schlüssel aber nie, weil er genauso abgelehnt würde.
Die Methode capy pollt mit dem defaultTimeout des Clients, 120 Sekunden, denn ein Capy-Lösevorgang besteht aus einem Abruf und etwas Arithmetik und nicht aus einer Browser-Sitzung. CapSkip misst zusätzlich selbst die Zeit. In seinen Capy-Einstellungen kann eine Aufgabe bis zu 250 Sekunden (Wait Timeout) auf einen der 10 Threads (Max. Threads) warten, und ein Versuch bekommt 60 Sekunden (Row Timeout). Ein normaler Lösevorgang passt um ein Vielfaches in all das hinein. Das SDK pollt außerdem durch Verbindungsfehler hindurch weiter. Stoppt CapSkip mitten im Lösevorgang, endet der Aufruf daher mit TimeoutException, nicht mit NetworkException.
Vollständiges lauffähiges Beispiel
Hier ist der gesamte Ablauf in einer Datei. logIn ruft die Seite in ein eigenes Cookie-Jar ab, liest Schlüssel und Host aus, löst mit Wiederholungen und sendet das Formular mit seinen Cookies und einem Referer ab. Die Funktion gibt die Antwort und das Cookie-Jar zurück, sodass die Anfragen, die Sie nach dem Login stellen, die neue Session mitführen.
// npm install capskip cheerio
// login.mjs: run with node login.mjs
import * as cheerio from "cheerio";
import {
CapSkip, ApiException, CapSkipError, TimeoutException, ValidationException,
} from "capskip";
const PAGE_URL = "https://example.com/login";
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),
pollingInterval: 0.5,
});
async function solveCapy(key, url, options = {}, attempts = 3) {
for (let attempt = 1; ; attempt++) {
try {
return await solver.capy(key, url, options);
} catch (err) {
// A missed hole reads as unsolvable; the next try draws a new puzzle.
const miss = err instanceof ApiException && err.message.includes("UNSOLVABLE");
if (!miss || attempt === attempts) throw err;
console.log(`attempt ${attempt}: ${err.message}`);
}
}
}
function addCookies(jar, res) {
for (const line of res.headers.getSetCookie()) {
const pair = line.split(";")[0];
const eq = pair.indexOf("=");
if (eq > 0) jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
async function logIn(pageUrl, username) {
// One jar per run, so runs side by side never share a session.
const jar = new Map();
const page = await fetch(pageUrl);
addCookies(jar, page);
const html = await page.text();
const $ = cheerio.load(html);
const key = html.match(/PUZZLE_[A-Za-z0-9_-]+/)?.[0];
if (!key) throw new Error("No PUZZLE_ key in the HTML; find it in DevTools.");
const apiServer = html.match(/(https:\/\/[^"'\s<>]+?)\/puzzle\/get_js\//)?.[1];
const form = $("form")
.has('script[src*="/puzzle/get_js/"], input[name="capy_captchakey"]').first();
if (!form.length) throw new Error("No form holds the widget; select it by id.");
const result = await solveCapy(key, pageUrl, { apiServer });
// All three together, straight away: the challenge key is single-use.
const body = new URLSearchParams(form.serializeArray().map((f) => [f.name, f.value]));
// Use the page's own field names, password included.
body.set("username", username);
body.set("capy_captchakey", result.captchakey);
body.set("capy_challengekey", result.challengekey);
body.set("capy_answer", result.answer);
const action = new URL(form.attr("action") || page.url, page.url);
const res = await fetch(action, {
method: "POST",
headers: {
cookie: [...jar].map(([k, v]) => `${k}=${v}`).join("; "),
referer: page.url,
},
body,
// A login answers 302 with a new session cookie. Keep the 302, so
// its Set-Cookie lands in the jar instead of being followed away.
redirect: "manual",
});
addCookies(jar, res);
return { res, jar };
}
try {
const { res, jar } = await logIn(PAGE_URL, "YOUR_USERNAME");
// A 302 to the account page means it worked; jar holds that session.
console.log(res.status, res.headers.get("location"), [...jar.keys()]);
} catch (err) {
if (err instanceof ValidationException) console.error(`not sent: ${err.message}`);
else if (err instanceof TimeoutException) console.error("gave up after defaultTimeout");
else if (err instanceof CapSkipError) console.error(`solve failed: ${err.name}: ${err.message}`);
else throw err;
process.exitCode = 1;
}Scheitern alle drei Versuche, sehen Sie sich zuerst den Schlüssel an, denn ein abgelehnter Schlüssel scheitert jedes Mal gleich, während ein Fehltreffer fast nie dreimal hintereinander passiert. Wirft die Prüfung des Schlüssels einen Fehler, baut die Seite das Widget aus einem gebündelten Skript auf: Öffnen Sie den Network-Tab und kopieren Sie Schlüssel und Host aus der eigenen Anfrage des Widgets. Den rohen Endpunkt hinter dieser Methode finden Sie im Capy-Abschnitt der API-Referenz.
Viele Logins parallel ausführen
Jede CapSkip-Methode in Node gibt ein Promise zurück, und AsyncCapSkip ist nur ein anderer Name für dieselbe Klasse. Lösevorgänge parallel auszuführen ist also ganz normales JavaScript. Was Sie brauchen, ist eine Obergrenze. CapSkip führt standardmäßig 10 Capy-Lösevorgänge gleichzeitig aus und lässt den Rest auf einen freien Thread warten. Ein kleiner Pool, der zehn gleichzeitig in Arbeit hält, passt also genau dazu. Setzen Sie ihn anstelle des try-Blocks am Ende des vollständigen Beispiels ein:
// 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 users = ["YOUR_USERNAME_1", "YOUR_USERNAME_2", "YOUR_USERNAME_3"];
const results = await mapLimited(users, 10, (user) => logIn(PAGE_URL, user));
for (const r of results) console.log(r instanceof Error ? r.message : r.res.status);logIn baut bei jedem Aufruf ein eigenes Cookie-Jar, und genau das macht die Funktion parallel sicher. Ein einziges Cookie-Jar für zehn Durchläufe würde deren Sessions und CSRF-Tokens durcheinanderbringen. Außerdem bleibt so jeder Lösevorgang mit seinem eigenen Absenden gepaart. Wer erst hundert Puzzles löst und danach absendet, lässt die frühesten Challenge-Keys altern, während die letzten Lösungen noch laufen. Bei hohem Volumen fügen Sie außerdem Proxys hinzu. Jeder Lösevorgang holt ein neues Puzzle von der Capy API, und ein stetiger Strom solcher Abrufe von einer einzigen Adresse ist genau das Muster, für das es Rate Limiting gibt. Konfigurieren Sie einen Proxy-Pool im Capy-Bereich von CapSkip, oder übergeben Sie bei jedem Aufruf ein proxy-Objekt; der Proxy betrifft nur den Abruf des Puzzles, die einzige Anfrage, die ein Capy-Lösevorgang stellt.
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, und aktivieren Sie die Validierung des API-Schlüssels. CapSkip öffnet seinen Port beim Start des Server-Modus für jede Adresse. Um ihn auf die Rechner zu beschränken, die Sie erwarten, fügen Sie in der Windows Firewall eine Blockierregel für alle anderen hinzu. 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 meldet, die Session sei abgelaufen, obwohl solver.capy() alle drei Werte geliefert hat | Das Absenden ging ohne die Cookies hinaus, die die Seite gesetzt hat, daher passt das CSRF-Feld zu keiner Session mehr | Führen Sie die Cookies aus dem Seitenabruf mit, ein Cookie-Jar pro Durchlauf |
| Das Absenden liefert 200 und die Login-Seite, obwohl die Website den Login akzeptiert hat | Der Login hat mit 302 und einem neuen Session-Cookie geantwortet, und fetch ist der Weiterleitung ohne dieses Cookie gefolgt | Senden Sie mit redirect auf manual ab und legen Sie die Set-Cookie-Zeilen der 302-Antwort in das Cookie-Jar |
| Ein 403 beim Absenden, trotz richtigem Cookie und allen drei Werten | fetch hat weder Referer noch Origin gesendet, und die Website prüft beide | Senden Sie die Seiten-URL als Referer-Header |
| Die Website lehnt die Antwort selbst ab | Die Antwort wurde verändert, oder nur ein oder zwei der Werte sind im Formular angekommen | Senden Sie result.answer unverändert zusammen mit den anderen beiden Werten in einer Anfrage |
| Ein Absenden, das einmal funktioniert hat, scheitert beim zweiten Mal | Der Challenge-Key ist nur einmal verwendbar und kurzlebig | Lösen Sie für jedes Absenden neu und senden Sie sofort ab |
| Eine ApiException bei jedem Versuch, bei einem bestimmten Schlüssel | Die Capy API hat den Schlüssel abgelehnt, oder api_server zeigt auf den falschen Host | Kopieren Sie den Schlüssel erneut, einschließlich Präfix, und prüfen Sie den Host des Script-Tags |
| Gelegentlich eine ApiException mit ERROR_CAPTCHA_UNSOLVABLE | CapSkip konnte die Lücke in diesem Puzzle nicht finden | Versuchen Sie es erneut; der nächste Versuch bekommt ein anderes Puzzle |
| ValidationException, bevor überhaupt etwas gesendet wird | Ein leerer Schlüssel oder eine leere URL, eine Option, die die Methode nicht annimmt, etwa ein pollingInterval pro Aufruf, avatar als Version oder ein Proxy-Typ, den CapSkip nicht akzeptiert | Prüfen Sie, ob der Schlüssel gefunden wurde, und korrigieren oder entfernen Sie die Option, die die Meldung nennt |
| Eine ApiException mit ERROR_KEY_DOES_NOT_EXIST oder ERROR_WRONG_USER_KEY beim ersten Aufruf | In CapSkip ist die Validierung des API-Schlüssels aktiv, und das Skript hat den Standardschlüssel oder gar keinen gesendet | Setzen Sie CAPSKIP_API_KEY auf einen in CapSkip konfigurierten Schlüssel |
| Jeder Aufruf dauert etwa vier Sekunden | Der Abstand zwischen den Abfragen verdoppelt sich immer weiter bis zur Standard-Obergrenze von 5 Sekunden, daher wartet eine Antwort, die nach zwei Sekunden bereitsteht, auf die Abfrage bei 3.75 Sekunden | Setzen Sie pollingInterval: 0.5 im Konstruktor |
| TimeoutException unter hoher paralleler Last | Mehr laufende Logins, als 10 Threads innerhalb von 120 Sekunden abarbeiten können, oder CapSkip wurde mitten im Lösevorgang gestoppt | Begrenzen Sie den Pool auf Max. Threads, oder übergeben Sie für diesen Aufruf ein längeres timeout |
| NetworkException beim ersten Aufruf, mit ECONNREFUSED oder ETIMEDOUT in der Meldung oder in err.cause.code, wenn der Host ein Name ist | 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
Funktioniert das Paket capskip hier mit TypeScript?
Ja, es bringt eigene Typdefinitionen mit. Für Capy sind zwei Details wichtig. Der Ergebnistyp markiert captchakey, challengekey und answer als optional, weil andere Methoden sie nicht setzen. Im Strict Mode brauchen Sie daher eine Prüfung oder eine Non-Null-Assertion, bevor Sie die Werte an body.set übergeben. Und der Options-Typ akzeptiert jeden Schlüssel, sodass eine falsch geschriebene oder falsch platzierte Option, etwa ein pollingInterval pro Aufruf, sauber kompiliert und erst zur Laufzeit mit einer ValidationException scheitert.
Führt der User-Agent node dazu, dass die Antwort abgelehnt wird?
Von Capy nicht. Eine Capy-Antwort ist nicht an einen Browser gebunden, CapSkip gibt dafür keinen User-Agent zurück, und der optionale User-Agent, den Sie übergeben können, verändert nur die Anfrage, die das Puzzle abruft. Die Website selbst kann eine Anfrage mit dem User-Agent node aus eigenen Gründen trotzdem abweisen. Wenn das passiert, setzen Sie sowohl beim Abruf der Seite als auch beim Absenden den User-Agent-Header eines Browsers.
Worin unterscheidet sich das von der Python-Version?
Der Aufruf, die Haltezeit und die Regeln sind dieselben. In Python verwaltet requests.Session die Cookies für Sie, während Sie sie in Node selbst mitführen, und das Ergebnis ist dort ein dict und hier ein Objekt. Die Python-Variante finden Sie im Leitfaden zum Lösen von Capy Puzzle in Python.
Kann ein Node-Skript auf einer gehosteten Plattform den Solver erreichen?
Ja. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, damit es auf einer Netzwerkadresse statt auf Loopback lauscht, lesen Sie diese Adresse in Ihrem Skript aus CAPSKIP_HOST und übergeben Sie sie an den Konstruktor. Ein VPS, ein Container-Host, ein CI-Runner und eine Serverless-Funktion verbinden sich alle über dieselbe HTTP-API. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, zusammen mit einer Blockierregel in der Windows Firewall für Adressen, die den Port nicht erreichen sollen. Der Solver bleibt auf Hardware, die Ihnen gehört, an der Art, wie Lösungen gezählt werden, ändert sich also nichts.
Die Kurzfassung
Um Capy Puzzle in Node.js zu lösen, rufen Sie die Seite in ein Cookie-Jar ab, ziehen den PUZZLE_-Schlüssel mit einer einzigen Regex heraus und notieren den Host des Widget-Skripts, falls es nicht der Standard ist. Rufen Sie solver.capy() mit dem Schlüssel und der echten Seiten-URL über einen Client auf, der mit pollingInterval: 0.5 erstellt wurde, und lassen Sie ihm seine etwa zwei Sekunden. Senden Sie captchakey, challengekey und answer in den drei capy_-Feldern ab, unverändert, zusammen und sofort, mit den Cookies der Seite und der Seite als Referer, und behalten Sie das Cookie, das eine 302-Antwort zurückgibt. Wiederholen Sie den gelegentlichen Fehltreffer, halten Sie nicht mehr Logins gleichzeitig in Arbeit als Max. Threads, und wechseln Sie in den Server-Modus, sobald das Skript den Rechner des Solvers verlässt.
- Wie der Typ funktioniert und was der Solver abdeckt: die Capy-Puzzle-Solver-Seite.
- Alle Captcha-Typen, die das Node.js-Paket verarbeitet: die Node.js-Captcha-Solver-Seite.
Noch ein letzter Punkt zu dieser Wiederholung. Jeder Versuch bekommt ein anderes Puzzle, daher ist ein neuer Anlauf die richtige Abhilfe bei einem Fehltreffer, und da ein lokaler Captcha-Löser auf Ihrem eigenen Rechner läuft, kostet ein zweiter Versuch ein paar Sekunden statt einer weiteren abgerechneten Lösung.
