So lösen Sie Captchas in Node-RED mit einem function-Node

Ein Captcha-Schritt in Node-RED besteht aus zwei Nodes, wenn Sie über die rohe API gehen, oder aus einem einzigen function-Node, wenn Sie lieber fünf Zeilen JavaScript schreiben. Beide rufen einen Löser auf, der auf Ihrer eigenen Hardware läuft, keiner von beiden zählt also pro Lösung ab. Woran Leute scheitern, ist nicht das Lösen. Es ist, dass ein function-Node kein npm-Paket per require laden kann, solange Sie das nicht in settings.js einschalten, und dass Node-RED sehr oft auf einem Raspberry Pi läuft, der Löser aber nicht.
Was Sie brauchen
- Node-RED 3 oder neuer, egal wo es läuft.
- CapSkip muss laufen und lauschen. Local-Modus, wenn Node-RED auf derselben Windows-Maschine läuft, Server-Modus, wenn nicht. Beide werden beschrieben unter Verbindungseinstellungen.
- Die Seiten-URL des geschützten Formulars und dessen sitekey.
- Bearbeitungszugriff auf settings.js, wenn Sie den SDK-Weg statt des Weges über die rohe API wollen.
Nichts davon braucht einen Browser. Node-RED steuert kein Chrome, es macht HTTP-Aufrufe, der Flow liest den sitekey also aus dem HTML der Seite und sendet den Token als gewöhnliches Formularfeld zurück.
Zwei Wege, den Löser aus einem Flow aufzurufen
Entscheiden Sie sich vor dem Verdrahten für einen, denn sie führen zu sehr unterschiedlichen Flows.
| Welcher Weg | Was es Sie kostet | Wann er der richtige ist |
|---|---|---|
| http request-Nodes gegen die rohe API | Ein Node zum Absenden, ein delay, ein Node zum Abfragen und ein switch als Schleife | Sie können settings.js nicht bearbeiten, oder der Flow soll auf der Arbeitsfläche lesbar bleiben |
| Das Node-SDK in einem function-Node | Eine Zeile in settings.js und ein Modul im Reiter Setup | Sie wollen, dass Polling, Backoff und Timeouts für Sie erledigt werden |
Der zweite Weg ist kürzer, und der Grund dafür ist wissenswert. Die rohe API sagt Ihnen, Sie sollen fünfzehn Sekunden warten und dann alle fünf Sekunden abfragen, und ein handgebauter Flow hält sich wörtlich daran. Das SDK beginnt stattdessen nach 250 ms mit dem Abfragen und vergrößert die Abstände bis zu einer Obergrenze, es liefert einen Token also meist spürbar früher, als ein delay-Node es je tun wird.
Weg eins: http request-Nodes und die rohe API
Die API ist 2captcha-kompatibel, das bedeutet zwei Endpunkte. Sie senden an in.php und bekommen eine id zurück, dann fragen Sie res.php mit dieser id ab, bis nicht mehr gemeldet wird, die Antwort sei noch nicht bereit. Vier Nodes, in einer Schleife verdrahtet.
| Node im Flow | Einstellung |
|---|---|
| Der http request-Node, der absendet | POST an http://127.0.0.1:8080/in.php, Return auf ein geparstes JSON-Objekt gesetzt |
| Ein function-Node, der die id behält | msg.payload.request als msg.captchaId speichern |
| Ein delay-Node | Feste Verzögerung, 15 Sekunden für reCAPTCHA v2 |
| Der http request-Node, der abfragt | GET an http://127.0.0.1:8080/res.php, Return auf ein geparstes JSON-Objekt gesetzt |
| Ein switch-Node | Zurück zum delay, solange die Antwort noch CAPCHA_NOT_READY lautet |
Der sendende Node nimmt seinen Body aus msg.payload, bauen Sie den also in einem vorgelagerten function-Node auf. Erst json auf 1 macht die Antworten zu JSON statt zum älteren, mit Pipes getrennten Text, was Ihnen ein String-Splitting erspart.
// Feed this into the submit node. No npm module needed.
msg.url = "http://127.0.0.1:8080/in.php";
msg.method = "POST";
msg.payload = {
key: env.get("CAPSKIP_KEY") || "capskip",
method: "userrecaptcha",
googlekey: "YOUR_SITEKEY",
pageurl: "https://example.com/page-with-recaptcha",
json: 1
};
return msg;Der abfragende Node braucht die id wieder im Query-String. Bauen Sie die URL in einem function-Node, damit der http request-Node nichts mehr per Template einsetzen muss.
// After the delay. Loop back here until the answer arrives.
const key = env.get("CAPSKIP_KEY") || "capskip";
msg.url = "http://127.0.0.1:8080/res.php?key=" + key +
"&action=get&id=" + msg.captchaId + "&json=1";
msg.method = "GET";
return msg;Zwei Dinge zur Antwort. Ein status von 0 mit einem request von CAPCHA_NOT_READY ist kein Fehler, sondern die noch laufende Lösung, und das ist der Zweig, den Ihr switch-Node zurück zum delay schickt. Ein status von 1 bedeutet, dass msg.payload.request den Token enthält. Begrenzen Sie die Schleife auf eine sinnvolle Zahl von Durchläufen, damit eine wirklich unlösbare Challenge sich nicht ewig dreht. Ausführlicher behandelt wird der Polling-Zustand im Leitfaden zur CAPCHA_NOT_READY-Antwort, und alle Parameter sind aufgeführt in der CapSkip-API-Dokumentation.
Weg zwei: das SDK in einem function-Node
Ein function-Node läuft in einer Sandbox, die standardmäßig keinen Zugriff auf npm-Pakete hat. Zwei Einstellungen steuern das, und sie verhalten sich unterschiedlich.
Die ältere ist functionGlobalContext: Sie laden das Modul per require in settings.js und holen es im Node mit einem global.get-Aufruf wieder heraus. Das funktioniert, aber jeder function-Node in der Instanz sieht es, und ein neues Modul bedeutet einen Neustart von Node-RED.
Die bessere ist functionExternalModules. Setzen Sie sie in settings.js auf true, und der function-Node bekommt einen Reiter Setup, in dem Sie ein Modul und die Variable benennen, unter der es erscheinen soll. Node-RED installiert es beim Deploy in Ihr Benutzerverzeichnis, und nur dieser Node sieht es.
// settings.js, in your Node-RED user directory.
module.exports = {
// Lets a function node declare its own npm modules
// on the Setup tab, installed on deploy.
functionExternalModules: true,
// The older, instance-wide alternative.
// functionGlobalContext: { capskip: require("capskip") },
}Starten Sie Node-RED neu, öffnen Sie einen function-Node, gehen Sie zum Reiter Setup und fügen Sie das Modul capskip unter dem Variablennamen capskip hinzu. Ein Deploy genügt, dann wird es installiert. Ab da kann der Node-Körper es direkt verwenden.
Schritt 1: Lösen im function-Node
Eine Lösung ist ein Netzwerkaufruf, der Sekunden dauert, der Node muss also asynchron fertig werden. Das heißt: kein einfaches return der Nachricht. Die Regel von Node-RED ist hier eindeutig: Erledigen Sie die Arbeit in einem async-Block, schicken Sie die Nachricht mit einem node.send-Aufruf hinaus und geben Sie aus dem Node-Körper null zurück, damit nichts doppelt gesendet wird.
// npm install capskip - or add it on the Setup tab
const solver = new capskip.CapSkip({
host: "127.0.0.1",
port: 8080,
apiKey: env.get("CAPSKIP_KEY") || "capskip"
});
(async () => {
try {
const result = await solver.recaptcha(msg.sitekey, msg.pageUrl);
msg.token = result.code; // inject this into the form
node.send(msg);
} catch (err) {
node.error(err, msg); // routes to a catch node
}
node.done();
})();
return null;Erst msg als zweites Argument an node.error lässt einen catch-Node den Fehler aufgreifen. Lassen Sie es weg, landet der Fehler in der Debug-Seitenleiste und der Flow stoppt einfach, und das ist der mit Abstand häufigste Grund dafür, dass ein Captcha-Zweig in Node-RED aussieht, als hätte er gar nichts getan.
Eine Methode deckt reCAPTCHA v2, Invisible, Enterprise und v3 ab. Die Varianten sind Optionen und keine eigenen Aufrufe: Ein unsichtbares Widget ist dieselbe Zeile mit einem Options-Objekt, in dem invisible auf 1 steht, und v3 ist version auf v3 plus eine action. Turnstile und GeeTest haben eigene Methoden mit derselben Form, und alle sind aufgeführt auf Die Seite zu den SDKs fürs Captcha-Lösen.
Schritt 2: der gesamte Flow in einem Node
Die Seite abrufen, den sitekey aus dem HTML ziehen, lösen, dann den Token mit dem Rest des Formulars zurücksenden. Das ist die Fassung zum Einfügen, wenn der Flow aus einem inject-Node, diesem function-Node und einem debug-Node bestehen soll.
// Module on the Setup tab: capskip. fetch is built in
// from Node 18, which Node-RED 3 and 4 both require.
const PAGE = "https://example.com/page-with-recaptcha";
const solver = new capskip.CapSkip({ host: "127.0.0.1", port: 8080 });
(async () => {
try {
const html = await (await fetch(PAGE)).text();
const found = html.match(/data-sitekey=["']([^"']+)/);
if (!found) { throw new Error("No data-sitekey on the page."); }
// Solve, then submit straight away. Tokens go stale.
const result = await solver.recaptcha(found[1], PAGE);
const reply = await fetch(PAGE, {
method: "POST",
body: new URLSearchParams({
"g-recaptcha-response": result.code
})
});
msg.payload = { status: reply.status, token: result.code };
node.send(msg);
} catch (err) {
node.error(err, msg);
}
node.done();
})();
return null;Lösen Sie im Schritt direkt vor dem Absenden, nie in einem früheren Zweig, der danach auf etwas anderes wartet. Ein reCAPTCHA-Token ist etwa zwei Minuten gültig und wird genau einmal akzeptiert, ein Flow, der löst, dann in einem delay-Node sitzt und dann absendet, kassiert also eine Ablehnung, die kein bisschen nach einem Löserproblem aussieht. Dieses Fehlerbild sollten Sie nachlesen, im Leitfaden zur Gültigkeitsdauer von reCAPTCHA-Token.
Node-RED und den Löser auf verschiedenen Maschinen betreiben
Das wiegt in Node-RED schwerer als in den meisten Werkzeugen, weil ein großer Teil der Installationen auf einem Raspberry Pi, einem NAS oder einer kleinen Linux-Kiste läuft und CapSkip eine Windows-Anwendung ist. Trifft das auf Ihr Setup zu, dann ist 127.0.0.1 der Pi, der Löser läuft nicht darauf, und der Aufruf scheitert mit einem abgelehnten Verbindungsversuch, bevor er die API überhaupt erreicht.
Der Server-Modus ist die Antwort, und er ist eine Einstellungsänderung, kein anderes Produkt. Der Local-Modus bindet an 127.0.0.1 und antwortet nur diesem Gerät. Der Server-Modus bindet an Ihre Netzwerkadresse oder öffentliche IP, sodass ein Flow auf einem Pi, einem Container-Host oder einer gehosteten Node-RED-Instanz die Windows-Maschine über dieselbe API aufruft. Eine statische öffentliche IP hält diese Adresse stabil. So oder so bleibt es Ihre Hardware und bleibt ohne Zähler, ein vielbeschäftigter Flow kostet also nicht mehr als ein ruhiger.
// Same call, same SDK. Only the host moves.
const solver = new capskip.CapSkip({
host: "10.0.0.12",
port: 8080,
apiKey: env.get("CAPSKIP_KEY")
});Schalten Sie die Schlüsselvalidierung ein, sobald der Löser auf einer Netzwerkadresse lauscht, und geben Sie jeder Node-RED-Instanz einen eigenen Schlüssel, damit einer widerrufen werden kann, ohne die anderen anzufassen. Legen Sie den Schlüssel in eine Umgebungsvariable statt in den Node-Körper: Die Flows-Datei ist JSON auf der Platte und landet oft in einem git-Repository. Beide Modi werden durchgegangen in der CapSkip-Einrichtungsanleitung.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| capskip is not defined | Das Modul wurde nie im Reiter Setup deklariert | functionExternalModules auf true setzen, dann hinzufügen und deployen |
| Der function-Node gibt nichts aus | Die Nachricht wurde zurückgegeben statt aus dem async-Block gesendet | node.send aufrufen und aus dem Node-Körper null zurückgeben |
| Der Flow stoppt ohne sichtbaren Fehler | node.error wurde ohne das Nachrichtenargument aufgerufen | msg als zweites Argument übergeben und einen catch-Node verdrahten |
| connect ECONNREFUSED 127.0.0.1:8080 | Node-RED läuft nicht auf der Maschine mit dem Löser | Den Löser in den Server-Modus schalten und host auf seine Adresse setzen |
| Die Abfrageschleife endet nie | Der switch-Node hat keine Versuchsgrenze | Durchläufe in einer Kontextvariablen zählen und nach einem Limit aufgeben |
| Das Formular weist ein Token ab, das in Ordnung aussieht | Es wurde mehrere Nodes früher gelöst | Direkt vor dem Absenden lösen, nicht in einem früheren Zweig |
| ERROR_GOOGLEKEY | Der sitekey gehört nicht zu dieser Seiten-URL | data-sitekey erneut von der Seite lesen, an die Sie absenden |
FAQ
Brauche ich das SDK, oder reichen http request-Nodes?
Beides funktioniert. Der Weg über http request braucht keinen Zugriff auf settings.js und hält jeden Schritt auf der Arbeitsfläche sichtbar, was manche Teams zur Nachvollziehbarkeit bevorzugen. Der SDK-Weg übernimmt Polling, Backoff und Timeouts für Sie und liefert einen Token meist schneller, weil er schon nach einer Viertelsekunde zu prüfen beginnt statt erst nach fünfzehn Sekunden.
Kann eine gehostete Node-RED-Instanz einen Löser auf meinem Schreibtisch erreichen?
Nur im Server-Modus. Eine gehostete Instanz läuft auf fremder Infrastruktur, 127.0.0.1 ist dort also deren Container und nicht Ihre Maschine. Binden Sie den Löser an eine erreichbare Adresse, stellen Sie ihn hinter eine Firewall-Regel, die nur die ausgehenden Adressen der Plattform zulässt, und schalten Sie die Schlüsselvalidierung ein. Die Seite zu den Verbindungseinstellungen behandelt die gesamte Einrichtung.
Wie verhindere ich, dass ein Flow den Löser überschwemmt?
Setzen Sie einen delay-Node im Modus rate limit vor den lösenden Node. Er stellt Nachrichten in eine Warteschlange und gibt sie mit fester Rate frei, und genau diese Drossel wollen Sie, wenn drei Zeitpläne auf dieselbe Maschine zeigen. In einer Schleife ohne Obergrenze zu lösen ist der übliche Weg, auf dem ein Batch zu einem Haufen Timeouts wird.
Ist das dasselbe wie in n8n?
Die Löserseite ist identisch, die Flow-Seite nicht. n8n führt seinen Code-Node in einer abgeriegelten Sandbox ohne npm-Installationen aus, dort heißt es also HTTP-Nodes oder gar nichts. Node-RED installiert bereitwillig ein Paket für einen einzigen function-Node, und deshalb gibt es hier den SDK-Weg. Die n8n-Fassung ist beschrieben in der Anleitung zum Captcha-Workflow in n8n.
Die Kurzfassung
Schalten Sie functionExternalModules ein, fügen Sie capskip im Reiter Setup hinzu und erledigen Sie die Lösung in einem async-Block, der mit einem node.send-Aufruf endet und null zurückgibt. Verdrahten Sie einen catch-Node und übergeben Sie die Nachricht an node.error, damit Fehler sichtbar sind. Wenn Node-RED auf einem Pi läuft und der Löser auf Windows, dann ist das der Server-Modus und ein geänderter host-String. Der Rest der Node.js-Oberfläche steht auf die Node.js-Captcha-Solver-Seite, und die reCAPTCHA-Optionen stehen auf die reCAPTCHA-v2-Solver-Seite.
Noch eine Sache sollten Sie wissen, bevor Sie diesen Flow auf einen Fünf-Minuten-Zeitplan setzen. CapSkip ist ein lokaler Captcha-Löser und läuft auf Hardware, die Ihnen bereits gehört: Ein Flow, der für immer alle fünf Minuten feuert, kostet exakt so viel wie einer, den Sie von Hand auslösen.
