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:
- Das Captcha selbst. Manche Challenge-Typen dauern länger als andere. Das ist eine Eigenschaft des Captchas der Website, nicht Ihres Codes.
- Die Auslastung des Solvers. Eine ausgelastete Phase kann einen einzelnen Aufruf verlangsamen. Die Wiederholungslogik sollte damit umgehen, statt den Aufruf vorzeitig abzubrechen.
- Ihre eigene Pipeline. Zu früh zu lösen, jede Anfrage hintereinander abzuarbeiten und auf nicht zusammenhängende Arbeit zu warten, kostet Zeit, die Sie entfernen können.
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
- Halten Sie pro Website eine HTTP-Sitzung, damit Sie nicht für jede Anfrage neu verbinden.
- Führen Sie zwischen Lösung und Absenden keine langsame Verarbeitung oder Datenbankarbeit aus.
- Verwenden Sie ein sinnvolles Timeout für Ihre eigenen Anfragen. Der Lösungsaufruf braucht etwa 185 Sekunden, weil er blockiert, bis das Token bereit ist. Ihre anderen Aufrufe sollten viel kürzer sein.
- Senden Sie nur dann einen Proxy, wenn der Aufgabentyp ihn braucht. Die Proxy-Aufgabentypen nehmen ein Feld
proxyim Formathttp://user:pass@host:portentgegen, aber ein Proxy ist für sich genommen kein Geschwindigkeitsgewinn.
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:
- Sitekeys. Lesen Sie den Sitekey einmal pro Seitenvorlage und speichern Sie ihn. Er läuft nicht ab wie ein Token. Lesen Sie ihn nur erneut, wenn das Formular anfängt zu scheitern.
- HTTP-Verbindungen. Eine Sitzung mit Keep-Alive vermeidet bei jedem Aufruf einen neuen TLS-Handshake. Erstellen Sie eine Sitzung pro Thread oder pro Worker, nicht eine pro Anfrage.
- Geparste Konfiguration. Laden Sie Ihre Zielliste, Proxy-Liste und Einstellungen einmal beim Start, statt Dateien innerhalb der Lösungsfunktion zu lesen.
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.