Cara Memecahkan CAPTCHA di Skrip Windmill (Python)

Pemecahan captcha di Windmill kira-kira sesederhana yang bisa dicapai integrasi semacam ini. Windmill membaca impor di bagian atas skrip Anda, mencocokkannya dengan PyPI, lalu menguncinya di sebuah lockfile, sehingga CapSkip SDK hadir tanpa langkah instalasi dan tanpa file requirements. Yang tersisa adalah fungsi main yang menerima sitekey dan mengembalikan token. Bagian yang layak dipikirkan bukan kodenya, melainkan di mesin mana worker berjalan, karena itulah yang menentukan apakah solver tetap di alamat loopback atau harus mendengarkan di jaringan Anda.
Apa yang Anda butuhkan
- Satu instance Windmill, self-hosted atau di cloud mereka, dan sebuah workspace tempat Anda bisa men-deploy skrip.
- CapSkip berjalan di mesin Windows. Anda bisa tetap memakai mode Local jika worker berjalan di mesin yang sama. Jika worker tinggal di dalam container atau di host lain, beralihlah ke mode Server.
- Sitekey dan URL halaman dari situs yang Anda otomatisasi.
- Satu variabel Windmill yang menyimpan key solver, dan satu variabel lingkungan worker yang menyimpan alamatnya.
Mengapa baris impor sudah menjadi seluruh proses instalasi
Windmill mem-parsing impor tingkat atas saat Anda menyimpan skrip, menentukan paket PyPI mana yang dipetakan, lalu menjalankan dependency job yang menulis sebuah lockfile. Lockfile itu melekat pada versi skrip tersebut, jadi deployment yang Anda uji adalah deployment yang berjalan enam bulan kemudian. Tidak ada file requirements yang harus dirawat dan tidak ada yang perlu diinstal manual di worker.
Anda bisa mengunci interpreter-nya di tempat yang sama, lewat sebuah komentar di header skrip. Skrip yang di-deploy tanpa meminta versi tertentu akan berjalan di Python 3.11.
# py312 # pip install capskip - Windmill resolves this import itself # and locks the version when the script is deployed. from capskip import CapSkip
Langkah 1: skrip pemecahan
Skrip Windmill adalah sebuah fungsi main. Argumennya menjadi skema input sekaligus formulir yang dirender Windmill, jadi beri tipe pada argumen itu. Apa pun yang Anda kembalikan menjadi hasil skrip, dan langkah flow di hilir membacanya dari sana.
# py312
# pip install capskip - resolved from this import on save.
import wmill
from capskip import CapSkip
def main(sitekey: str, page_url: str) -> str:
# Host and port come from the worker environment. The key is
# a Windmill variable, so it is stored encrypted and never
# appears in the script body or in the run logs.
solver = CapSkip(
host=wmill.get_variable("u/admin/capskip_host"),
port=8080,
apiKey=wmill.get_variable("u/admin/capskip_key"),
)
result = solver.recaptcha(sitekey=sitekey, url=page_url)
return result["code"] # the token, for the next stepItulah keseluruhan integrasi untuk reCAPTCHA v2. Setiap varian lain memakai metode yang sama dengan satu keyword tambahan: invisible disetel 1, enterprise disetel 1, atau version disetel v3 dengan sebuah nama action. Turnstile dan GeeTest punya metodenya sendiri dengan bentuk yang sama, dan daftar parameter lengkapnya ada di dokumentasi API CapSkip.
Ada dua hal tentang SDK yang layak diketahui sebelum Anda menulis loop polling sendiri. SDK melakukan polling untuk Anda, mulai dari 250 milidetik lalu melambat alih-alih tidur pada interval datar, yang biasanya lebih cepat daripada waktu tunggu yang dipublikasikan API mentah. Dan batas atasnya untuk reCAPTCHA, Turnstile, dan GeeTest adalah 300 detik, ditetapkan oleh recaptchaTimeout. Angka itu penting saat Anda menyetel timeout skrip di Langkah 4.
Langkah 2: simpan key di variabel Windmill
Windmill punya variabel dan secret sebagai fitur kelas satu, dan skrip di atas membacanya secara langsung. Ada cara kedua yang lebih cocok untuk flow: oper variabelnya sebagai argumen langkah memakai sintaks referensi, dan Windmill akan menyelesaikannya saat run dengan izin milik pemanggil.
| Di mana nilainya disimpan | Bagaimana skrip mengambilnya |
|---|---|
| Variabel secret Windmill | Baca di badan skrip dengan klien wmill, seperti di atas |
| Variabel Windmill, dioper sebagai argumen langkah | Beri argumen itu nilai dollar-var diikuti path variabelnya |
| Resource Windmill yang menyimpan beberapa field sekaligus | Beri argumen itu nilai dollar-res diikuti path resource-nya |
| Variabel lingkungan di host worker | Baca dari environment proses, setelah worker diizinkan meneruskannya |
Referensi itu diselesaikan secara rekursif, termasuk di dalam list dan objek bersarang, jadi sebuah langkah yang menerima list berisi key bisa memuat satu referensi di tiap elemennya. Berikan workspace ini key solver-nya sendiri alih-alih memakai satu key bersama untuk semua yang Anda jalankan.
Langkah 3: lokasi worker menentukan mode koneksi
Inilah pertanyaan yang sebenarnya membentuk penyiapannya, dan mudah keliru karena skripnya terlihat sama saja pada kedua kasus. Worker Windmill adalah proses otonom yang menjalankan satu skrip pada satu waktu. Bisa berupa container di sebelah database, proses di sebuah VM, atau proses di desktop Anda sendiri. Apa pun bentuknya, panggilan SDK membuka socket dari worker, jadi solver harus bisa dijangkau dari sana dan bukan dari tempat lain.
CapSkip punya dua mode koneksi persis untuk keperluan ini. Local mengikat ke 127.0.0.1 dan hanya melayani perangkat itu. Server mengikat ke alamat jaringan atau IP publik Anda, sehingga mesin lain, host container, atau platform hosted bisa menjangkau mesin Windows yang sama lewat API. Keduanya disetel di bagian pengaturan koneksi, dan mode Server hanya mengubah di mana solver berjalan. Itu tetap hardware Anda dan tetap tanpa batas pemakaian.
| Di mana worker Anda berjalan | Mode mana, dan nilai host-nya |
|---|---|
| Di mesin Windows yang sama dengan CapSkip | Mode Local. Nilai host tetap 127.0.0.1 |
| Di dalam container atau di mesin lain di jaringan Anda sendiri | Mode Server. Nilai host adalah alamat LAN mesin solver |
| Di cloud Windmill, atau di VM di luar jaringan Anda | Mode Server dengan IP publik statis, ditambah aturan firewall |
Worker Windmill memang bisa berjalan di Windows, dan itulah kasus yang membuat Anda tetap bisa memakai alamat loopback. Satu pengaturan menjadi penting di sana. Isolasi PID namespace aktif secara default di Linux, dan dokumentasi Windmill sendiri menyebutkan agar nilainya disetel menjadi false untuk worker Windows. Setel variabel bernama ENABLE_UNSHARE_PID menjadi false pada worker Windows dan worker itu akan mulai secara normal.
Untuk dua baris lainnya, alamat solver sebaiknya berada di environment worker, bukan di dalam skrip. Windmill tidak menyerahkan setiap variabel host ke sebuah job secara default, jadi sebutkan variabel yang Anda inginkan di variabel bernama WHITELIST_ENVS pada worker, dipisahkan koma. Sebuah worker group juga bisa membawa variabel lingkungan statis dan dinamisnya sendiri, disetel lewat UI, yang menjadi opsi lebih rapi ketika hanya sebagian worker Anda yang berada dekat solver.
Langkah 4: timeout dan retry
Windmill menyediakan field Timeout di pengaturan runtime sebuah skrip, di sebelah batas Cache dan Concurrency. Setel di atas pemecahan Anda yang paling lambat, bukan di bawahnya. Checkbox reCAPTCHA v2 biasanya selesai jauh di bawah satu menit, tetapi halaman tantangan Turnstile dan GeeTest butuh waktu lebih lama, dan SDK akan terus melakukan polling hingga 300 detik sebelum menyerah dengan TimeoutException. Timeout skrip di bawah nilai itu mengubah pemecahan yang lambat menjadi job yang dibunuh tanpa isi yang berguna di log.
Begitu skrip menjadi satu langkah di dalam flow, Anda mendapat lapisan kedua. Langkah flow Windmill melakukan retry dalam dua bentuk, dan bentuk eksponensial cocok untuk solver yang sedang sibuk sebentar.
| Bentuk retry | Apa yang Anda konfigurasi | Kapan memakainya |
|---|---|---|
| Jeda konstan | Jumlah maksimum percobaan dan jeda tetap | Solver yang sesekali restart, di mana jeda datar sudah cukup |
| Backoff eksponensial | Jumlah maksimum percobaan, basis dalam detik, dan pengali | Apa pun yang mungkin benar-benar sibuk, sehingga Anda mundur alih-alih menggempurnya |
Jeda untuk bentuk eksponensial adalah pengali dikali basis yang dipangkatkan nomor percobaan, jadi basis 3 dengan pengali 2 sepanjang lima percobaan menyebar waktu tunggu dari 6 detik hingga 486 detik. Ada juga pengaturan Continue on error yang membuat flow melanjutkan setelah retry habis dan meneruskan error-nya sebagai hasil langkah, dan begitulah cara Anda membangun cabang yang mundur dengan aman alih-alih menggagalkan seluruh run.
Contoh lengkap yang berfungsi
Satu skrip yang memecahkan sekaligus mengirim, sehingga token tidak menganggur menunggu langkah berikutnya. Poin terakhir itu bukan soal gaya. Token reCAPTCHA berlaku sekitar dua menit, dan flow yang memecahkan di satu langkah, menunggu sebuah persetujuan, lalu mengirim di langkah lain adalah cara yang pasti untuk kehilangannya. Ada penjelasan lebih lanjut soal itu di panduan kedaluwarsa token reCAPTCHA.
# py312
# pip install capskip requests - both resolved from these imports.
import os
import requests
import wmill
from capskip import CapSkip
from capskip.exceptions import TimeoutException, NetworkException
SITE = "https://example.com/page-with-recaptcha"
def main(sitekey: str, username: str) -> dict:
# CAPSKIP_HOST is set on the worker and allowed through by
# WHITELIST_ENVS. It falls back to the loopback address so the
# same script still runs on a worker that sits next to CapSkip.
solver = CapSkip(
host=os.environ.get("CAPSKIP_HOST", "127.0.0.1"),
port=8080,
apiKey=wmill.get_variable("u/admin/capskip_key"),
)
try:
result = solver.recaptcha(sitekey=sitekey, url=SITE)
except TimeoutException:
# Let the flow's retry policy decide what happens next.
raise
except NetworkException:
raise RuntimeError("CapSkip is unreachable from this worker")
# Submit immediately. The token is short lived, and the field
# name below is the one the page's own form posts.
posted = requests.post(
SITE,
data={
"username": username,
"g-recaptcha-response": result["code"],
},
timeout=30,
)
return {"status": posted.status_code, "captcha_id": result["captchaId"]}Skrip terakhir itu mengembalikan id captcha alih-alih token, dan itu memang disengaja. Id berguna saat Anda membaca riwayat run Windmill nanti, sedangkan token tidak: token sudah kedaluwarsa saat itu, dan menaruhnya di hasil job yang tersimpan berarti menaruhnya di log Anda.
Memecahkan beberapa sekaligus
Worker Windmill menjalankan satu skrip pada satu waktu, memakai seluruh mesin yang dimilikinya. Jadi konkurensi di sini adalah soal berapa banyak worker yang Anda jalankan, bukan soal apa yang dilakukan skrip Anda. Ada dua cara mendapatkannya, dan keduanya bisa digabung.
- Jalankan lebih banyak worker. Sebuah worker group bisa diskalakan secara terpisah, dan job diambil oleh worker mana pun yang sedang bebas.
- Pecahkan secara batch di dalam satu skrip. SDK Python menyertakan klien asinkron sungguhan, jadi beberapa pemecahan bisa berjalan bersamaan dalam satu job. Itu layak dilakukan ketika satu run membutuhkan sepuluh token alih-alih satu, dan polanya diuraikan di panduan memecahkan CAPTCHA secara paralel dengan Python.
Setel batas concurrency pada skrip jika situs targetnya yang rapuh. CapSkip sendiri tidak berbasis meteran, jadi menjalankan lebih banyak pemecahan tidak menambah biaya, tetapi situs yang Anda otomatisasi bisa saja menyadarinya.
Kesalahan umum dan artinya
| Apa yang Anda lihat | Penyebab | Perbaiki |
|---|---|---|
| NetworkException, connection refused di port 8080 | Worker tidak berada di mesin tempat CapSkip diikat | Beralihlah ke mode Server dan setel variabel host ke alamat solver |
| Variabel lingkungan host terbaca kosong di dalam job | Variabelnya ada di worker tetapi tidak pernah diizinkan lewat | Tambahkan namanya ke WHITELIST_ENVS, atau setel pada worker group |
| ModuleNotFoundError pada impor capskip | Dependency job belum berjalan untuk versi ini | Simpan dan deploy skripnya, lalu periksa apakah dependency job sudah selesai |
| Job dibunuh di tengah proses pemecahan | Timeout skrip lebih pendek daripada waktu pemecahan | Naikkan Timeout di pengaturan runtime skrip, di atas 300 detik |
| TimeoutException dari SDK | Pemecahan memang benar-benar melampaui recaptchaTimeout | Biarkan flow me-retry-nya, dan periksa apakah sitekey dan URL halaman sudah benar |
| ERROR_WRONG_USER_KEY di dalam respons | Variabel Windmill kosong, sehingga key kosong yang terkirim | Periksa path variabelnya, termasuk prefiks workspace |
| Worker di Windows tidak mau start | Isolasi PID namespace belum dimatikan untuk worker ini | Setel ENABLE_UNSHARE_PID menjadi false pada worker itu |
| Token yang valid ditolak oleh situs target | Token kedaluwarsa di antara langkah pemecahan dan langkah pengiriman | Pecahkan dan kirim dalam satu skrip, atau di langkah berdampingan tanpa jeda |
FAQ
Bisakah saya tetap memakai CapSkip di alamat loopback dengan Windmill?
Bisa, jika worker berjalan di mesin Windows yang sama dengan solver. Itulah satu-satunya bentuk deployment tempat mode Local bertahan, dan layak disiapkan secara sengaja: jalankan worker khusus di mesin solver, beri worker itu tag-nya sendiri, lalu arahkan skrip CAPTCHA ke tag tersebut. Bentuk lain apa pun, termasuk cloud Windmill dan container mana pun, membutuhkan mode Server karena worker-nya berada di tempat lain.
Apakah saya butuh file requirements untuk SDK-nya?
Tidak. Windmill membaca impor tingkat atas saat skrip disimpan, mencocokkannya dengan paket PyPI, dan membuat lockfile untuk versi skrip tersebut. Baris impor itulah deklarasi dependensinya. Jika impor gagal saat run, yang perlu diperiksa adalah apakah dependency job selesai, bukan apakah ada file yang hilang.
Haruskah flow melakukan polling hasilnya sendiri?
Tidak untuk pemecahan yang muat di dalam satu job. SDK sudah melakukan polling, dan ia mundur bertahap dari 250 milidetik alih-alih tidur pada interval tetap, jadi loop sleep buatan tangan justru lebih lambat dan lebih banyak kode. Lakukan polling dari flow hanya jika Anda sengaja memisahkan pengiriman dan pengambilan ke dalam dua langkah, dan dalam kasus itu periksa respons pending berdasarkan namanya. Penulisannya CAPCHA_NOT_READY, kurang satu huruf T, dan artinya teruslah menunggu, bukan ada yang salah. Ada ulasan lengkap tentang respons CAPCHA_NOT_READY.
Bagaimana perbandingannya dengan melakukannya di Airflow, Dagster, atau n8n?
Windmill butuh kode paling sedikit di antara keempatnya, karena sebuah skrip hanyalah fungsi biasa dan dependensinya berasal dari baris impor. Airflow menuntut sebuah task di dalam DAG dan punya interval scheduler yang harus dipikirkan, yang dibahas di panduan DAG CAPTCHA Airflow. Dagster membingkai pekerjaan yang sama sebagai sebuah asset, yang dijelaskan di panduan langkah demi langkah Dagster. n8n adalah graf node, bukan runtime kode, jadi panduan n8n dibangun di sekitar node HTTP Request miliknya. Pertanyaan soal koneksi identik di keempatnya.
Versi singkatnya
Impor SDK di bagian atas skrip Windmill dan biarkan dependency job menguncinya. Simpan key solver di sebuah variabel Windmill dan alamatnya di variabel lingkungan worker yang diizinkan lewat oleh WHITELIST_ENVS. Tentukan mode koneksi dengan bertanya di mana worker berjalan, bukan di mana Anda duduk: worker di mesin Windows milik solver sendiri tetap memakai mode Local, dan selain itu semuanya membutuhkan mode Server. Setel timeout skrip di atas 300 detik, tambahkan exponential backoff di langkah flow, dan kirim token di job yang sama dengan yang menghasilkannya.
- SDK Python-nya sendiri dibahas di halaman pemecah CAPTCHA Python.
- Tantangan checkbox dibahas di halaman pemecah reCAPTCHA v2.
- Panggilan yang setara di Node.js, PHP, dan C# tercantum di halaman SDK pemecahan CAPTCHA.
Satu hal yang perlu ditimbang sebelum Anda menjadwalkannya setiap lima menit: CapSkip adalah pemecah captcha lokal yang berjalan di hardware yang sudah Anda miliki, jadi flow yang berjalan terus-menerus dan yang berjalan sesekali biayanya persis sama.
