Scarnox · Blog

Szybkie rozwiązywanie captcha: jak skrócić opóźnienie w potoku

Szybki solver captcha to taki, który wywołujesz we właściwym momencie, równolegle, nie marnując żądań. Wywołanie Scarnox zwraca wynik, gdy token jest gotowy, więc jego czas zależy od typu captcha, docelowego serwisu i bieżącego obciążenia. Ten przewodnik nie obiecuje stałego czasu. Pokazuje, co kontrolujesz we własnym kodzie, jak to mierzyć i jak uruchamiać wiele rozwiązań naraz, aby czas, który spędzasz, był dobrze wykorzystany.

Co decyduje o czasie rozwiązania

Liczą się trzy rzeczy, a tylko jedna z nich zależy od Ciebie:

Skup wysiłek na trzecim punkcie. To jedyna część, którą możesz zmierzyć i zmienić.

Mierz przed optymalizacją

Owiń wywołanie licznikiem czasu i zapisz wynik. Chcesz znać czas rozwiązania i czas, jaki Twój kod spędza wokół niego:

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")

Uruchamiaj to na stronach, na których Ci zależy, o porach, o których uruchamiasz swoje zadania. Zachowuj wyniki. Pokażą, czy zmiana rzeczywiście pomogła.

Rozwiązuj równolegle

Rozwiązania są niezależne. Jeśli masz do obsłużenia dziesięć sitekey, czekanie na każde po kolei marnuje czas, który mógłbyś poświęcić pozostałym. Pula wątków wystarcza w większości skryptów, bo każde rozwiązanie to jedno żądanie HTTP, które większość czasu spędza na czekaniu:

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)

# Zacznij od małej liczby. Zwiększaj liczbę workerów tylko, dopóki błędów jest mało.
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}")

Jeśli przy zwiększaniu liczby workerów pojawiają się błędy wyglądające na limity tempa lub brak pojemności, zmniejsz ją. Równoległość pomaga tylko wtedy, gdy serwer za nią nadąża.

Nie rozwiązuj zawczasu

Najszybszy kod to kod, który nie czeka na token, którego jeszcze nie użyje. Zamawianie tokenów z wyprzedzeniem i ich przechowywanie to typowy błąd. Tokeny wygasają po około dwóch minutach od wydania, więc zapisany token jest często nieaktualny, gdy go potrzebujesz. Rozwiązuj, gdy formularz ma być za chwilę wysłany. Szczegółowe uzasadnienie znajdziesz w artykule token captcha wygasł: przyczyny i poprawki.

Skróć resztę ścieżki

Używaj ponownie tego, co nie wygasa

Część pracy można bezpiecznie współdzielić między rozwiązaniami. Trzymaj to poza gorącą ścieżką:

Te zmiany kosztują niewiele i usuwają narzut, który pojawia się przy każdym wywołaniu.

Mierz ogon, a nie średnią

Średni czas rozwiązania ukrywa wolne wywołania, które wstrzymują partię. Zapisuj czas każdego rozwiązania, a potem patrz na medianę i 95. percentyl. Potok, który kończy się w czasie najwolniejszego zadania, jest ograniczony przez ogon, więc niewielka zmiana w sposobie ponawiania wolnych wywołań może mieć większe znaczenie niż jakakolwiek zmiana mediany:

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]))

Porównuj podsumowanie przed i po każdej zmianie. Jeśli p95 prawie się nie rusza, czas jest tracony gdzie indziej w Twoim kodzie.

Sprawdzaj wynik, a nie tylko szybkość

Szybka porażka to nadal porażka. Licz rozwiązania zwracające błąd i traktuj rosnący odsetek błędów jako sygnał, by zwolnić. Nieudane rozwiązania nie są naliczane, ale czas na nie poświęcony to nadal czas, który potok stracił. Pełny wzorzec ponawiania znajdziesz w artykule obsługa błędów solvera captcha.

Następne kroki