Scarnox · Blog

Captcha solver hata yönetimi - yeniden deneme, backoff ve asla tekrarlanmaması gerekenler

Captcha çözme, değiştirme gücünüz olmayan bir sisteme, önceden haber vermeden değişebilen bir doğrulama için yapılan bir ağ çağrısıdır. Hataları iyi yönetmek, zarifçe bozulan bir pipeline ile bir yeniden deneme fırtınasında zaman kaybeden bir pipeline arasındaki farktır.

Scarnox ile ekonomi size yardım eder: başarısız çözümler ücretlendirilmez; yani bir yeniden deneme yalnızca zaman harcatır, başka bir şey değil. Ama yanlış hatayı tekrar denemek yine de o zamanı boşa harcar ve yeterince sert tekrar denemek kendi başına sorunlar yaratabilir. Bu rehber, bir Python işine yapıştırabileceğiniz bir yeniden deneme politikası verir.

Scarnox bugün PopularCaptcha'yı çözer. Aynı hata yönetimi herhangi bir captcha solver için geçerlidir; dolayısıyla aşağıdaki politika captcha türüne bağlı değildir.

Her zaman önce errorId'yi kontrol edin

Bir çözüm yanıtında errorId alanı bulunur. 0 başarıyı gösterir ve token solution.token altındadır. Başka her değer çözümün başarısız olduğu anlamına gelir. Başarısız bir yanıt ayrıca bir errorCode ve bir errorDescription taşır. İkisini de loglayın: kodu sonra aramak isteyeceksiniz, açıklamayı ise bir çalıştırma ters gittiğinde okuyacaksınız.

{"errorId": 1, "errorCode": "...", "errorDescription": "..."}

HTTP durumu 200 olduğu için bir yanıtın başarılı olduğunu varsaymayın. errorId değerini her seferinde kontrol edin.

Tekrar denenmesi gereken hatalar

Bazı hatalar geçicidir. Bunları üstel backoff ve küçük rastgele bir gecikme ile tekrar deneyin:

Bu hatalar tekrar denemeye değer, çünkü bir sonraki deneme, sizin tarafınızda bir değişiklik olmadan başarılı olabilir.

Tekrar denemeden önce düzeltilmesi gereken hatalar

Diğer hatalar beklemekle geçmez. Aynı girdiyle yeniden denemek aynı şekilde başarısız olur. Önce bunları düzeltin:

Her iki listede de olmayan bir hata ile karşılaştığınızda, onu düzeltilmesi gereken bir hata olarak ele alın. Hata metnine bakın, karar verin ve kodu doğru listeye ekleyin. Listeleri kısa ve gözden geçirilmiş tutun.

Yapıştırabileceğiniz bir politika

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:
        # Bizim tarafımızda ağ hatası ya da zaman aşımı: yeniden denemek güvenli.
        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"]

    # API bir hata bildirdi. Yalnızca geçici olduğunu gördüğünüz kodları yeniden deneyin.
    code = data.get("errorCode", "UNKNOWN")
    retryable = code in TRANSIENT_CODES
    raise SolveError(code, data.get("errorDescription", ""), retryable=retryable)

# API hata kodlarını yalnızca loglarınızda geçici olduklarını gördükten sonra buraya ekleyin.
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()  # bekleme süresi + rastgele gecikme (jitter)
            print(f"attempt {attempt + 1} failed ({err.code}), retrying in {delay:.1f}s")
            time.sleep(delay)
    raise RuntimeError("unreachable")

Politika, düzeltilmesi gereken bir hatayı asla tekrar denemez ve sabit sayıda denemeden sonra durur. TRANSIENT_CODES kümesi bilerek boş başlar: bir kodu yalnızca loglarınız geçici olduğunu gösterdikten sonra ekleyin.

Neden jitter önemlidir

Elli işçi aynı anda başarısız olur ve hepsi tam iki saniye uyursa, birlikte tekrar dener ve birlikte yeniden başarısız olurlar. Rastgele bir saniye kesri onları dağıtır. Bu, büyük API sağlayıcılarının kendi istemcileri için verdiği tavsiyenin aynısıdır.

Zaman aşımları

HTTP zaman aşımınızı tipik çözme süresinin çok üzerinde, yaklaşık 185 saniyeye ayarlayın. İstek token hazır olana kadar bloke olur; bu yüzden kısa bir istemci zaman aşımı, yavaş bir başarıyı boşuna tekrar denediğiniz bir başarısızlığa çevirebilir. Çözümden uzun bir istemci zaman aşımı güvenli ayardır.

Token'lar önbelleğe alınamaz

Token yaklaşık iki dakika içinde süresi dolar. Token'ları çalıştırmalar arasında önbelleğe almayın ve ikinci bir gönderim için yeniden kullanmayın. Token kullanıldıktan sonra bir gönderim başarısız olursa, eski token'ı tekrar göndermek yerine yeniden çözün. Zamanlama ayrıntıları için token süresi rehberi bölümüne bakın.

Birden çok işçi arasında eşzamanlılık ve backoff

Birçok işçi tek bir anahtarı paylaşıyorsa, bir işçideki backoff diğerlerini yavaşlatmaz. Eşzamanlı çözümler için bir semafor ya da kuyruk gibi ortak bir sınır ekleyin; böylece yeniden denemelerin patlaması bir istek patlamasına dönüşmez. Basit bir eşzamanlılık sınırı, hız sınırı hatalarının çoğunu başlamadan önce çözer.

Yardımcı olan loglama

Başarısız her denemede errorCode değerini, deneme numarasını, sitekey'i, sayfa host'unu ve hatanın yeniden denenip denenmediğini loglayın. API anahtarını ya da proxy şifresini loglamayın. Saat başına kodları sayan bir pano, sorunun kodunuzda, proxy'de mi yoksa serviste mi olduğunu size hızla söyler.

Sonraki adımlar

Yeniden denemelerinizi ücretsiz bir denemeyle test edin: hesap oluşturun ve kredi kartı gerekmeden ücretsiz deneme kredisiyle başlayın.