Fehlerbehandlung für Captcha-Solver: Wiederholungen, Backoff und was man nie wiederholt
Ein Captcha-Lösungsaufruf ist ein Netzwerkaufruf an ein System, das Sie nicht kontrollieren, gegen eine Challenge, die sich ohne Vorwarnung ändern kann. Fehler gut zu behandeln entscheidet darüber, ob eine Pipeline sanft degradiert oder in einem Wiederholungs-Sturm Zeit verschwendet.
Bei Scarnox hilft Ihnen die Wirtschaftlichkeit: Fehlgeschlagene Lösungen sind kostenlos, eine Wiederholung kostet also nur Zeit und sonst nichts. Aber einen falschen Fehler zu wiederholen verschwendet trotzdem diese Zeit, und zu hartnäckiges Wiederholen kann eigene Probleme verursachen. Dieser Leitfaden gibt Ihnen eine Wiederholungsstrategie, die Sie in einen Python-Job einfügen können.
Scarnox löst heute PopularCaptcha. Dieselbe Fehlerbehandlung gilt für jeden Captcha-Solver, die Strategie unten hängt also nicht vom Captcha-Typ ab.
Immer zuerst errorId prüfen
Eine Lösungsantwort hat ein Feld errorId. 0 bedeutet Erfolg, und das Token steht unter solution.token. Jeder andere Wert bedeutet, dass die Lösung fehlgeschlagen ist. Eine fehlgeschlagene Antwort enthält außerdem errorCode und errorDescription. Protokollieren Sie beides: Den Code suchen Sie später, und die Beschreibung lesen Sie, wenn ein Lauf schiefgeht.
{"errorId": 1, "errorCode": "...", "errorDescription": "..."}
Gehen Sie nie davon aus, dass eine Antwort erfolgreich ist, weil der HTTP-Status 200 lautet. Prüfen Sie errorId jedes Mal.
Fehler, die Sie wiederholen sollten
Manche Fehler sind vorübergehend. Wiederholen Sie diese mit exponentiellem Backoff und einer kleinen Zufallsverzögerung:
- Netzwerkfehler: Verbindungsabbrüche, DNS-Probleme, Lesetimeouts auf Ihrer Seite.
- Serverfehler: HTTP-5xx-Antworten, die ein Problem auf Seiten des Dienstes bedeuten.
- Ratenbegrenzung: HTTP 429, was bedeutet, dass Sie zu schnell Anfragen senden. Werden Sie langsamer.
- Timeouts: Die Lösung dauerte länger, als Ihr Client gewartet hat. Wiederholen Sie mit einem längeren Timeout, falls Sie noch keines gesetzt haben.
Diese Fehler lohnen eine Wiederholung, weil der nächste Versuch ohne Änderung auf Ihrer Seite gelingen kann.
Fehler, die Sie vor einer Wiederholung beheben müssen
Andere Fehler verschwinden nicht durch Warten. Eine Wiederholung mit derselben Eingabe scheitert genauso. Beheben Sie diese zuerst:
- Ungültiger Aufgabentyp. Prüfen Sie, dass
captcha_typegenau so geschrieben ist wie in der Dokumentation. - Falscher oder fehlender Sitekey. Lesen Sie den Sitekey erneut von der Seite. Ein Sitekey aus einem alten Deployment ist eine häufige Ursache.
- Falsche Seiten-URL.
site_urlsollte die Seite sein, die das Widget anzeigt. - Proxy-Probleme. Ein toter, falsch geschriebener oder blockierter Proxy lässt jeden Versuch scheitern. Testen Sie den Proxy zuerst allein. Siehe Proxy oder proxyless.
- Kontoprobleme. Ein ungültiger Schlüssel oder ein Konto ohne ausreichendes Guthaben lässt jeden Versuch scheitern, bis jemand das Konto korrigiert. Alarmieren Sie einen Menschen und wiederholen Sie nicht in einer Schleife.
Wenn ein Fehler auf keiner der beiden Listen steht, behandeln Sie ihn wie einen Fehler, der zuerst behoben werden muss. Lesen Sie den Fehlertext, entscheiden Sie und nehmen Sie den Code in die richtige Liste in Ihrem Code auf. Halten Sie die Listen kurz und regelmäßig überprüft.
Eine Richtlinie zum Einfügen
import os
import random
import time
import requests
API_URL = "https://api.scarnox.com/api/tasks/create"
HEADERS = {"Authorization": f"Bearer {os.environ['SCARNOX_KEY']}"}
MAX_ATTEMPTS = 4
class SolveError(Exception):
def __init__(self, code: str, description: str, retryable: bool):
super().__init__(f"{code}: {description}")
self.code = code
self.retryable = retryable
def solve_once(payload: dict) -> str:
try:
r = requests.post(API_URL, headers=HEADERS, json=payload, timeout=185)
except requests.RequestException as exc:
# Netzwerkfehler oder Timeout auf unserer Seite: Wiederholen ist sicher.
raise SolveError("NETWORK", str(exc), retryable=True)
if r.status_code == 429 or r.status_code >= 500:
raise SolveError(f"HTTP_{r.status_code}", r.text[:200], retryable=True)
try:
data = r.json()
except ValueError:
raise SolveError(f"HTTP_{r.status_code}", r.text[:200], retryable=False)
if data.get("errorId") == 0:
return data["solution"]["token"]
# Die API hat einen Fehler gemeldet. Nur Codes wiederholen, die Sie als vorübergehend erkannt haben.
code = data.get("errorCode", "UNKNOWN")
retryable = code in TRANSIENT_CODES
raise SolveError(code, data.get("errorDescription", ""), retryable=retryable)
# Fügen Sie API-Fehlercodes hier erst hinzu, nachdem Sie in Ihren Logs gesehen haben, dass sie vorübergehend sind.
TRANSIENT_CODES: set[str] = set()
def solve(payload: dict) -> str:
for attempt in range(MAX_ATTEMPTS):
try:
return solve_once(payload)
except SolveError as err:
last_attempt = attempt == MAX_ATTEMPTS - 1
if not err.retryable or last_attempt:
raise
delay = min(30, 2 ** attempt) + random.random() # Backoff + Jitter
print(f"attempt {attempt + 1} failed ({err.code}), retrying in {delay:.1f}s")
time.sleep(delay)
raise RuntimeError("unreachable")
Die Richtlinie wiederholt nie einen Fehler, der zuerst behoben werden muss, und sie stoppt nach einer festen Zahl von Versuchen. Die Menge TRANSIENT_CODES startet absichtlich leer: Nehmen Sie einen Code erst auf, nachdem Ihre Logs zeigen, dass er vorübergehend ist.
Warum Jitter wichtig ist
Wenn fünfzig Worker gleichzeitig fehlschlagen und alle exakt zwei Sekunden warten, wiederholen sie alle gemeinsam und scheitern gemeinsam erneut. Ein zufälliger Bruchteil einer Sekunde verteilt sie. Das ist derselbe Rat, den die meisten großen API-Anbieter für ihre eigenen Clients geben.
Timeouts
Setzen Sie Ihr HTTP-Timeout deutlich über der typischen Lösungszeit, etwa 185 Sekunden. Die Anfrage blockiert, bis das Token bereit ist. Ein kurzes Client-Timeout kann einen langsamen Erfolg in einen Fehler verwandeln, den Sie dann sinnlos wiederholen. Ein Client-Timeout, das länger ist als die Lösung, ist die sichere Einstellung.
Tokens sind nicht cachebar
Ein Token läuft innerhalb von etwa zwei Minuten ab. Speichern Sie Tokens nicht zwischen Läufen und verwenden Sie keines für eine zweite Absendung. Wenn ein Absenden fehlschlägt, nachdem das Token verbraucht wurde, lösen Sie neu, statt das alte Token erneut abzuschicken. Der Leitfaden Ablauf von Captcha-Tokens behandelt das Timing ausführlicher.
Parallelität und Backoff über mehrere Worker
Teilen sich viele Worker einen Schlüssel, verlangsamt der Backoff eines Workers die anderen nicht. Fügen Sie ein gemeinsames Limit für gleichzeitige Lösungen hinzu, etwa einen Semaphor oder eine Warteschlange, damit ein Schwall von Wiederholungen nicht zu einem Schwall von Anfragen wird. Eine einfache Begrenzung der Parallelität verhindert die meisten Ratenbegrenzungsfehler, bevor sie entstehen.
Protokollieren, was hilft
Protokollieren Sie für jeden fehlgeschlagenen Versuch den errorCode, die Versuchsnummer, den Sitekey, den Host der Seite und ob der Fehler wiederholt wurde. Protokollieren Sie nicht den API-Schlüssel oder ein Proxy-Passwort. Ein Dashboard, das Codes pro Stunde zählt, zeigt Ihnen schnell, ob ein Problem in Ihrem Code, im Proxy oder beim Dienst liegt.
Nächste Schritte
- PopularCaptcha in Python lösen
- Proxy oder proxyless: welcher Aufgabentyp wird gebraucht?
- CI grün halten, wenn Ihre App ein Captcha hat
- Kostenlose Testphase für den Captcha-Solver
Testen Sie Ihre Fehlerbehandlung mit einer kostenlosen Testphase: Erstellen Sie ein Konto und starten Sie mit einer kostenlosen Testphase, ganz ohne Karte.