Captcha in Postman lösen und auf das Token pollen

Sie können Captchas in Postman lösen, ohne eine einzige Zeile Anwendungscode zu schreiben. Die API ist 2captcha-kompatibel und läuft auf Ihrer eigenen Maschine, es sind also zwei Anfragen: Schicken Sie die Aufgabe per POST an /in.php und merken Sie sich die ID, dann fragen Sie /res.php ab, bis die Antwort eintrifft. Ein kurzes Post-response-Skript macht aus dieser zweiten Anfrage eine Schleife, und eine Collection-Variable trägt die ID von der einen zur anderen. Dieser Leitfaden enthält die genauen Felder, beide Skripte und die eine Einstellung, die verhindert, dass die Schleife endlos läuft.
Was Sie brauchen
- Die Postman-Desktop-App. Sie spricht direkt mit 127.0.0.1, ganz ohne Agent. Die Web-App wird weiter unten behandelt und braucht ein zusätzliches Teil
- CapSkip läuft und ist erreichbar. Local mode lauscht auf 127.0.0.1 Port 8080 für einen Client auf derselben Maschine, und Server mode lauscht auf Ihrem Netzwerk oder Ihrer öffentlichen IP, sodass ein Laptop, ein Teammitglied oder ein gehosteter Runner ihn erreichen kann. Beides finden Sie in den Verbindungseinstellungen
- Ein Sitekey und eine Seiten-URL, gegen die gelöst wird
Sonst nichts. Kein SDK, keine Abhängigkeit, kein Konto. Die Schlüsselvalidierung ist standardmäßig aus, jeder nicht leere String funktioniert also als Schlüsselwert, und wenn Sie gar nichts senden, kommt ERROR_WRONG_USER_KEY zurück statt einer Lösung.
Richten Sie zuerst drei Collection-Variablen ein
Legen Sie diese auf der Collection an, nicht in einer Umgebung. Eine Collection-Variable reist mit dem Export mit, das Ganze funktioniert also weiterhin, wenn jemand anderes es importiert.
| Variable | Anfangswert | Warum |
|---|---|---|
| baseUrl | http://127.0.0.1:8080 | Eine Stelle zum Ändern, wenn der Löser auf einen Server umzieht |
| apiKey | capskip | Ein beliebiger nicht leerer String. Die Validierung ist standardmäßig aus |
| captchaId | leer | Das Submit-Skript schreibt sie, die Poll-Anfrage liest sie |
Anfrage 1: Die Aufgabe absenden
Ein POST an {{baseUrl}}/in.php mit einem Formular-Body. Wählen Sie x-www-form-urlencoded, nicht rohes JSON: Die API liest Formularfelder.
| Schlüssel | Wert |
|---|---|
| key | {{apiKey}} |
| method | userrecaptcha |
| googlekey | YOUR_SITEKEY |
| pageurl | https://example.com/page-with-recaptcha |
| json | 1 |
Das Feld json=1 ist in Postman wichtiger als am Terminal. Ohne dieses Feld ist die Antwort der schlichte String OK, gefolgt von einem Pipe-Zeichen und der ID, den Sie dann von Hand aufteilen müssen. Mit dem Feld bekommen Sie ein Objekt, und der Pretty Printer funktioniert.
Fügen Sie das im Tab Scripts unter Post-response ein. Ältere Postman-Versionen nennen denselben Tab Tests.
// Post-response script on the submit request.
const body = pm.response.json();
pm.test('task accepted', function () {
pm.expect(body.status).to.eql(1);
});
// Hand the ID to the polling request.
pm.collectionVariables.set('captchaId', body.request);Die Form der Antwort ist für jeden Captcha-Typ gleich. Status ist 1, wenn die Aufgabe angenommen wurde, und der interessante Wert steht immer im Feld request.
{"status": 1, "request": "2122988149"}Achten Sie auf den Parameternamen. reCAPTCHA nimmt googlekey und Turnstile nimmt sitekey, und der falsche von beiden ist die übliche Ursache für eine ERROR_GOOGLEKEY-Antwort. Auf der Seite Turnstile-Löser finden Sie die vollständige Feldliste für Challenge-Seiten, die zusätzlich die Werte cData und chlPageData brauchen, die von der Seite ausgelesen werden.
Derselbe Request, für die anderen acht Typen
Nur der Submit-Request ändert sich. Fünf method-Werte decken jeden Typ ab, den CapSkip unterstützt, und die Varianten sind zusätzliche Felder im selben Formular-Body statt eigener Endpunkte. Duplizieren Sie Request 1, wenden Sie die letzte Spalte an und lassen Sie den Rest des Requests unverändert.
| Typ | method setzen auf | Dann den Body ändern |
|---|---|---|
| Bild, hochgeladene Datei | method = post | Body auf form-data umstellen und das Bild als file anhängen |
| Bild, base64 | method = base64 | googlekey und pageurl weglassen, body mit dem kodierten Bild senden |
| reCAPTCHA v2 checkbox | method = userrecaptcha | Nichts. Das ist der oben gezeigte Request |
| reCAPTCHA v2 Invisible | method = userrecaptcha | invisible mit dem Wert 1 hinzufügen |
| reCAPTCHA Enterprise | method = userrecaptcha | enterprise mit dem Wert 1 hinzufügen |
| reCAPTCHA v3 | method = userrecaptcha | version auf v3 gesetzt hinzufügen, dazu eine action |
| Turnstile-Widget | method = turnstile | googlekey in sitekey umbenennen |
| Turnstile-Challenge-Seite | method = turnstile | googlekey in sitekey umbenennen, dann data und pagedata hinzufügen |
| GeeTest v3 | method = geetest | gt und challenge anstelle von googlekey senden |
Zwei davon brauchen in Postman besondere Aufmerksamkeit. Ein hochgeladenes Bild ist der einzige Fall, der x-www-form-urlencoded nicht nutzen kann, denn eine Datei braucht einen Multipart-Body, stellen Sie also genau diesen einen Request auf form-data um. Und GeeTest läuft gegen eine Frist: Sein Challenge-Wert verfällt nach etwa einer Minute, holen Sie ihn also unmittelbar vor dem Senden, statt einen früher gespeicherten wiederzuverwenden.
Der Polling-Request ändert sich nie. Genau das ist das Argument dafür, das Ganze überhaupt als Collection zu bauen: Ein einziger Request 2 bedient jeden Typ der Liste, weil die Antwort immer in derselben Form zurückkommt.
Anfrage 2: Pollen, bis die Antwort eintrifft
Ein POST an {{baseUrl}}/res.php, im selben Formular-Body-Stil.
| Schlüssel | Wert |
|---|---|
| key | {{apiKey}} |
| action | get |
| id | {{captchaId}} |
| json | 1 |
Während der Job läuft, kommt hier CAPCHA_NOT_READY zurück, genau so geschrieben, mit dem fehlenden Buchstaben. Das ist ein Status und kein Fehler, und die einzig richtige Reaktion darauf ist, erneut zu fragen.
// Post-response script on the polling request.
const body = pm.response.json();
const tries = Number(pm.collectionVariables.get('tries') || 0);
if (body.request === 'CAPCHA_NOT_READY' && tries < 20) {
// Run this same request again.
pm.collectionVariables.set('tries', tries + 1);
pm.execution.setNextRequest(pm.info.requestId);
} else {
pm.collectionVariables.set('captchaToken', body.request);
pm.collectionVariables.set('tries', 0);
pm.execution.setNextRequest(null);
}Drei Dinge in diesem Skript sind eine Erwähnung wert, denn mit jedem davon lässt sich ein Nachmittag verlieren.
Die Schleife läuft nur im Collection Runner. Die eigene Dokumentation von Postman sagt ausdrücklich, dass setNextRequest keine Wirkung hat, wenn Sie eine einzelne Anfrage senden. Ein Klick auf Send bei der Poll-Anfrage schickt sie also nur einmal, und das Skript wirkt tot. Führen Sie die Collection, die Postman CLI oder Newman aus.
Der Zähler ist nicht optional. Ohne Obergrenze läuft eine Aufgabe, die nie fertig wird, so lange in der Schleife, bis Sie den Lauf von Hand stoppen. Zwanzig Versuche im Abstand von fünf Sekunden ergeben über hundert Sekunden Wartezeit und decken damit bequem die 15 bis 20 Sekunden ab, die ein reCAPTCHA-v2-Job braucht.
Referenzieren Sie die Anfrage über die ID. Die Übergabe von pm.info.requestId richtet die Schleife auf die gerade laufende Anfrage, ein späteres Umbenennen bricht die Kette also nicht stillschweigend.
Geben Sie dem Runner eine Verzögerung, sonst hämmern Sie auf den Endpunkt ein
Das Skript oben läuft so schnell in der Schleife, wie der Runner kann, und einen Job zu pollen, der seit zwei Sekunden läuft, ist verschwendete Arbeit. Der Collection Runner hat in seiner Lauf-Konfiguration ein Feld Delay, in Millisekunden, das vor jeder Anfrage angewendet wird. Setzen Sie es auf 5000.
Wie lange Sie vor dem ersten Poll warten sollten, hängt davon ab, was Sie abgeschickt haben:
| Typ | Nicht fertig vor |
|---|---|
| Bild | 1 Sekunde |
| reCAPTCHA v2 | 15 bis 20 Sekunden |
| reCAPTCHA v3 | 10 bis 15 Sekunden |
| GeeTest v3 | etwa 5 Sekunden |
In Newman ist dieselbe Einstellung ein Kommandozeilen-Flag, eine Collection, die in der App funktioniert, funktioniert also unverändert in CI.
# npm install -g newman newman run captcha.postman_collection.json --delay-request 5000
Stattdessen die Klartext-Antwort lesen
Lassen Sie json=1 weg, und der Body kommt als Text zurück, was gelegentlich genau das ist, was Sie wollen. Zwei Helfer decken das ab.
// Plain text mode: OK|2122988149
const id = pm.response.text().split('|')[1];
// Turnstile also returns the user agent, as a header.
const ua = pm.response.headers.get('X-Turnstile-User-Agent');Dieser Header ist keine Spielerei. Cloudflare bindet ein Turnstile-Token an den Browser-Fingerabdruck, der es erzeugt hat, das Token muss also mit demselben User Agent übermittelt werden, sonst weist die Website es zurück, obwohl das Token selbst völlig gültig ist.
Noch eine Regel, die besonders Postman-Nutzer erwischt, weil die App es so leicht macht, zweimal auf Send zu klicken: Ein Ergebnis lässt sich nur einmal lesen. Das zweite Lesen derselben ID kommt leer zurück, was genau wie eine fehlgeschlagene Lösung aussieht. Speichern Sie das Token beim ersten Lesen in einer Variablen.
Die Agent-Frage, und wie Sie Postman auf einen Server richten
Wenn Sie die Postman-Web-App statt der Desktop-App nutzen, laufen Anfragen über einen Agent, und die Wahl des Agents entscheidet, ob 127.0.0.1 überhaupt erreichbar ist. Der Cloud Agent läuft in der Infrastruktur von Postman und kann nichts in einem privaten Netzwerk erreichen. Der Desktop Agent läuft auf Ihrer Maschine und kann es.
| Wie Sie Postman betreiben | Erreicht es einen lokalen Löser? |
|---|---|
| Desktop-App | Ja, kein Agent nötig |
| Web-App mit dem Desktop Agent | Ja, der Agent leitet über Ihre Maschine |
| Web-App mit dem Cloud Agent | Nein, er sieht kein privates Netzwerk |
Server mode ändert diese Rechnung. Betreiben Sie CapSkip auf einem Rechner, der auf Ihrem Netzwerk oder einer öffentlichen IP lauscht, ändern Sie die Variable baseUrl so, dass sie darauf zeigt, und jede Anfrage in der Collection folgt. Sonst ändert sich nichts, denn das Einzige, was sich bewegt hat, ist die Adresse.
# The collection variable is the only edit. baseUrl = http://YOUR_SERVER_IP:8080
Eine statische öffentliche IP wird empfohlen, und die Schritte dazu finden Sie in den Verbindungseinstellungen. Server mode ist weiterhin Ihre eigene Hardware und weiterhin ohne Verbrauchsabrechnung: Es ändert sich, wo der Löser lauscht, nicht, wem er gehört.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
ERROR_WRONG_USER_KEY | Das Feld key kam leer an, weil {{apiKey}} nicht aufgelöst wurde | Definieren Sie apiKey auf der Collection, nicht in einer Umgebung, an deren Auswahl Sie denken müssen |
ERROR_WRONG_METHOD | Ein Tippfehler in method oder im Wert von action | Beides sind Formularfelder, keine HTTP-Verben: Submit nimmt ein method-Feld, Polling nimmt ein action-Feld mit dem Wert get |
ERROR_GOOGLEKEY | Ein Turnstile-Sitekey wurde an userrecaptcha geschickt | Passen Sie die Methode zum Feldnamen |
ERROR_PAGEURL | Der Seiten-URL fehlt das Schema, oder sie wurde abgeschnitten | Nehmen Sie https mit auf und nutzen Sie einen Formular-Body statt eines Query-Strings |
| Leerer Antwort-Body | Diese ID wurde bereits einmal gelesen | Speichern Sie das Token beim ersten Lesen; senden Sie für ein neues erneut ab |
| Anfrage konnte nicht gesendet werden, Verbindung abgelehnt | An dieser Adresse lauscht nichts | Starten Sie den Löser, oder prüfen Sie, ob der Desktop Agent ausgewählt ist |
| Das Skript läuft, aber nichts wiederholt sich | Sie haben auf Send geklickt, statt die Collection auszuführen | setNextRequest funktioniert nur in einem Collection-Lauf |
Der genaue Wortlaut jedes Codes, den die API zurückgeben kann, steht in der API-Dokumentation.
Häufig gestellte Fragen
Kann ich das Ganze in einer einzigen Anfrage erledigen?
Technisch ja, mit pm.sendRequest in einem Pre-request-Skript, und es lohnt sich selten. Die Sandbox ist für kurze Skripte gebaut, ein blockierendes Poll lässt die Anfrage also ohne jede Rückmeldung hängend wirken, während sie wartet. Zwei Anfragen plus der Runner zeigen Ihnen jeden Versuch, und genau dafür ist man in Postman statt im Code.
Läuft das in CI über Newman?
Ja. setNextRequest funktioniert in Newman und in der Postman CLI genauso wie in der App, eine exportierte Collection läuft also unverändert. Zu klären ist nur die Erreichbarkeit: Ein gehosteter Runner hat seine eigene Loopback-Adresse, der Löser muss also im Server mode an einer Adresse laufen, zu der der Runner eine Route hat.
Gibt es ein Limit dafür, wie viele ich in die Warteschlange stellen kann?
Nein. Reichen Sie so viele Aufgaben ein, wie Sie wollen, und pollen Sie jede ID unabhängig. Die Arbeit passiert auf Ihrer eigenen Hardware statt in der geteilten Warteschlange von jemand anderem, die Obergrenze ist also, wie schnell Ihre Maschine sie abarbeitet, und keine Quote und kein Guthaben.
Wann Sie aus Postman herauswachsen sollten
Postman ist das richtige Werkzeug, um zu belegen, dass die API funktioniert, um einen neuen Captcha-Typ zu erkunden und einem Kollegen etwas in die Hand zu geben, das er importieren und ausführen kann. Für die Produktion ist es das falsche Werkzeug, vor allem wegen des Pollings: Der Runner wartet jedes einzelne Mal seine feste Verzögerung ab, während offizielle SDKs mit dem Pollen bei 250 Millisekunden beginnen und dann zurückfahren, sodass eine schnelle Lösung schnell zurückkommt. Sie verwandeln außerdem die Fehler-Strings in typisierte Exceptions.
Für dieselben zwei Aufrufe in einem Shell-Skript siehe die cURL-Anleitung. So oder so gehen die Aufrufe an Ihre eigene Maschine, und genau darum lohnt sich ein unbegrenzter Captcha-Löser als Ziel für eine Collection: Sie können die Collection zwanzig Mal laufen lassen, bis die Felder stimmen, und es kostet nichts außer Ihrer eigenen CPU.
