So beheben Sie ERROR_UPLOAD beim Senden eines Bild-Captchas

error_upload - How to Fix ERROR_UPLOAD When Sending an Image CAPTCHA

ERROR_UPLOAD bedeutet, dass keine verwertbaren Bilddaten beim Löser angekommen sind. Die Anfrage selbst war in Ordnung und der API-Key war in Ordnung, aber der Teil, der das Bild transportieren sollte, kam leer an oder überhaupt nicht. Es ist einer von vier Codes, die alles abdecken, was auf dem Weg dorthin schiefgehen kann, und sie lassen sich leicht verwechseln, weil alle vier von Ihrer Seite aus identisch aussehen. Dieser Beitrag trennt sie voneinander, nennt die tatsächlichen Grenzwerte und zeigt Upload-Code, der keinen von ihnen auslöst.

Was Sie brauchen

  • CapSkip muss laufen, entweder im Local-Modus auf der Loopback-Adresse oder im Server-Modus auf einer Maschine, die Ihr Code erreichen kann. Beide werden unter Verbindungseinstellungen beschrieben, und die Wahl ändert nichts an den folgenden Fehlern.
  • Python 3.10 oder neuer mit installiertem SDK, oder curl, wenn Sie den Endpoint lieber direkt ansprechen möchten.
  • Ein echtes Captcha-Bild, auf der Festplatte gespeichert. Ein Screenshot einer ganzen Seite ist der falsche Testfall und schickt Sie auf die Jagd nach dem Größenlimit statt nach dem eigentlichen Fehler.

Was die einzelnen Bild-Fehlercodes bedeuten

Diese Codes gibt die API für Bild-Einreichungen zurück, direkt aus der CapSkip-API-Dokumentation. Lesen Sie zuerst die Spalte mit der Bedeutung, denn der Unterschied liegt darin, wo der Fehler passiert ist, nicht darin, wie schwerwiegend er war.

CodeWas es bedeutetWo das Problem liegt
ERROR_UPLOADKeine Bilddaten übermittelt, oder der Upload ist fehlgeschlagenDer Request-Body. Es kam nichts Verwertbares an.
ERROR_INVALID_IMAGEUngültiges Bildformat oder beschädigte BilddatenDie Bytes sind angekommen, lassen sich aber nicht als Bild dekodieren
ERROR_INVALID_BASE64Ungültige base64-KodierungDie Zeichenkette ließ sich nicht in Bytes zurückverwandeln
ERROR_TOO_BIG_CAPTCHA_FILESIZEÜber 600 kB, oder über 1000px in einer DimensionDas Bild ist echt und gültig, nur zu groß
ERROR_BAD_PARAMETERSFehlende oder ungültige PflichtparameterDie Formularfelder rund um das Bild

Es gibt einen fünften Fehlerfall, der die API überhaupt nie erreicht. Wenn Sie dem SDK einen Pfad übergeben, der nicht existiert, wirft es eine ValidationException mit dem Namen der Datei, ohne dass eine Anfrage gestellt wird. Diesen Unterschied sollten Sie sich früh einprägen: Eine Python-Exception bedeutet, dass Ihre eigene Maschine den Vorgang gestoppt hat, und ein Code, der mit ERROR beginnt, bedeutet, dass der Löser ihn gestoppt hat.

Schritt 1: Das Bild in einer Form senden, die die API akzeptiert

Das SDK nimmt drei Eingabeformen entgegen und macht aus allen die gleiche Übermittlung. Einen lokalen Pfad, eine Remote-URL oder eine Data-URI, die das Bild inline enthält.

# pip install capskip
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

# All three of these are accepted by the same method.
result = solver.normal("captcha.png")
result = solver.normal("https://example.com/captcha.jpg")
result = solver.normal("data:image/png;base64,iVBORw0KGgo...")

print(result["code"])   # the recognized text

Verwenden Sie einen absoluten Pfad statt eines relativen. Ein relativer Pfad wird gegen das Arbeitsverzeichnis des Prozesses aufgelöst, was selten das Verzeichnis ist, in dem Sie den Code geschrieben haben, und das ist der häufigste Grund, warum ein Skript, das in Ihrem Editor funktioniert, unter einem Scheduler oder einem Dienst fehlschlägt.

Bei der rohen HTTP-API gibt es zwei Wege, das Bild zu übertragen, und die Wahl des falschen Felds ist der häufigste Auslöser für ERROR_UPLOAD. Multipart sendet die Datei selbst, base64 sendet sie als Zeichenkette.

# no install needed, curl only
# method=post carries the file as multipart/form-data
curl -X POST "http://127.0.0.1:8080/in.php" \
  -F "key=capskip" \
  -F "method=post" \
  -F "[email protected]"

# method=base64 carries the same image as a string
curl -X POST "http://127.0.0.1:8080/in.php" \
  -d "key=capskip" \
  -d "method=base64" \
  --data-urlencode "body=BASE64_STRING_HERE"

Achten Sie auf die Feldnamen. Bei der Multipart-Methode landet das Bild in einem Feld namens file, bei der base64-Methode in einem Feld namens body. Eine Datei im Feld body zu senden, oder das @ wegzulassen, das curl anweist, die Datei zu lesen statt ihren Namen als reinen Text zu senden, führt in beiden Fällen zu einem Upload ohne Bild darin.

Schritt 2: ERROR_INVALID_BASE64 beheben

Base64-Fehler sind fast immer Zeilenumbrüche. GNU coreutils bricht seine Ausgabe standardmäßig bei 76 Zeichen um, und diese Zeilenumbrüche überleben bis in die Anfrage, also geben Sie beim Kodieren auf der Kommandozeile eine Umbruchbreite von null an. Die Encoder sind sich darin nicht einig und manche brechen gar nicht um, was dafür spricht, im Code zu kodieren statt an der Eingabeaufforderung.

Ein weiterer Unterschied überrascht alle, die zwischen SDK und roher API wechseln. Das SDK akzeptiert eine vollständige Data-URI, das Präfix eingeschlossen. Der body-Parameter am rohen Endpoint erwartet dagegen nur den base64-Payload für sich, ohne vorangestelltes Präfix.

# pip install capskip
import base64

with open("captcha.png", "rb") as fh:
    raw = base64.b64encode(fh.read()).decode()

# One continuous string. No newlines, no spaces, no truncation.
assert "\n" not in raw and len(raw) > 50

# The SDK wants the prefix, the raw body parameter does not.
result = solver.normal("data:image/png;base64," + raw)

Diese Längenprüfung ist wichtiger, als es aussieht. Eine base64-Zeichenkette, die kürzer als etwa fünfzig Zeichen ist, wird als Dateipfad statt als Bilddaten interpretiert, sodass ein abgeschnittener Payload überhaupt nicht als fehlerhaftes base64 scheitert. Er scheitert als fehlende Datei, was Sie an der falschen Stelle suchen lässt.

Schritt 3: ERROR_TOO_BIG_CAPTCHA_FILESIZE beheben

Hierfür gibt es veröffentlichte Zahlen statt einer vagen Richtlinie. Die API lehnt ein Bild über 600 kB ab, und sie lehnt eines über 1000px in einer Dimension ab. Beide Grenzwerte sind für ein echtes Captcha großzügig bemessen, wenn Sie also einen davon erreichen, bedeutet das meist, dass ein Screenshot der ganzen Seite statt des Widgets rausgegangen ist.

Das Zuschneiden auf das Captcha ist die bessere Lösung, weil es gleichzeitig die Genauigkeit verbessert. Das Verkleinern ist die Rückfalloption, wenn zuverlässiges Zuschneiden nicht möglich ist.

# pip install capskip pillow
from PIL import Image

img = Image.open("captcha.png")

# Bring the longest side under the documented 1000px ceiling.
if max(img.size) > 1000:
    img.thumbnail((1000, 1000))

# optimize=True usually clears the 600 kB limit on its own.
img.save("captcha-ready.png", optimize=True)

Schritt 4: ERROR_INVALID_IMAGE beheben

Dieser Code bedeutet, dass die Bytes intakt angekommen sind, aber kein Bild ergeben. In der Praxis gibt es dafür eine dominante Ursache. Sie haben das Captcha mit einem HTTP-Client heruntergeladen, der Server hat mit einer Fehlerseite oder einer Weiterleitung geantwortet, und Ihr Code hat den Response-Body in einer Datei mit der Endung .png gespeichert, ohne jemals hineinzuschauen. Die Datei ist real, die Größe ist plausibel, und die ersten vier Bytes sind HTML.

# no install needed, standard library only
SIGNATURES = (b"\x89PNG", b"\xff\xd8\xff", b"GIF8", b"RIFF")

with open("captcha.png", "rb") as fh:
    head = fh.read(4)

# An error page saved as .png starts with <!DO or <htm instead.
print(any(head.startswith(sig) for sig in SIGNATURES))

Führen Sie diese Prüfung beim Download aus, nicht erst beim Lösen. Das Lesen von vier Bytes kostet nichts, und es macht aus einem verwirrenden Löser-Fehler einen offensichtlichen Netzwerkfehler, genau dort, wo der Netzwerkfehler tatsächlich aufgetreten ist.

Vollständiges lauffähiges Beispiel

Fügt man die vier Korrekturen zusammen, entsteht eine Funktion, die vor dem Senden validiert und die Fehler, die Ihre eigene Maschine ausgelöst hat, von den Fehlern trennt, die der Löser zurückgegeben hat.

# pip install capskip pillow
from PIL import Image
from capskip import CapSkip, ApiException, ValidationException

solver = CapSkip(host="127.0.0.1", port=8080, defaultTimeout=120)

def solve_image(path):
    """Return the CAPTCHA text, or None with a reason printed."""
    img = Image.open(path)          # raises if it is not an image
    if max(img.size) > 1000:
        img.thumbnail((1000, 1000))
        img.save(path, optimize=True)

    try:
        return solver.normal(path)["code"]
    except ValidationException as exc:
        print("never left this machine:", exc)
    except ApiException as exc:
        print("the solver rejected it:", exc)
    return None

Das Bild zunächst mit Pillow zu öffnen, führt zwei der vier Codes zu einer einzigen lokalen Exception zusammen, denn eine Datei, die sich hier nicht öffnen lässt, hätte sich dort ohnehin nie dekodieren lassen. Was danach bei der API ankommt, ist ein nachweislich gutes Bild in einer nachweislich passenden Größe.

Eine Sache, die Sie nicht hinzufügen sollten: einen Proxy. Proxys werden für reCAPTCHA, Turnstile, GeeTest und CaptchaFox unterstützt, nicht aber für Bild-Captchas. Einen zu übergeben ist ein Parameterfehler und kein stiller No-Op, der Aufruf schlägt also direkt fehl.

Den Löser stattdessen auf einem Server betreiben

Bild-Uploads sind der einzige Captcha-Typ, bei dem die Anfrage echten Payload trägt, deshalb lohnt es sich zu wissen, wohin dieser Payload geht. CapSkip bietet zwei Varianten:

ModusLauscht aufSinnvoll, wenn
Lokal127.0.0.1, nur dieses GerätIhr Skript und der Löser laufen auf einer Maschine
ServerIhre Netzwerkadresse oder öffentliche IPWorker, ein VPS oder eine gehostete Plattform rufen über die API an

Im Server-Modus richten Sie den SDK-Host auf die Maschine mit dem Löser, und sonst ändert sich nichts in Ihrem Code, sodass sich ein Pool von Workern eine Instanz teilen kann. Eine statische öffentliche IP ist empfehlenswert, wenn die Aufrufer außerhalb Ihres eigenen Netzwerks sitzen. Die Details stehen unter Verbindungseinstellungen, und der Server-Modus bleibt trotzdem Ihre eigene Hardware und bleibt pauschal abgerechnet: Er verändert nur, wo der Löser läuft, nicht, wem er gehört oder wie abgerechnet wird.

FAQ

Was ist die maximale Bildgröße, die CapSkip akzeptiert?

600 kB und 1000px in jeder Dimension. Überschreiten Sie einen der beiden Werte, erhalten Sie ERROR_TOO_BIG_CAPTCHA_FILESIZE statt einer teilweisen Lösung. Beide Zahlen sind auf der API-Referenzseite veröffentlicht, und beide liegen deutlich über jedem echten Captcha-Bild. Wenn Sie sie erreichen, ist das ein Zeichen, dass Sie mehr von der Seite senden, als beabsichtigt.

Warum funktioniert dasselbe Bild lokal, schlägt aber auf meinem Server fehl?

Meist der Pfad, nicht das Bild. Ein relativer Pfad wird gegen das Arbeitsverzeichnis aufgelöst, in dem der Prozess gestartet wurde, und ein Dienst oder eine geplante Aufgabe startet selten dort, wo Sie es vermuten. Verwenden Sie einen absoluten Pfad und prüfen Sie dann, ob die Datei für das Konto lesbar ist, unter dem der Prozess läuft. Wenn der Löser selbst auf einer anderen Maschine läuft, stellen Sie sicher, dass Host und Port auf ihn zeigen und nicht auf loopback.

Kann ich beim Lösen von Bild-Captchas einen Proxy verwenden?

Nein. Die Proxy-Unterstützung deckt reCAPTCHA, Turnstile, GeeTest und CaptchaFox ab, also die Typen, bei denen der Löser die Seite selbst lädt. Ein Bild-Captcha besteht aus Bytes, die Sie bereits besitzen, es gibt also nichts, was ein Proxy dabei tun könnte, und der Parameter wird abgelehnt statt ignoriert.

Wie lange sollte ein Bild-Captcha dauern?

Im Normalbetrieb deutlich unter einer Sekunde. Das Polling-Timeout für Bildaufgaben liegt standardmäßig bei 120 Sekunden, was ein Sicherheitsnetz ist und keine Erwartung, und das SDK beginnt das Polling bei einer viertel Sekunde und verlängert die Abstände von dort aus. Wenn Sie Sekunden statt Millisekunden warten, ist die interessante Frage, was der Löser gerade tut, nicht, ob Ihr Timeout lang genug ist.

Die kürzeste Fassung

Finden Sie zuerst heraus, auf welcher Seite es gescheitert ist, bevor Sie etwas ändern. Eine Python-Exception bedeutet, dass das Bild Ihre Maschine nie verlassen hat, und ein Code, der mit ERROR beginnt, bedeutet, dass es angekommen und abgelehnt wurde. Ordnen Sie dann den Code zu: nichts angekommen ist ERROR_UPLOAD, angekommen aber nicht dekodierbar ist ERROR_INVALID_IMAGE, die Zeichenkette nicht dekodierbar ist ERROR_INVALID_BASE64, und zu groß bedeutet über 600 kB oder über 1000px. Validieren Sie die Bytes beim Download, und die meisten dieser Fälle hören auf, aufzutreten. Ein fehlgeschlagener Lösungsversuch nach einem sauberen Upload ist ein anderes Problem, behandelt in dem Leitfaden zu ERROR_CAPTCHA_UNSOLVABLE. Für die Parameter selbst ist die Bild-Captcha-Solver-Seite die Referenz, und die Python-Besonderheiten finden Sie auf der Python-Captcha-Solver-Seite. Auch die Wirtschaftlichkeit spricht hier dafür: unbegrenzter Captcha-Löser auf eigener Hardware bedeutet, dass ein erneuter Versuch nach einem fehlgeschlagenen Upload nichts kostet außer dem Versuch selbst.