Cara Memecahkan CAPTCHA di Prefect Flow dengan Retry

Langkah captcha di Prefect adalah satu task dengan retry padanya, dan seluruh flow-nya sekitar dua puluh baris. Detail yang mengejutkan orang yang datang dari platform terkelola lain justru menyenangkan: Prefect Cloud tidak pernah menjalankan kode Anda. Prefect Cloud menjadwalkan pekerjaan, lalu worker di mesin Anda sendiri mengambilnya lewat koneksi keluar. Jadi solver bisa berada di 127.0.0.1 dan flow-nya tetap menjangkaunya, hal yang tidak berlaku di Zapier atau Make.com. Dua hal yang benar-benar sering salah adalah perilaku retry dan caching, dan masing-masing hanya butuh satu argumen.
Apa yang Anda butuhkan
- Prefect 3 dan Python 3.10 atau lebih baru, plus CapSkip Python SDK.
- Sebuah workspace Prefect Cloud atau server Prefect yang di-host sendiri. Keduanya bekerja sama saja untuk keperluan ini.
- URL halaman formulir yang terproteksi, beserta sitekey-nya.
- CapSkip berjalan dalam mode Local ketika worker dan solver berbagi satu mesin, atau dalam mode Server ketika keduanya terpisah. Keduanya dijelaskan di pengaturan koneksi.
# pip install prefect pip install -U prefect capskip # Point the CLI at your workspace, then start a worker # on the machine that should run the flows. prefect cloud login
Di mana flow Anda sebenarnya berjalan
Inilah pertanyaan yang menentukan seluruh penyiapan jaringan Anda, jadi jawablah lebih dulu. Default Prefect adalah model hibrida: lapisan orkestrasinya di-host, dan lapisan eksekusinya milik Anda. Prefect Cloud menyimpan metadata dan mengoordinasikan run, ia tidak mengeksekusi kode Anda, dan ia tidak memerlukan akses masuk ke jaringan Anda. Worker yang Anda jalankan di dalam infrastruktur Anda sendiri melakukan polling keluar untuk mencari pekerjaan lalu menjalankan run secara lokal.
Konsekuensi praktisnya layak dinyatakan dengan gamblang. Jalankan Process worker di mesin Windows yang sama dengan CapSkip dan flow-nya memanggil 127.0.0.1:8080 persis seperti skrip di meja Anda. Tanpa tunnel, tanpa alamat publik, tanpa sertifikat. Itu kebalikan dari situasi di platform otomatisasi yang hanya berbasis cloud, dan itulah alasan utama sebuah orkestrator merupakan tempat yang nyaman untuk menaruh pekerjaan pemecahan CAPTCHA.
Ada satu pengecualian dan Anda perlu mengetahuinya sebelum memilih work pool. Work pool Prefect Managed menjalankan flow Anda di infrastruktur milik Prefect alih-alih milik Anda, yang praktis tetapi sepenuhnya menghilangkan opsi loopback. Dalam konfigurasi itu solver membutuhkan mode Server dan alamat yang dapat dijangkau. Prefect memublikasikan enam alamat keluar statis yang dipakai run Managed, jadi Anda bisa mengizinkan persis alamat-alamat itu melewati firewall menuju port solver dan menutup semua sisanya. Run Managed juga harus memakai image Prefect resmi dan dibatasi 24 jam, satu alasan lagi mengapa work pool Process atau Docker biasanya lebih cocok di sini.
Langkah 1: simpan kuncinya di sebuah Secret block
Jangan menuliskan API key secara hardcode di file flow. Prefect menyertakan Secret block, nilainya terenkripsi saat tersimpan di backend, dan memuatnya hanya butuh dua baris. Simpan sekali dari sebuah shell Python.
# pip install prefect
from prefect.blocks.system import Secret
secret = Secret(value="YOUR_API_KEY")
secret.save("capskip-api-key")
# Rotating it later needs overwrite, or the save is refused.
# secret.save("capskip-api-key", overwrite=True)Langkah 2: tulis task pemecahannya
Satu task, satu pemecahan. Beri retry padanya, karena panggilan ke solver adalah panggilan jaringan dan panggilan jaringan bisa gagal. Prefect menerima jeda tetap, daftar jeda, atau helper exponential backoff, dan faktor jitter menyebarkan retry sehingga sekumpulan kegagalan tidak kembali serentak.
Argumen penting yang satunya lagi. Token CAPTCHA hanya sekali pakai dan kedaluwarsa dalam beberapa menit, jadi token tidak boleh datang dari cache. Prefect 3 mematikan caching kecuali persistensi hasil dinyalakan, yang berarti kebanyakan orang aman secara kebetulan. Jika tim Anda menyalakan persistensi secara global, dan banyak yang melakukannya, task yang diulang bisa mengembalikan token yang dihasilkannya pada percobaan pertama dan formulir akan menolaknya. Setel kebijakannya secara eksplisit lalu berhentilah memikirkannya.
# pip install capskip
from prefect import task
from prefect.tasks import exponential_backoff
from prefect.cache_policies import NO_CACHE
from prefect.blocks.system import Secret
from capskip import CapSkip
# NO_CACHE matters: a token is valid once and expires fast.
@task(
retries=3,
retry_delay_seconds=exponential_backoff(backoff_factor=5),
retry_jitter_factor=0.5,
cache_policy=NO_CACHE,
)
def solve_recaptcha(sitekey: str, page_url: str) -> str:
key = Secret.load("capskip-api-key").get()
solver = CapSkip(host="127.0.0.1", port=8080, apiKey=key)
return solver.recaptcha(sitekey=sitekey, url=page_url)["code"]Satu metode mencakup reCAPTCHA v2, Invisible, Enterprise, dan v3. Varian-variannya berupa opsi alih-alih panggilan terpisah: invisible=1, enterprise=1, atau version="v3" dengan sebuah action. Turnstile dan GeeTest punya metode sendiri dan berbentuk sama. Daftar parameter lengkapnya ada di dokumentasi API CapSkip.
Langkah 3: jangan mencoba ulang error yang tidak akan pernah lolos
Retry buta membuang waktu pada kegagalan yang bersifat deterministik. Argumen yang salah bentuk gagal dengan cara yang sama pada setiap percobaan, begitu pula sitekey yang bukan milik halaman itu. Sebuah fungsi kondisi retry menerima state-nya lalu memutuskan, dan mengembalikan False akan mengakhiri task saat itu juga dengan exception aslinya.
# Retry the transient ones. Fail fast on the rest.
from capskip import ValidationException, ApiException
def worth_retrying(task, task_run, state) -> bool:
try:
state.result()
except (ValidationException, ApiException):
return False # bad arguments or a bad sitekey
except Exception:
return True # solver down, or a timeout
return TrueOper sebagai retry_condition_fn pada task. NetworkException berarti CapSkip tidak berjalan atau host-nya salah, dan TimeoutException berarti pemecahan melampaui recaptchaTimeout, yang default-nya 300 detik. Keduanya benar-benar layak dicoba ulang. Kedua exception itu, ditambah ValidationException dan ApiException, semuanya diturunkan dari satu base yang sama, jadi menangkap CapSkipError bisa dipakai jika Anda lebih suka menangani kegagalan di satu tempat.
Contoh lengkap yang berfungsi
Seluruh flow-nya. Ambil halamannya, tarik sitekey darinya, pecahkan, lalu kirimkan token kembali bersama formulir. Setiap langkah adalah satu task, jadi masing-masing punya retry sendiri, log sendiri, dan entri sendiri di graf run.
# pip install prefect capskip httpx
import re
import httpx
from prefect import flow, task
from prefect.tasks import exponential_backoff
from prefect.cache_policies import NO_CACHE
from prefect.blocks.system import Secret
from capskip import CapSkip
PAGE_URL = "https://example.com/page-with-recaptcha"
@task(retries=2, retry_delay_seconds=5)
def read_sitekey(page_url: str) -> str:
html = httpx.get(page_url, timeout=30).text
match = re.search(r'data-sitekey=["\']([^"\']+)', html)
if not match:
raise RuntimeError("No data-sitekey on the page.")
return match.group(1)
@task(
retries=3,
retry_delay_seconds=exponential_backoff(backoff_factor=5),
cache_policy=NO_CACHE,
)
def solve_recaptcha(sitekey: str, page_url: str) -> str:
key = Secret.load("capskip-api-key").get()
solver = CapSkip(host="127.0.0.1", port=8080, apiKey=key)
return solver.recaptcha(sitekey=sitekey, url=page_url)["code"]
@task(retries=2, cache_policy=NO_CACHE)
def submit_form(page_url: str, token: str) -> int:
reply = httpx.post(
page_url,
data={"g-recaptcha-response": token},
timeout=30,
)
return reply.status_code
@flow(name="captcha-protected-submit")
def run():
sitekey = read_sitekey(PAGE_URL)
token = solve_recaptcha(sitekey, PAGE_URL)
return submit_form(PAGE_URL, token)
if __name__ == "__main__":
print(run())Pecahkan tepat sebelum mengirimkan, jangan pernah di langkah terjadwal yang lebih awal. Token yang tersimpan di result store selama sepuluh menit sementara task hulu menyelesaikan pekerjaannya sudah menjadi token mati ketika formulir melihatnya.
Memecahkan satu batch tanpa membanjiri solver
Prefect menjalankan task secara bersamaan secara default melalui thread pool, jadi seratus pemecahan hanyalah satu list comprehension di atas submit tanpa konfigurasi task runner sama sekali. Itu lebih banyak paralelisme daripada yang mungkin Anda inginkan diarahkan ke satu mesin.
Batas konkurensi global adalah pengendalinya. Buat batasnya sekali lewat CLI, lalu tempati satu slot di dalam task, dan run mana pun yang melewati batas akan menunggu alih-alih menumpuk.
# Create the limit once. Six solves in flight at a time. prefect gcl create capskip --limit 6
# The limit is enforced across every flow run, not per flow.
from prefect import flow, task
from prefect.cache_policies import NO_CACHE
from prefect.concurrency.sync import concurrency
from prefect.futures import wait
from capskip import CapSkip
@task(retries=3, cache_policy=NO_CACHE)
def solve_one(sitekey: str, page_url: str) -> str:
with concurrency("capskip", occupy=1):
solver = CapSkip(host="127.0.0.1", port=8080)
return solver.recaptcha(sitekey=sitekey, url=page_url)["code"]
@flow
def solve_many(sitekey: str, urls):
# submit, not map: map would iterate the sitekey string.
futures = [solve_one.submit(sitekey, u) for u in urls]
wait(futures)Batas itu berlaku untuk setiap flow run di dalam workspace, dan itulah persis yang Anda inginkan ketika tiga jadwal mengarah ke solver yang sama. Jika Anda lebih suka melakukan fan out di dalam satu proses, AsyncCapSkip pada SDK Python adalah klien asyncio yang sesungguhnya dan pendekatan itu dibahas di panduan memecahkan CAPTCHA secara paralel.
Menjalankan solver di mesin lain
Worker berpindah-pindah. Process worker di meja Anda menjadi Docker worker di sebuah server, lalu menjadi work pool Kubernetes, dan pada suatu titik flow-nya tidak lagi berada di mesin tempat solver berjalan. Tidak ada yang berubah di kode selain host-nya.
CapSkip punya dua mode koneksi. Local mengikat ke 127.0.0.1 dan hanya melayani perangkat itu. Server mengikat ke alamat jaringan atau IP publik Anda, sehingga worker di sebuah VM, host container, atau work pool Managed memanggil mesin Windows yang sama lewat API. IP publik statis menjaga alamat itu tetap stabil. Perangkat kerasnya tetap milik Anda dan tetap tanpa kuota di kedua mode, jadi biaya satu hari yang sibuk tidak berubah karena modenya.
# Same SDK, same call. Only the host moves. solver = CapSkip(host="10.0.0.12", port=8080, apiKey=key)
Nyalakan validasi kunci begitu solver mendengarkan di alamat jaringan, dan beri setiap worker kunci miliknya sendiri agar satu kunci bisa dicabut tanpa mengusik yang lain. Kedua mode dibahas langkah demi langkah di panduan penyiapan CapSkip.
Kesalahan umum dan artinya
| Apa yang Anda lihat | Penyebab | Perbaiki |
|---|---|---|
| Retry mengembalikan token kedaluwarsa yang sama | Persistensi hasil menyala, jadi task menyimpan outputnya di cache | Setel cache_policy=NO_CACHE pada task pemecahan |
| Formulir menolak token yang tampak baik-baik saja | Dipecahkan beberapa menit sebelum dikirimkan | Pecahkan di langkah tepat sebelum pengiriman |
| NetworkException di work pool Managed | Flow berjalan di infrastruktur Prefect, bukan milik Anda | Alihkan solver ke mode Server, atau pakai Process worker |
| NetworkException di worker Anda sendiri | CapSkip tidak berjalan, atau host-nya salah | Jalankan aplikasinya, atau arahkan host ke alamat server |
| Tiga retry terbuang pada sitekey yang salah | Setiap percobaan gagal dengan cara deterministik yang sama | Tambahkan retry_condition_fn dan gagal cepat pada ApiException |
| Solver kelebihan beban selama satu batch | Task berjalan bersamaan secara default | Tempati satu slot pada batas konkurensi global |
| TimeoutException | Proses pemecahan melampaui recaptchaTimeout | Naikkan di atas nilai default 300 detik |
| ValidationException | Argumen hilang atau salah format | Periksa sitekey dan URL halaman sebelum mengirim |
FAQ
Bisakah flow Prefect Cloud benar-benar memanggil 127.0.0.1?
Ya, pada work pool hibrida, karena kodenya berjalan di worker Anda dan bukan di cloud milik Prefect. Loopback di sana berarti mesin milik worker itu sendiri, jadi jika CapSkip ada di mesin tersebut, panggilannya berhasil. Pengecualiannya adalah work pool Managed, di mana Prefect yang menyediakan komputasinya dan Anda membutuhkan mode Server dengan alamat yang dapat dijangkau.
Haruskah pemecahan menjadi task tersendiri atau bagian dari task yang lebih besar?
Task tersendiri. Langkah inilah yang paling mungkin gagal sesaat, ia pantas mendapat kebijakan retry yang tidak diinginkan langkah lain, dan memisahkannya berarti graf run menunjukkan kepada Anda persis seberapa sering pemecahan menjadi bagian yang lambat. Letakkan berdampingan dengan langkah pengiriman agar tokennya masih segar saat dipakai.
Apa bedanya dengan melakukannya di Airflow?
Terutama bentuk kodenya. Airflow menuntut sebuah operator dan scheduler yang Anda host sendiri, dan konfigurasi retry-nya berada di task instance. Prefect memberi Anda fungsi berdekorator dan worker yang terhubung keluar. Sisi solver-nya identik di keduanya, dan versi Airflow-nya ditulis di panduan CAPTCHA Airflow.
Apakah pemecahan yang lama dihitung ke dalam waktu flow run saya?
Ya, task-nya terblokir selama melakukan polling. Itu tidak masalah di worker Anda sendiri, di mana biayanya hanya waktu jam dinding. Hal itu jadi penting di work pool Managed, tempat komputasi dihitung berdasarkan durasi run dan dibatasi 24 jam. Satu alasan lagi untuk tetap memecahkan CAPTCHA di perangkat keras yang sudah Anda miliki.
Versi singkatnya
Simpan kuncinya di sebuah Secret block, bungkus pemecahannya dalam sebuah task dengan retry dan NO_CACHE, lalu panggil di langkah tepat sebelum pengiriman. Jalankan worker di mesin tempat CapSkip berada dan host-nya tetap 127.0.0.1. Tambahkan batas konkurensi global sebelum Anda melakukan fan out satu batch. Sisi Python dari semua ini dibahas di halaman pemecah CAPTCHA Python. Tiga panggilan yang sama juga ada di Node.js, PHP, dan C#, dan semuanya tercantum di halaman SDK pemecahan CAPTCHA.
Ada satu hal yang layak diketahui sebelum Anda menjadwalkannya setiap jam. CapSkip melakukan bypass captcha di perangkat keras yang sudah Anda miliki, jadi flow yang memecahkan sepuluh ribu sehari memakan biaya yang sama dengan yang memecahkan sepuluh.
