Cara Memecahkan CAPTCHA di Job RQ Melampaui Batas 180 Detik

rq captcha - How to Solve CAPTCHAs in RQ Jobs Past the 180-Second Limit

Untuk memecahkan CAPTCHA di RQ, job queue Python yang dibangun di atas Redis, taruh seluruh urutannya dalam satu job: ambil halaman, pecahkan CAPTCHA dengan CapSkip, kirim form. Job CAPTCHA RQ lalu hanya perlu satu pengaturan yang diubah, yaitu timeout-nya. RQ memberi setiap job 180 detik kecuali Anda menentukan lain. SDK CapSkip menunggu hingga 300 detik untuk token reCAPTCHA, jadi pemecahan yang mengantre di belakang thread yang sibuk akan dimatikan di tengah polling. RQ mencatat JobTimeoutException, dan CapSkip tetap menyelesaikan pemecahan itu tanpa ada yang mengambil hasilnya. Panduan ini membahas job, timeout, worker, percobaan ulang, dan tempat worker bisa berjalan, termasuk Windows.

Apa yang Anda butuhkan

  • CapSkip yang berjalan di komputer Windows. Contoh kodenya memecahkan reCAPTCHA v2, dan bentuk job yang sama berlaku untuk setiap tipe yang didukung CapSkip.
  • Python 3.10 atau lebih baru dengan RQ 2.x, paket capskip, dan requests. Contoh kodenya dijalankan di RQ 2.12.
  • Server Redis, versi 5 atau lebih baru, yang bisa dijangkau baik oleh kode yang memasukkan job ke queue maupun oleh worker. RQ juga menerima Valkey 7.2 atau lebih baru.
  • Sebuah mesin untuk worker. Worker default RQ bergantung pada pemanggilan Unix yang tidak dimiliki Windows, jadi salah satu bagian di bawah membahas pilihan di Windows, di WSL, dan di mesin lain.
  • Sebuah alamat untuk pemecah CAPTCHA. Dalam mode Local, CapSkip menjawab di 127.0.0.1 hanya untuk perangkat itu. Dalam mode Server, CapSkip mendengarkan di alamat jaringan atau IP publik Anda, sehingga worker di mesin lain dapat memanggilnya melalui API. Keduanya diatur di pengaturan koneksi.
pip install rq capskip requests

Langkah 1: tulis job di modulnya sendiri

RQ menyimpan job sebagai path import sebuah fungsi beserta argumennya, dan worker meng-import fungsi itu saat mengambil job tersebut. Fungsi yang didefinisikan di skrip yang memasukkannya ke queue berada di __main__, yang tidak bisa di-import oleh worker, jadi RQ sudah menolaknya dengan ValueError saat job dimasukkan ke queue. Taruh job di modulnya sendiri:

# tasks.py: the worker imports jobs by module path, so keep them here
# pip install rq capskip requests
import os
from html.parser import HTMLParser

import requests
from capskip import CapSkip

solver = CapSkip(
    apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
    host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.getenv("CAPSKIP_PORT", "8080")),
)


class FormPage(HTMLParser):
    """Collects the reCAPTCHA sitekey and the hidden fields of its form."""

    def __init__(self):
        super().__init__()
        self.sitekey, self.hidden, self._form = None, {}, None

    def handle_starttag(self, tag, attrs):
        a = dict(attrs)
        if tag == "form":
            self._form = {}
        if a.get("data-sitekey"):
            self.sitekey = a["data-sitekey"]
            if self._form is not None:
                self.hidden = self._form
        hidden = (a.get("type") or "").lower() == "hidden"
        if tag == "input" and hidden and a.get("name") and self._form is not None:
            self._form[a["name"]] = a.get("value") or ""

    def handle_endtag(self, tag):
        if tag == "form":
            self._form = None


def sign_up(page_url, username):
    # One session per job, so the page's cookies go back with the form.
    session = requests.Session()
    page = FormPage()
    page.feed(session.get(page_url, timeout=30).text)

    # Solve inside the job, right before the post: tokens expire.
    token = solver.recaptcha(page.sitekey, page_url)["code"]

    resp = session.post(page_url, data={
        **page.hidden,
        "username": username,
        "g-recaptcha-response": token,
    }, headers={"Referer": page_url}, timeout=30)
    resp.raise_for_status()
    return resp.status_code

Setiap job membuka requests.Session miliknya sendiri, sehingga cookie sesi yang disetel halaman ikut terkirim kembali bersama form. Parser menyimpan field tersembunyi dari form yang memuat widget, termasuk token CSRF yang menjadi pasangan cookie itu, dan mengabaikan form pencarian atau newsletter di bagian lain halaman. Contoh ini mengirim kembali ke URL halaman; jika action milik form menunjuk ke tempat lain, kirim ke sana. Header Referer ada di sana karena requests tidak mengirim Referer maupun Origin, dan beberapa framework, salah satunya Django, menolak form post HTTPS yang tidak memiliki keduanya.

raise_for_status mengubah error HTTP, seperti 403 dari pemeriksaan CSRF yang gagal, menjadi job yang gagal, bukan keberhasilan yang diam-diam, dan itu penting begitu percobaan ulang aktif. Jika situs menolak token dengan halaman 200 biasa, job itu tetap dihitung berhasil, jadi periksa juga respons untuk melihat apa yang ditampilkan situs Anda saat berhasil.

Klien pemecah CAPTCHA dibuat saat modul di-import. Dengan worker default RQ, hal itu terjadi di work horse baru pada setiap job, dan dengan SimpleWorker sekali per worker. Keduanya tidak masalah, karena klien tidak menahan koneksi apa pun tetap terbuka di antara pemanggilan. SDK tidak membaca variabel lingkungan dengan sendirinya, itulah sebabnya kode membaca CAPSKIP_HOST, CAPSKIP_PORT dan CAPSKIP_API_KEY lalu memberikannya ke konstruktor. Arahkan worker yang berbeda ke mesin CapSkip yang berbeda dengan mengubah environment-nya, bukan kodenya.

Langkah 2: masukkan job ke queue dengan timeout yang sesuai untuk pemecahan

Inilah pengaturan yang menentukan apakah job CAPTCHA RQ bertahan menghadapi pemecahan yang lambat. Berikan job_timeout di setiap pemanggilan enqueue, atau beri objek Queue sebuah default_timeout. Nilai default itu berada di objek Python, bukan di Redis, jadi setiap skrip yang memasukkan job ke queue harus membangun Queue-nya dengan cara yang sama:

# enqueue.py
from redis import Redis
from rq import Queue

from tasks import sign_up

# RQ's default is 180 seconds; a reCAPTCHA solve may take 300.
queue = Queue("captcha", connection=Redis(), default_timeout=420)

job = queue.enqueue(sign_up, "https://example.com/signup", "YOUR_USERNAME")
print(job.id)

Angka itu berasal dari penjumlahan kasus terburuk. SDK melakukan polling untuk pemecahan reCAPTCHA hingga 300 detik, yaitu recaptchaTimeout-nya, lalu memunculkan TimeoutException. Pengambilan halaman dan kiriman form masing-masing memberikan timeout=30 ke requests, yang membatasi koneksi dan setiap penantian data, bukan seluruh permintaan. Totalnya sekitar 360 detik, dan 420 menyisakan cadangan satu menit untuk halaman yang lambat.

CapSkip punya hitungan waktunya sendiri. Di pengaturan reCAPTCHA-nya, sebuah tugas bisa menunggu 250 detik untuk mendapatkan salah satu dari 10 thread-nya, lalu punya waktu hingga 250 detik untuk memuat challenge dan 250 detik lagi untuk memecahkannya. Tugas yang kehabisan salah satu dari batas waktu itu digagalkan oleh CapSkip dengan ApiException. Tugas yang cukup lama menunggu thread lalu pemecahannya lambat bisa lebih dulu melewati batas 300 detik milik SDK, dan berakhir dengan TimeoutException. Namun SDK tidak menyetel timeout pada pemanggilan HTTP-nya sendiri ke CapSkip. Jika CapSkip berhenti menjawab di tengah sebuah permintaan, worker default RQ tetap mengakhiri job itu saat job_timeout tercapai; SimpleWorker, yang dibahas di bawah, tidak bisa.

Inilah yang terjadi jika Anda tetap memakai nilai default. Pada detik ke-180, RQ memunculkan JobTimeoutException di dalam job, di titik mana pun job itu sedang berada, yang untuk job CAPTCHA hampir selalu berupa loop polling milik SDK. Job itu masuk ke registry job gagal dengan Task exceeded maximum timeout value (180 seconds) sebagai error-nya. CapSkip tidak diberi tahu. CapSkip terus bekerja, menghasilkan token yang tidak diambil siapa pun, dan thread yang dipakainya terbuang sia-sia. Kami mereproduksi persis hal itu dengan pemecahan yang memakan waktu 200 detik.

Jangan menangkap Exception di sekitar pemecahan

JobTimeoutException adalah subclass Exception biasa, yang dimunculkan ke dalam kode Anda. except Exception yang luas di sekitar pemecahan akan menangkapnya, job kembali secara normal, dan RQ mencatatnya sebagai selesai, tanpa jejak timeout di mana pun. Dalam pengujian kami, job yang ditulis seperti itu kehabisan batas waktunya di tengah pemecahan dan worker mencatat Job OK. Tangkap base class milik SDK sendiri sebagai gantinya:

import logging

from capskip import CapSkipError

log = logging.getLogger(__name__)

try:
    token = solver.recaptcha(page.sitekey, page_url)["code"]
except CapSkipError as exc:
    # The SDK's errors only. RQ's JobTimeoutException is not one of
    # them, so a job that ran out of time still fails as it should.
    log.warning("solve failed for %s: %s", page_url, exc)
    raise

Langkah 3: jalankan worker sebanyak jumlah thread CapSkip

Satu worker RQ menjalankan satu job pada satu waktu, jadi untuk job CAPTCHA RQ, jumlah worker sama dengan jumlah pemecahan yang sedang berjalan. CapSkip memecahkan 10 reCAPTCHA sekaligus secara default, sesuai pengaturan Max. Threads-nya, dan membuat tugas berikutnya menunggu thread yang kosong. Worker di atas jumlah itu tidak menyelesaikan lebih banyak pekerjaan. Tugas mereka justru mengantre di dalam CapSkip, dan antrean itu menggerogoti timeout setiap job. Satu worker per thread adalah ukuran yang tepat:

# Ten workers for CapSkip's ten reCAPTCHA threads
rq worker-pool captcha -n 10

worker-pool menjalankan worker dan memulai ulang worker yang mati. Setiap worker yang dijalankannya memiliki scheduler yang aktif, dan salah satunya menjalankan scheduler itu untuk queue. Percobaan ulang di langkah berikutnya bergantung pada hal itu. Jika Anda menaikkan Max. Threads di CapSkip, naikkan juga ukuran pool agar sesuai, dan hitung setiap worker di setiap mesin yang memanggil CapSkip yang sama.

Langkah 4: percobaan ulang, dan mengapa butuh scheduler

Secara default, RQ tidak mencoba ulang apa pun. Job yang gagal masuk ke registry job gagal dan tetap di sana. Agar RQ mencoba lagi, berikan Retry saat Anda memasukkan job ke queue:

from rq import Retry

job = queue.enqueue(
    sign_up, "https://example.com/signup", "YOUR_USERNAME",
    # Up to two more runs: 30 seconds, then 120 seconds after a failure.
    retry=Retry(max=2, interval=[30, 120]),
)

Percobaan ulang dengan interval tidak dimasukkan kembali ke queue. RQ menjadwalkannya, dan hanya worker yang menjalankan scheduler yang pernah memindahkannya kembali. worker-pool selalu menjalankan satu scheduler. Worker tunggal yang dijalankan dengan rq worker biasa tidak menjalankannya, sehingga setiap job yang dicoba ulang menunggu di registry terjadwal selamanya tanpa error. Kami melihat sendiri hal itu terjadi, dan job yang sama berjalan dalam hitungan detik begitu worker dengan scheduler dijalankan. Jalankan worker tunggal seperti ini:

rq worker captcha --with-scheduler

Percobaan ulang menjalankan seluruh job lagi: halaman baru, pemecahan baru, dan kiriman baru. Itulah yang Anda inginkan, karena token reCAPTCHA hanya diterima sekali, jadi tidak ada yang perlu dilanjutkan. Itu juga alasan job sebaiknya berakhir pada kiriman form. Apa pun yang bisa gagal setelah kiriman yang berhasil akan membuat percobaan ulang mengirim untuk kedua kalinya.

Satukan pemecahan dan kiriman form dalam job yang sama

Memisahkan pekerjaan ini memang menggoda. Satu job memecahkan dan mengembalikan token, lalu job kedua, atau kode yang memasukkan job pertama ke queue, mengirimkannya belakangan. Jangan lakukan itu. Token reCAPTCHA kedaluwarsa sekitar dua menit setelah diterbitkan, dan RQ tidak menjanjikan kapan job berikutnya dimulai. Saat beban tinggi, job itu menunggu di belakang semua job lain di queue. RQ juga hanya menyimpan nilai kembalian job selama 500 detik secara default. Memecahkan di dalam job, tepat sebelum kiriman form, menjaga seluruh masa hidup token di dalam satu pemanggilan fungsi. Artikel tentang berapa lama token reCAPTCHA tetap valid membahas aturan kedaluwarsanya secara rinci.

Tempat worker RQ bisa berjalan: Windows, WSL, atau mesin lain

Worker default RQ melakukan fork proses baru untuk setiap job. Windows tidak memiliki os.fork, jadi di sana worker berhenti dengan AttributeError pada job pertamanya. Dokumentasi RQ menyatakan bahwa worker membutuhkan fork dan mengarahkan pengguna Windows ke WSL, meskipun halamannya tentang kelas worker menawarkan SpawnWorker untuk Windows. Di RQ 2.12, SpawnWorker juga gagal, karena ia menunggu setiap job dengan os.wait4, yang juga tidak dimiliki Windows. Dengan begitu tersisa tiga jalur.

Di PC CapSkip itu sendiri, gunakan SimpleWorker. SimpleWorker menjalankan setiap job di dalam proses worker, bukan di proses anak. Di Windows dengan RQ 2.12, worker ini menjalankan job kami dan scheduler, dan worker-pool menjalankan beberapa worker seperti itu secara berdampingan. Anggap itu sebagai hasil pengujian kami, bukan dukungan resmi dari upstream. SimpleWorker menegakkan job_timeout dengan timer, tetapi timer itu hanya terpicu di antara statement Python. Saat kami membuat pemanggilan ke CapSkip macet selama 40 detik dengan job_timeout 8 detik, job gagal pada detik ke-40, bukan ke-8. Pemanggilan yang tidak pernah kembali akan terus menahan worker itu sampai Anda memulai ulang worker tersebut. CapSkip tetap dalam mode Local, karena worker berada di mesin yang sama. Redis tidak punya build Windows resmi, tetapi Redis yang berjalan di WSL menjawab program Windows di 127.0.0.1, dan begitulah cara kami menguji penyiapan ini:

# On Windows: no fork, so run jobs inside the worker process
rq worker-pool captcha -n 10 -w rq.worker.SimpleWorker

Di WSL 2 pada PC yang sama, perlakukan worker sebagai mesin lain. Dalam jaringan NAT default WSL 2, 127.0.0.1 di dalam Linux adalah VM Linux, bukan Windows, jadi worker di sana tidak bisa menjangkau CapSkip dalam mode Local. Kami sudah memeriksanya, dan koneksinya gagal. Alihkan CapSkip ke mode Server dan arahkan CAPSKIP_HOST ke alamat host Windows sebagaimana terlihat dari Linux. Panduan Microsoft tentang jaringan WSL menunjukkan cara menemukan alamat itu, dan juga menjelaskan mode jaringan mirrored di Windows 11 22H2 dan yang lebih baru, tempat Linux bisa menjangkau server Windows di 127.0.0.1.

Di server Linux, host container, atau sebuah platform, gunakan mode Server. CapSkip lalu mendengarkan di alamat jaringan atau IP publik Anda, dan setiap worker terhubung melalui API yang sama. Gunakan IP publik statis jika rutenya melewati internet dan aktifkan validasi API key. CapSkip membuka port-nya untuk semua alamat saat mode Server dimulai, jadi untuk membatasinya hanya pada worker Anda, tambahkan aturan blokir Windows Firewall untuk alamat lainnya. Pemecah CAPTCHA tetap berupa mesin Windows milik Anda sendiri, dan pemecahannya tetap tanpa kuota, berapa pun jumlah worker yang memanggilnya.

Contoh lengkap yang berfungsi

Dua file dan satu perintah. tasks.py berisi job, dengan penanganan exception yang sempit dari Langkah 2; enqueue.py menyetel timeout dan percobaan ulang.

# tasks.py
# pip install rq capskip requests
import logging
import os
from html.parser import HTMLParser

import requests
from capskip import CapSkip, CapSkipError

log = logging.getLogger(__name__)
solver = CapSkip(
    apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
    host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.getenv("CAPSKIP_PORT", "8080")),
)


class FormPage(HTMLParser):
    def __init__(self):
        super().__init__()
        self.sitekey, self.hidden, self._form = None, {}, None

    def handle_starttag(self, tag, attrs):
        a = dict(attrs)
        if tag == "form":
            self._form = {}
        if a.get("data-sitekey"):
            self.sitekey = a["data-sitekey"]
            if self._form is not None:
                self.hidden = self._form
        hidden = (a.get("type") or "").lower() == "hidden"
        if tag == "input" and hidden and a.get("name") and self._form is not None:
            self._form[a["name"]] = a.get("value") or ""

    def handle_endtag(self, tag):
        if tag == "form":
            self._form = None


def sign_up(page_url, username):
    session = requests.Session()
    page = FormPage()
    page.feed(session.get(page_url, timeout=30).text)
    if not page.sitekey:
        raise ValueError(f"no data-sitekey on {page_url}")

    try:
        token = solver.recaptcha(page.sitekey, page_url)["code"]
    except CapSkipError as exc:
        # RQ's JobTimeoutException is not a CapSkipError, so it passes.
        log.warning("solve failed for %s: %s", page_url, exc)
        raise

    resp = session.post(page_url, data={
        **page.hidden,
        "username": username,
        "g-recaptcha-response": token,
    }, headers={"Referer": page_url}, timeout=30)
    resp.raise_for_status()
    return resp.status_code
# enqueue.py
from redis import Redis
from rq import Queue, Retry

from tasks import sign_up

queue = Queue("captcha", connection=Redis(), default_timeout=420)

for user in ["YOUR_USERNAME_1", "YOUR_USERNAME_2"]:
    job = queue.enqueue(
        sign_up, "https://example.com/signup", user,
        retry=Retry(max=2, interval=[30, 120]),
    )
    print(job.id)
# Linux or WSL; on Windows add -w rq.worker.SimpleWorker
rq worker-pool captcha -n 10

Jalankan pool dari folder yang berisi tasks.py, karena dari sanalah worker meng-import-nya. rq info menampilkan jumlah job yang menunggu, sedang dieksekusi, selesai, dan gagal untuk setiap queue, beserta worker-nya. Perintah itu tidak menghitung registry terjadwal, tempat percobaan ulang berinterval menunggu. Job yang gagal menyimpan traceback-nya selama satu tahun secara default: ambil job dengan Job.fetch lalu baca exc_info-nya.

Kesalahan umum dan artinya

Apa yang Anda lihatPenyebabPerbaiki
JobTimeoutException: Task exceeded maximum timeout value (180 seconds)Job berjalan dengan timeout default RQ, dan pemecahannya memakan waktu lebih lamaSetel job_timeout atau default_timeout milik queue ke 420
Job ditandai selesai, tetapi tidak ada yang sampai ke situsexcept Exception yang luas menangkap JobTimeoutException milik RQTangkap CapSkipError di sekitar pemecahan, bukan Exception
Job yang dicoba ulang tidak pernah berjalan lagiPercobaan ulang memiliki interval dan tidak ada worker yang menjalankan scheduler. Job menunggu di registry terjadwal, yang tidak dihitung oleh rq infoGunakan worker-pool, atau jalankan worker tunggal dengan opsi scheduler
ValueError: Functions from the __main__ module cannot be processed by workersFungsi job didefinisikan di skrip yang memasukkannya ke queuePindahkan ke modul seperti tasks.py lalu import dari sana
AttributeError untuk os.fork atau os.wait4 saat worker Windows mengambil jobWorker default dan SpawnWorker sama-sama memakai pemanggilan khusus UnixJalankan worker dengan rq.worker.SimpleWorker. Job yang sempat diambil worker tadi tetap berstatus sedang dieksekusi sampai proses pembersihan RQ menandainya gagal
Situs menolak kiriman form karena sesi dianggap kedaluwarsaHalaman diambil dan form dikirim lewat sesi yang berbeda, atau tanpa RefererGunakan satu requests.Session per job dan kirim URL halaman sebagai Referer
NetworkException pada pemecahan pertama dari WSL atau mesin lain127.0.0.1 di sana bukan mesin CapSkipAlihkan CapSkip ke mode Server dan setel CAPSKIP_HOST ke alamatnya
ApiException yang berisi ERROR_CAPTCHA_UNSOLVABLECapSkip menggagalkan tugas itu, misalnya setelah timeout tunggu atau timeout pemecahan miliknya sendiriBiarkan Retry menjalankan job itu lagi, dan jaga jumlah worker sama dengan atau di bawah Max. Threads
TimeoutException setelah 300 detikSebuah tugas menunggu thread lalu pemecahannya lambat, sehingga gabungan keduanya melewati 300 detik, atau CapSkip ditutup di tengah pemecahanSesuaikan ukuran pool dengan Max. Threads dan pastikan CapSkip masih berjalan

FAQ

Apakah timeout job RQ menghentikan pemecahan di CapSkip?

Tidak. Timeout itu mengakhiri kode Python Anda, bukan tugas yang sedang dikerjakan CapSkip. CapSkip menyelesaikannya, atau menggagalkannya menurut hitungan waktunya sendiri, dan hasilnya menunggu polling yang tidak pernah datang. Itulah sebabnya timeout harus berada di atas waktu pemecahan, bukan di bawahnya: job yang dimatikan di tengah pemecahan membuat Anda kehilangan job itu sekaligus satu thread CapSkip.

Haruskah job memakai AsyncCapSkip?

Tidak ada keuntungannya. RQ memang menjalankan job async def, di dalam event loop miliknya sendiri. Setiap worker tetap menjalankan satu job pada satu waktu, jadi paralelismenya tetap berasal dari jumlah worker. Klien CapSkip biasa menjaga job tetap sederhana. AsyncCapSkip baru bermanfaat di program asyncio yang berjalan lama dan menjalankan banyak pemecahan dari satu proses.

Bisakah worker di platform hosting menjangkau CapSkip?

Bisa. Alihkan CapSkip ke mode Server di pengaturan koneksi agar ia mendengarkan di alamat jaringan, bukan di loopback. Setel CAPSKIP_HOST di environment worker; kode job tidak berubah. Worker di VPS, platform container, atau host terkelola semuanya terhubung melalui API HTTP yang sama. Gunakan IP publik statis, dan aturan blokir Windows Firewall untuk setiap alamat yang bukan worker. Pemecah CAPTCHA tetap berada di perangkat milik Anda, jadi menambah worker tidak menambah biaya per pemecahan.

Apa bedanya dengan memecahkan CAPTCHA di Celery?

Pekerjaannya sama: pecahkan dan kirim dalam satu task, dengan batas waktu di atas batas milik pemecah CAPTCHA itu sendiri. Mekanisme di sekitarnya yang berbeda. Celery punya batas waktu lunak dan keras serta model acknowledgement dengan jebakannya sendiri. RQ punya satu timeout per job, 180 detik secara default, yang dimunculkan di dalam job sebagai exception, dan worker default-nya mematikan proses job itu 60 sampai 90 detik kemudian jika job masih berjalan. Percobaan ulang dengan interval juga membutuhkan scheduler milik RQ. Sisi Celery dibahas dalam panduan memecahkan CAPTCHA di task Celery.

Versi singkatnya

Untuk job CAPTCHA RQ, taruh pengambilan halaman, pemecahan, dan kiriman form dalam satu fungsi job di modulnya sendiri. Masukkan job ke queue dengan job_timeout 420 detik, atau jadikan itu nilai default queue, karena 180 detik bawaan RQ lebih pendek daripada waktu yang mungkin dibutuhkan pemecahan reCAPTCHA. Tangkap CapSkipError di sekitar pemecahan, jangan pernah Exception. Jalankan worker sebanyak jumlah thread CapSkip dengan worker-pool, yang juga menjalankan scheduler yang dibutuhkan percobaan ulang berinterval. Di Windows, gunakan SimpleWorker; dari WSL atau mesin lain, alihkan CapSkip ke mode Server.

Sepuluh worker yang memecahkan CAPTCHA sepanjang waktu adalah persis jenis beban yang ditagih per seribu pemecahan oleh layanan berbayar per pemakaian. Karena berjalan di mesin Anda sendiri, sebuah pemecah captcha menangani beban itu tanpa tagihan per pemecahan.