Scarnox · Blog

Schnelles Captcha-Lösen: so verkürzen Sie die Latenz in Ihrer Pipeline

Ein schneller Captcha-Solver ist einer, den Sie zum richtigen Zeitpunkt, parallel und ohne verschwendete Anfragen aufrufen können. Der Lösungsaufruf der Scarnox-API kehrt zurück, sobald das Token bereit ist, seine Dauer hängt also vom Captcha-Typ, der Zielwebsite und der aktuellen Auslastung ab. Dieser Leitfaden verspricht keine feste Zeit. Er zeigt, was Sie in Ihrem eigenen Code kontrollieren, wie Sie messen und wie Sie viele Lösungen gleichzeitig ausführen, damit die Zeit, die Sie aufwenden, gut genutzt ist.

Was die Lösungszeit bestimmt

Drei Dinge sind wichtig, und nur eines davon liegt in Ihrer Hand:

Konzentrieren Sie Ihren Aufwand auf den dritten Punkt. Er ist der Teil, den Sie messen und ändern können.

Vor dem Optimieren messen

Umgeben Sie den Aufruf mit einem Timer und protokollieren Sie das Ergebnis. Sie wollen die Lösungszeit wissen und die Zeit, die Ihr Code um sie herum verbringt:

import time
import requests

API_KEY = "scarnox_YOURAPIKEY"
URL = "https://api.scarnox.com/api/tasks/create"

def timed_solve(sitekey: str, page_url: str) -> dict:
    started = time.perf_counter()
    r = requests.post(
        URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "captcha_type": "PopularCaptchaTaskProxyless",
            "site_key": sitekey,
            "site_url": page_url,
        },
        timeout=185,
    )
    elapsed = time.perf_counter() - started
    data = r.json()
    if data.get("errorId") != 0:
        return {"ok": False, "error": data.get("errorCode"), "seconds": elapsed}
    return {"ok": True, "token": data["solution"]["token"], "seconds": elapsed}

result = timed_solve("10000000-ffff-ffff-ffff-000000000001", "https://example.com/signup")
print(result["ok"], round(result["seconds"], 2), "s")

Führen Sie dies gegen die Seiten aus, die Ihnen wichtig sind, zu den Zeiten, in denen Ihre Jobs laufen. Bewahren Sie die Zahlen auf. Sie zeigen Ihnen, ob eine Änderung wirklich geholfen hat.

Lösungen parallel ausführen

Lösungen sind voneinander unabhängig. Wenn Sie zehn Sitekeys bearbeiten müssen, verschwenden Sie Zeit, wenn Sie auf jeden einzeln warten. Ein Thread-Pool reicht für die meisten Skripte, weil jede Lösung eine HTTP-Anfrage ist, die den größten Teil ihrer Zeit mit Warten verbringt:

from concurrent.futures import ThreadPoolExecutor

jobs = [
    ("10000000-ffff-ffff-ffff-000000000001", "https://example.com/signup"),
    ("10000000-ffff-ffff-ffff-000000000001", "https://example.com/register"),
    ("10000000-ffff-ffff-ffff-000000000001", "https://example.com/contact"),
]

def run(job):
    sitekey, page_url = job
    return timed_solve(sitekey, page_url)

# Klein anfangen. Die Zahl der Worker nur erhöhen, solange die Fehler niedrig bleiben.
with ThreadPoolExecutor(max_workers=5) as pool:
    results = list(pool.map(run, jobs))

for r in results:
    status = "ok" if r["ok"] else r["error"]
    print(f"{r['seconds']:.2f} s  {status}")

Treten bei einer Erhöhung der Worker-Zahl Fehler auf, die wie Ratenlimit- oder Kapazitätsprobleme aussehen, senken Sie sie wieder. Parallelität hilft nur, solange der Server mithält.

Nicht zu früh lösen

Der schnellste Code ist der Code, der nicht auf ein Token wartet, das er noch nicht braucht. Token im Voraus anzufordern und zu speichern ist der übliche Fehler. Tokens laufen etwa zwei Minuten nach der Ausstellung ab, ein gespeichertes Token ist also oft veraltet, wenn Sie es brauchen. Lösen Sie, wenn das Formular gleich abgesendet werden soll. Die ausführliche Begründung steht unter Captcha-Token abgelaufen: Ursachen und Lösungen.

Den übrigen Weg kurz halten

Wiederverwenden, was nicht abläuft

Manche Arbeit lässt sich sicher über Lösungen hinweg teilen. Halten Sie diese aus dem heißen Pfad heraus:

Diese Änderungen kosten wenig und entfernen Overhead, der bei jedem Aufruf anfällt.

Das Ende der Verteilung messen, nicht den Durchschnitt

Eine durchschnittliche Lösungszeit verbirgt die langsamen Aufrufe, die einen Batch aufhalten. Erfassen Sie daher jede Lösungszeit und betrachten Sie den Median und das 95. Perzentil. Eine Pipeline, die in der Zeit ihres langsamsten Jobs fertig wird, wird vom Ende der Verteilung begrenzt. Eine kleine Änderung daran, wie Sie langsame Aufrufe wiederholen, kann deshalb mehr bewirken als jede Änderung am Median:

import statistics

def summarise(seconds: list[float]) -> str:
    ordered = sorted(seconds)
    p95 = ordered[int(0.95 * (len(ordered) - 1))]
    return f"median {statistics.median(ordered):.2f} s, p95 {p95:.2f} s, n={len(ordered)}"

print(summarise([r["seconds"] for r in results]))

Vergleichen Sie die Zusammenfassung vor und nach jeder Änderung. Bewegt sich das p95 kaum, wird die Zeit an anderer Stelle in Ihrem Code verbraucht.

Das Ergebnis prüfen, nicht nur die Geschwindigkeit

Ein schneller Fehler ist trotzdem ein Fehler. Zählen Sie die Lösungen, die einen Fehler liefern, und behandeln Sie eine steigende Fehlerquote als Signal, langsamer zu werden. Fehlgeschlagene Lösungen werden nicht berechnet, aber die Zeit, die für sie verbraucht wurde, ist trotzdem Zeit, die Ihre Pipeline verloren hat. Das vollständige Wiederholungsmuster steht unter Fehlerbehandlung für Captcha-Solver.

Nächste Schritte