Scarnox · Blog

Solving captcha yang cepat: cara memangkas latensi di pipeline Anda

Solver captcha yang cepat adalah yang bisa Anda panggil di waktu yang tepat, secara paralel, tanpa membuang request. Panggilan solve Scarnox kembali saat token siap, jadi durasinya bergantung pada jenis captcha, situs target, dan beban saat itu. Panduan ini tidak menjanjikan waktu tetap. Panduan ini menunjukkan hal-hal yang bisa Anda kendalikan di kode sendiri, cara mengukurnya, dan cara menjalankan banyak solve sekaligus, agar waktu yang Anda habiskan digunakan dengan baik.

Apa yang menentukan waktu solve

Ada tiga hal yang penting, dan hanya satu di antaranya ada di tangan Anda:

Fokuskan usaha Anda pada poin ketiga. Itulah bagian yang bisa Anda ukur dan ubah.

Ukur sebelum mengoptimasi

Bungkus panggilan dengan timer dan catat hasilnya. Anda ingin tahu waktu solve dan waktu yang dihabiskan kode Anda di sekitarnya:

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

Jalankan ini terhadap halaman yang Anda pedulikan, pada waktu Anda menjalankan job. Simpan angkanya. Angka itu memberi tahu Anda apakah sebuah perubahan benar-benar membantu.

Jalankan solve secara paralel

Setiap solve bersifat independen. Jika Anda punya sepuluh sitekey untuk ditangani, menunggu satu per satu membuang waktu yang bisa Anda pakai untuk yang lain. Thread pool sudah cukup untuk sebagian besar script karena setiap solve adalah satu request HTTP yang sebagian besar waktunya dihabiskan untuk menunggu:

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)

# Mulai kecil. Naikkan jumlah worker hanya selama error tetap rendah.
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}")

Jika Anda melihat error yang tampak seperti masalah rate limit atau kapasitas saat jumlah worker dinaikkan, turunkan lagi. Paralelisme hanya membantu jika server bisa mengimbanginya.

Jangan solve terlalu dini

Kode tercepat adalah kode yang tidak menunggu token yang belum akan dipakai. Meminta token jauh-jauh hari lalu menyimpannya adalah kesalahan yang paling umum. Token kedaluwarsa sekitar dua menit setelah diterbitkan, jadi token yang disimpan sering kali sudah basi saat Anda membutuhkannya. Lakukan solve saat form hampir dikirim. Alasan detailnya ada di captcha token kedaluwarsa: penyebab dan solusinya.

Jaga bagian lain dari jalur tetap pendek

Gunakan ulang apa yang tidak kedaluwarsa

Sebagian pekerjaan bisa dibagi dengan aman di antara solve. Jauhkan hal-hal ini dari jalur utama:

Perubahan ini murah dan menghilangkan overhead yang muncul di setiap panggilan.

Ukur ekor distribusinya, bukan rata-ratanya

Rata-rata waktu solve menyembunyikan panggilan lambat yang menahan sebuah batch. Catat waktu setiap solve, lalu lihat median dan persentil ke-95. Pipeline yang selesai dalam waktu job paling lambatnya dibatasi oleh ekor distribusinya, jadi perubahan kecil pada cara Anda me-retry panggilan yang lambat bisa lebih berpengaruh dibanding perubahan apa pun pada 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]))

Bandingkan ringkasannya sebelum dan sesudah setiap perubahan. Jika p95 hampir tidak bergerak, berarti waktunya dihabiskan di bagian lain dari kode Anda.

Periksa hasilnya, bukan hanya kecepatannya

Kegagalan yang cepat tetaplah kegagalan. Hitung solve yang mengembalikan error, dan anggap kenaikan tingkat error sebagai sinyal untuk memperlambat. Solve yang gagal tidak dikenai biaya, tetapi waktu yang dihabiskan untuknya tetap waktu yang hilang dari pipeline Anda. Pola retry lengkapnya ada di penanganan error solver captcha.

Langkah selanjutnya