Windmill Betiğinde (Python) CAPTCHA Nasıl Çözülür

windmill captcha - How to Solve CAPTCHAs in a Windmill Script (Python)

Bir Windmill captcha çözümü, bu entegrasyonun olabileceği kadar küçüktür. Windmill betiğinizin en üstündeki import satırlarını okur, bunları PyPI’ye karşı çözümler ve bir lockfile içinde sabitler; böylece CapSkip SDK’sı kurulum adımı ve requirements dosyası olmadan gelir. Geriye kalan, bir sitekey alıp size bir token döndüren bir main fonksiyonudur. Üzerinde düşünmeye değer kısım kod değil, worker’ın hangi makinede çalıştığıdır; çünkü çözücünün loopback adresinde mi kalacağını yoksa ağınızı mı dinlemesi gerektiğini bu belirler.

Neye ihtiyacınız var

  • Kendi sunucunuzda ya da onların bulutunda bir Windmill örneği ve betik dağıtabileceğiniz bir workspace.
  • Bir Windows makinesinde çalışan CapSkip. Worker aynı makinede çalışıyorsa Local modu yeterlidir. Worker’lar konteynerlerde ya da başka bir host üzerinde duruyorsa Server moduna geçin.
  • Otomatikleştirdiğiniz sitenin sitekey'i ve sayfa URL'si.
  • Çözücü anahtarını tutan bir Windmill değişkeni ve adresini tutan bir worker ortam değişkeni.

Import satırı neden kurulumun tamamıdır

Windmill, bir betiği kaydettiğinizde en üst düzey import’ları ayrıştırır, hangi PyPI paketlerine karşılık geldiklerini bulur ve bir lockfile yazan bir bağımlılık işi başlatır. O lockfile betiğin sürümüne iliştirilir; böylece test ettiğiniz dağıtım, altı ay sonra çalışan dağıtımdır. Bakımını yapacağınız bir requirements dosyası yoktur ve worker üzerine elle kurulacak hiçbir şey yoktur.

Yorumlayıcıyı da aynı yerde, betik başlığındaki bir yorum satırıyla sabitleyebilirsiniz. Sürüm istemeyen, dağıtılmış bir betik Python 3.11 üzerinde çalışır.

# py312
# pip install capskip - Windmill resolves this import itself
# and locks the version when the script is deployed.
from capskip import CapSkip

Adım 1: çözüm betiği

Bir Windmill betiği bir main fonksiyonudur. Argümanları girdi şemasını ve Windmill’in oluşturduğu formu belirler, bu yüzden onlara tip verin. Döndürdüğünüz her şey betiğin sonucudur ve aşağı akıştaki bir flow adımı onu oradan okur.

# 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 step

reCAPTCHA v2 için entegrasyonun tamamı budur. Diğer her varyant, fazladan bir anahtar kelimeyle aynı yöntemdir: invisible 1 olarak, enterprise 1 olarak ya da version bir action adıyla birlikte v3 olarak ayarlanır. Turnstile ve GeeTest aynı biçime sahip kendi yöntemlerine sahiptir ve tam parametre listesi şurada: CapSkip API dokümantasyonu.

Elle yazılmış bir yoklama döngüsüne uzanmadan önce SDK hakkında bilinmeye değer iki şey var. Yoklamayı sizin yerinize yapar: sabit bir aralıkta uyumak yerine 250 milisaniyeden başlayıp geri çekilir, ki bu genellikle ham API’nin yayımlanmış bekleme sürelerini geride bırakır. Ve reCAPTCHA, Turnstile ile GeeTest için üst sınırı, recaptchaTimeout ile belirlenen 300 saniyedir. Bu sayı, Adım 4’te betik zaman aşımını ayarlarken önem kazanır.

Adım 2: anahtarı bir Windmill değişkeninde tutun

Windmill birinci sınıf değişkenlere ve gizli değerlere sahiptir ve yukarıdaki betik bunlardan birini doğrudan okur. Bunu yapmanın akışlara daha uygun ikinci bir yolu var: değişkeni referans söz dizimini kullanarak adım argümanı olarak geçirin, Windmill de onu çalışma zamanında çağıranın izinleriyle çözümlesin.

Değerin bulunduğu yerBetiğin ona nasıl ulaştığı
Bir Windmill gizli değişkeniYukarıdaki gibi, betik gövdesinde wmill istemcisiyle okuyun
Adım argümanı olarak geçirilen bir Windmill değişkeniArgümanın değeri olarak dolar-var ve ardından değişken yolunu verin
Aynı anda birkaç alan tutan bir Windmill kaynağıArgümanın değeri olarak dolar-res ve ardından kaynak yolunu verin
Worker host’undaki bir ortam değişkeniWorker’ın onu geçirmesine izin verildikten sonra, süreç ortamından okuyun

Bu referanslar, listelerin ve iç içe nesnelerin içi dahil olmak üzere özyinelemeli olarak çözümlenir; böylece bir anahtar listesi alan bir adım her elemanda bir referans tutabilir. Çalıştırdığınız her şeyde tek bir anahtarı paylaşmak yerine bu workspace’e kendi çözücü anahtarını verin.

Adım 3: bağlantı modunu worker’ın nerede çalıştığı belirler

Kurulumu asıl şekillendiren soru budur ve betik her iki durumda da aynı göründüğü için yanlış yapmak kolaydır. Bir Windmill worker’ı, aynı anda tek bir betik çalıştıran otonom bir süreçtir. Veritabanının yanındaki bir konteyner, bir VM üzerindeki bir süreç ya da kendi masaüstünüzdeki bir süreç olabilir. Hangisi olursa olsun, SDK çağrısı worker’dan bir soket açar; dolayısıyla çözücünün oradan erişilebilir olması gerekir, başka hiçbir yerden değil.

CapSkip’in tam olarak bunun için iki bağlantı modu var. Local, 127.0.0.1 adresine bağlanır ve yalnızca o cihaza hizmet verir. Server ise ağ adresinize ya da genel IP’nize bağlanır; böylece başka bir makine, bir konteyner host’u ya da barındırılan bir platform aynı Windows makinesine API üzerinden ulaşabilir. Her ikisi de şurada ayarlanır: bağlantı ayarları, ve Server modu yalnızca çözücünün nerede çalıştığını değiştirir. Donanım hâlâ sizindir ve hâlâ ölçümsüzdür.

Worker’ınızın çalıştığı yerHangi mod ve host değeri
CapSkip ile aynı Windows makinesindeLocal modu. Host değeri 127.0.0.1 olarak kalır
Bir konteynerde ya da kendi ağınızdaki başka bir makinedeServer modu. Host değeri, çözücü makinenin LAN adresidir
Windmill’in bulutunda ya da ağınızın dışındaki bir VM’deStatik genel IP’li Server modu, artı bir güvenlik duvarı kuralı

Windmill worker’ları Windows üzerinde de çalışır ve loopback adresinde kalmanızı sağlayan durum budur. Orada tek bir ayar önem taşır. PID ad alanı yalıtımı Linux’ta varsayılan olarak true’dur ve Windmill’in kendi belgeleri, Windows worker’ları için bunun false yapılmasını söyler. Bir Windows worker’ında ENABLE_UNSHARE_PID adlı değişkeni false yapın, worker normal şekilde başlar.

Diğer iki satır için adres, betikte değil worker ortamında bulunmalıdır. Windmill her host değişkenini varsayılan olarak bir işe geçirmez; bu yüzden istediklerinizi worker üzerindeki WHITELIST_ENVS adlı değişkende virgülle ayırarak adlandırın. Bir worker grubu da arayüzde ayarlanan kendi statik ve dinamik ortam değişkenlerini taşıyabilir; worker’larınızın yalnızca bir kısmı çözücünün yakınında olduğunda daha derli toplu seçenek budur.

Adım 4: zaman aşımı ve yeniden deneme

Windmill, bir betiğin çalışma zamanı ayarlarında, Cache ve Concurrency limitlerinin yanına bir Timeout alanı koyar. Bunu en yavaş çözümünüzün altına değil, üstüne ayarlayın. Bir reCAPTCHA v2 onay kutusu genellikle bir dakikanın epey altında biter, ama Turnstile doğrulama sayfaları ve GeeTest daha uzun sürer ve SDK, bir TimeoutException ile pes etmeden önce 300 saniyeye kadar yoklamayı sürdürür. Bu değerin altındaki bir betik zaman aşımı, yavaş bir çözümü, günlükte işe yarar hiçbir şey bulunmayan sonlandırılmış bir işe çevirir.

Betik bir akıştaki adım hâline geldiğinde ikinci bir katman kazanırsınız. Windmill akış adımları iki biçimde yeniden dener ve üstel olanı, kısa süreliğine meşgul olan bir çözücüye uygundur.

Yeniden deneme biçimiNeyi yapılandırırsınızNe zaman kullanılır
Sabit gecikmeAzami deneme sayısı ve sabit bir gecikmeAra sıra yeniden başlayan, sabit beklemenin sorun olmadığı bir çözücü
Üstel geri çekilmeAzami deneme sayısı, saniye cinsinden bir taban ve bir çarpanGerçekten meşgul olabilecek her şey; böylece üzerine gitmek yerine geri çekilirsiniz

Üstel biçimdeki gecikme, çarpanın, deneme sayısı kuvvetine yükseltilmiş tabanla çarpımıdır; yani 3 tabanı ile 2 çarpanı beş deneme boyunca beklemeleri 6 saniyeden 486 saniyeye kadar yayar. Ayrıca, yeniden denemeler tükendikten sonra akışın devam etmesini sağlayan ve hatayı adım sonucu olarak aktaran bir Continue on error ayarı vardır; başarısız olmak yerine yedeğe düşen bir dal böyle kurulur.

Tam çalışan örnek

Çözen ve gönderen tek bir betik; böylece token bir sonraki adımı bekleyerek ortalıkta durmaz. Bu son nokta üslupla ilgili değil. Bir reCAPTCHA token’ı yaklaşık iki dakika geçerlidir ve bir adımda çözen, sonra bir onay bekleyen, ardından başka bir adımda gönderen bir akış, onu kaybetmenin güvenilir yoludur. Bu konuda daha fazlası şu rehberde: reCAPTCHA token süresinin dolması.

# 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"]}

Son betik, token yerine captcha id’sini döndürüyor ve bu bilinçli bir tercih. Windmill’in çalıştırma geçmişini sonradan okurken id işe yarar, token ise yaramaz: o zamana kadar süresi dolmuştur ve onu saklanan bir iş sonucuna koymak, günlüklerinize koymak demektir.

Aynı anda birkaç tanesini çözme

Bir Windmill worker’ı, sahip olduğu makinenin tamamını kullanarak aynı anda tek bir betik çalıştırır. Yani buradaki eşzamanlılık, betiğinizin ne yaptığıyla değil, kaç worker çalıştırdığınızla ilgilidir. Bunu elde etmenin iki yolu var ve ikisi birleştirilebilir.

  • Daha fazla worker çalıştırın. Bir worker grubu bağımsız olarak ölçeklenebilir ve işler hangi worker boştaysa onun tarafından alınır.
  • Tek bir betiğin içinde toplu çözün. Python SDK’sı gerçek bir asenkron istemciyle gelir; böylece tek bir işte birkaç çözüm aynı anda havada olabilir. Bir koşu bir yerine on token gerektirdiğinde bunu yapmaya değer ve bu kalıp şurada anlatılıyor: Python ile CAPTCHA’ları paralel çözme kılavuzu.

Kırılgan olan taraf hedef siteyse betiğe bir eşzamanlılık sınırı koyun. CapSkip’in kendisi ölçümlü değildir, yani bunlardan daha fazlasını çalıştırmanın hiçbir ek maliyeti yoktur; ama otomatikleştirdiğiniz site bunu pekâlâ fark edebilir.

Sık görülen hatalar ve anlamları

GördüğünüzNedenDüzeltme
NetworkException, 8080 portunda bağlantı reddedildiWorker, CapSkip’in bağlı olduğu makinede değilServer moduna geçin ve host değişkenini çözücünün adresi olarak ayarlayın
Host ortam değişkeni işin içinde boş görünüyorWorker üzerinde var, ama geçmesine hiç izin verilmemişAdını WHITELIST_ENVS’e ekleyin ya da worker grubunda ayarlayın
capskip import’unda ModuleNotFoundErrorBağımlılık işi bu sürüm için henüz çalışmadıBetiği kaydedip dağıtın, sonra bağımlılık işinin bittiğini kontrol edin
İş, bir çözümün ortasında sonlandırılıyorBetik zaman aşımı, çözümün sürdüğü süreden kısaBetiğin çalışma zamanı ayarlarındaki Timeout değerini 300 saniyenin üzerine çıkarın
SDK’dan TimeoutExceptionÇözüm gerçekten recaptchaTimeout süresini aştıAkışın yeniden denemesine izin verin ve sitekey ile sayfa URL’sinin doğru olduğunu kontrol edin
Yanıtta ERROR_WRONG_USER_KEYWindmill değişkeni boş, bu yüzden boş bir anahtar gönderildiWorkspace ön eki dahil, değişken yolunu kontrol edin
Windows üzerindeki bir worker başlamıyorBu worker için PID ad alanı yalıtımı kapatılmamışO worker üzerinde ENABLE_UNSHARE_PID değerini false yapın
Geçerli bir token hedef site tarafından reddediliyorÇözüm adımı ile gönderim adımı arasında süresi dolduTek bir betikte ya da arada bekleme olmayan bitişik adımlarda çözün ve gönderin

FAQ

CapSkip’i Windmill ile birlikte loopback adresinde tutabilir miyim?

Evet, worker çözücüyle aynı Windows makinesinde çalışıyorsa. Local modunun ayakta kaldığı tek dağıtım biçimi budur ve bunu bilinçli olarak kurmaya değer: çözücü makinesinde özel bir worker çalıştırın, ona kendi worker etiketini verin ve CAPTCHA betiklerini o etikete yönlendirin. Windmill’in bulutu ve herhangi bir konteyner dahil diğer her biçim Server modu gerektirir, çünkü worker başka bir yerdedir.

SDK için bir requirements dosyasına ihtiyacım var mı?

Hayır. Windmill, betik kaydedildiğinde en üst düzey import’ları okur, bunları PyPI paketleriyle eşleştirir ve betiğin o sürümü için bir lockfile üretir. Import satırı, bağımlılık bildiriminin ta kendisidir. Import çalışma zamanında başarısız olursa bakılacak şey bir dosyanın eksik olup olmadığı değil, bağımlılık işinin tamamlanıp tamamlanmadığıdır.

Sonucu akışın kendisi mi yoklamalı?

Tek bir işe sığan bir çözüm için hayır. SDK zaten yoklama yapıyor ve sabit bir aralıkta uyumak yerine 250 milisaniyeden geri çekiliyor; dolayısıyla elle kurulmuş bir bekleme adımları döngüsü hem daha yavaş hem daha fazla kod olur. Akıştan yalnızca gönderme ile toplamayı bilinçli olarak iki adıma böldüyseniz yoklayın; o durumda da bekleme yanıtını adına göre sınayın. Adı, bir T eksik olacak şekilde CAPCHA_NOT_READY yazılır ve bir şeylerin ters gittiği değil, beklemeye devam edin anlamına gelir. Şu kaynak da var: CAPCHA_NOT_READY yanıtının tam açıklaması.

Bu, aynı işi Airflow, Dagster ya da n8n’de yapmakla nasıl karşılaştırılır?

Dördü arasında en az kodu Windmill gerektirir, çünkü bir betik düz bir fonksiyondur ve bağımlılıkları import satırından gelir. Airflow ise bir DAG içinde bir task ister ve üzerine düşünülecek bir zamanlayıcı aralığı vardır; bu şurada ele alınıyor: Airflow CAPTCHA DAG rehberi. Dagster aynı işi bir asset olarak çerçeveler; anlatımı şurada: Dagster anlatımı. n8n bir kod çalıştırma ortamı değil, bir düğüm grafiğidir; bu yüzden n8n rehberi onun HTTP Request düğümü etrafında kurulmuştur. Bağlantı sorusu dördünde de aynıdır.

Kısa özet

SDK’yı bir Windmill betiğinin en üstünde import edin ve bağımlılık işinin onu sabitlemesine izin verin. Çözücü anahtarını bir Windmill değişkenine, adresini de WHITELIST_ENVS’in geçmesine izin verdiği bir worker ortam değişkenine koyun. Bağlantı modunu, sizin nerede oturduğunuzu değil, worker’ın nerede çalıştığını sorarak belirleyin: çözücünün kendi Windows makinesindeki bir worker Local modunda kalır, diğer her şey Server modu gerektirir. Betik zaman aşımını 300 saniyenin üzerine ayarlayın, akış adımına üstel geri çekilme ekleyin ve token’ı onu üreten işin içinde gönderin.

Bunu her beş dakikada bir zamanlamadan önce tartmaya değer bir nokta: CapSkip bir yerel captcha çözücü olup zaten sahip olduğunuz donanımda çalışır; dolayısıyla sürekli tetiklenen bir akış ile ara sıra tetiklenen bir akış tam olarak aynı maliyettedir.