Locust’ta İstatistiklerinizi Bozmadan CAPTCHA Nasıl Çözülür

Locust’ta bir captcha çözümünün iki yerden uzak durması gerekir: istatistikleriniz ve her yinelemede çalışan yol. Locust, self.client üzerinden yapılan her isteği raporlar; bu nedenle çözümü onun üzerinden yaparsanız çözücünün yanıt süreleri, okumaya çalıştığınız raporun içine düşer. Bir task içine yerleştirilen çözüm ise her kullanıcının her yinelemesinde çalışır. Sınırların nerede olduğunu bildikten sonra ikisinden de kaçınmak zor değildir.
Neye ihtiyacınız var
- Python 3.10 veya daha yenisi üzerinde Locust 2.x; CapSkip istemcisinin ihtiyaç duyduğu sürüm de budur.
- Bir Windows makinesinde çalışan CapSkip ve locustfile dosyanızın yanına kurulmuş Python istemcisi.
- Korumalı formun sitekey ve sayfa URL’si; her kullanıcının yeniden bulması yerine dışarıdan verilir.
- Locust, çözücünün kendi makinesi dışında herhangi bir yerde çalışıyorsa Server modu; buna her container ve her worker makinesi dahildir. Bu, bağlantı ayarları altındaki tek bir ayardır.
# pip install capskip pip install -U locust capskip
Adım 1: çözümü self.client üzerinden geçirmeyin
Bir raporu sessizce bozan kısım burasıdır. Locust dokümantasyonu self.client’ın ne olduğu konusunda açıktır: requests.Session sınıfının bir alt sınıfı ve sarmalayıcısı olan bir HttpSession örneğidir ve eklediği şey, istek sonuçlarının Locust’a raporlanmasıdır. Başarı ve başarısızlık, yanıt süresi, yanıt uzunluğu, ad. Onun üzerinden gönderilen her şey istatistik tablosuna düşer.
Bir çözüm saniyeler sürer. Uygulamanızın uç noktaları milisaniyeler sürer. İkisini aynı tabloya koyarsanız doksan beşinci yüzdeliğiniz, bir CAPTCHA’nın ne kadar sürdüğünün ölçüsü haline gelir; bu da kimsenin istemediği bir sayıdır.
İyi haber şu ki çözüm, hiçbir şey yapmamaktır. CapSkip istemcisinin kendi HTTP taşıma katmanı vardır ve self.client’a hiç dokunmaz; bu yüzden bir çözüm, varsayılan olarak Locust istatistiklerinde görünmez. Locust yalnızca kendi oturumundan geçenleri kaydeder.
# pip install capskip
import os
from capskip import CapSkip
# CAPSKIP_HOST is the solver machine. 127.0.0.1 only works when
# Locust and CapSkip run on the same Windows box.
solver = CapSkip(
host=os.environ.get("CAPSKIP_HOST", "127.0.0.1"),
port=int(os.environ.get("CAPSKIP_PORT", "8080")),
apiKey=os.environ.get("CAPSKIP_API_KEY", "capskip"),
)
def fresh_token(sitekey, page_url):
result = solver.recaptcha(sitekey=sitekey, url=page_url)
return result["code"] # the token, and Locust never sees itHam uç noktalara karşı kendi kodunuzu yazmayı tercih ederseniz aynı kural geçerlidir: self.client değil, düz bir requests.Session kullanın. requests kütüphanesiyle doğrudan yapılan istekler Locust tarafından kaydedilmez; burada tam olarak istediğiniz de budur. Bu iki uç nokta şurada belgelenmiştir: CapSkip API dokümantasyonu.
Bunun tersini yapmanız gereken tek durum, bilerek çözücünün kendisine yük testi uyguladığınız durumdur. O zaman isteği bir name argümanıyla self.client üzerinden gönderin; böylece her çözüm, sitekey başına bir satır yerine tek bir satır altında gruplanır ve o satırı geri kalanından ayrı okursunuz.
Adım 2: çözümünüz gerçekte ne sıklıkta çalışıyor?
Locust size onu koyabileceğiniz dört yer sunar ve bunlar birbirinden kat kat farklıdır. Birini seçmeden önce çarpanı hesaplayın.
| Çözümün bulunduğu yer | Kaç kez çalışır |
|---|---|
| Bir task fonksiyonunun içinde | Kullanıcı başına her yinelemede bir kez. Dakikada binlerce |
| on_start metodunda | Simüle edilen kullanıcı başına bir kez. Beş yüz kullanıcı, beş yüz çözüm demektir |
| Bir test_start dinleyicisinde | Node başına bir kez; bu, çalıştırma başına bir kez demek değildir. Adım 3’e bakın |
| Master ile sınırlandırılmış bir test_start dinleyicisinde | Çalıştırma başına bir kez, sonra da paylaştırılması gerekir |
Neredeyse herkes için varsayılan yanıt on_start’tır. Bir kullanıcı çalışmaya başladığında on_start’ı çağırır; böylece simüle edilen her kullanıcı kendi token’ını alır, onu kendi oturumu boyunca tutar ve greenlet’ler arasında hiçbir şeyin aktarılması gerekmez. Gerçek bir kullanıcının yaptığına uyan biçim de budur.
from locust import HttpUser, task, between
class Signup(HttpUser):
wait_time = between(1, 3)
def on_start(self):
# One solve per simulated user, off the statistics.
self.token = fresh_token(SITEKEY, PAGE_URL)
@task
def submit(self):
self.client.post("/signup", data={
"email": "[email protected]",
"g-recaptcha-response": self.token,
})Beş yüz çözüm pahalı geliyor, çünkü sayaçlı bir serviste gerçekten pahalıdır. Çoğu yük testi rehberinin bir kez çözüp sonucu paylaşmak için kendini paralamasının nedeni budur. Kendi donanımınızda çalışan bir çözücüyle bu sayı bir bütçe sorusu olmaktan çıkar ve bir kapasite sorusuna dönüşür; bu da yanıtlanması çok daha kolay bir sorudur: rampayı çalıştırın ve makineyi izleyin.
Adım 3: test_start her node’da tetiklenir, çalıştırma başına bir kez değil
İnsanları şaşırtan Locust ayrıntısı budur ve kendini, bir şeyin tam katı olan bir çözüm sayısı olarak gösterir. Dokümantasyon, yeni bir yük testi başlatıldığında test_start’ın her node’da tetiklendiğini söyler. Dört worker süreciyle çalıştırın, beş node’unuz olur; yani düz bir test_start dinleyicisindeki çözüm beş kez çalışır.
Runner türünü kontrol ederek onu sınırlandırın. Locust tam da bunun için MasterRunner ve WorkerRunner sınıflarını sunar ve kendi dağıtık rehberindeki kalıp, hangisinin üzerinde olduğunuzu sınamaktır.
from locust import events
from locust.runners import WorkerRunner
@events.init.add_listener
def on_init(environment, **kwargs):
# Workers listen. Registering here runs before the test starts.
if isinstance(environment.runner, WorkerRunner):
environment.runner.register_message("captcha_token", take_token)
@events.test_start.add_listener
def on_test_start(environment, **kwargs):
# The master solves once and broadcasts. Workers skip this.
if not isinstance(environment.runner, WorkerRunner):
environment.runner.send_message(
"captcha_token", fresh_token(SITEKEY, PAGE_URL)
)
def take_token(environment, msg, **kwargs):
environment.shared_token = msg.dataO blokla ilgili iki nokta. Handler imzası sabittir: environment, message ve keyword argümanlarını alır ve veri msg.data olarak gelir. Bir handler uzun sürecekse, concurrent değeri True olacak şekilde kaydedin; böylece Locust’un heartbeat ve diğer sistem mesajlarını bloke etmek yerine kendi greenlet’inde çalışır. Yalnızca bir dize saklayan handler’ın buna ihtiyacı yoktur. İçinde çözüm yapan handler’ın vardır.
Bunların hiçbirini kurmadan önce bir sonraki bölümü okuyun, çünkü çalıştırma başına tek çözüm zaten genellikle yanlış bir hedeftir.
Adım 4: bir token uzun bir testi atlatamaz
Bir reCAPTCHA token’ı yaklaşık iki dakika geçerlidir. Bir yük testi ise genellikle iki dakikadan uzundur. Yani derli toplu mimari, yani test başında bir kez çözüp her worker’a yayınlamak, ilk birkaç dakikanın geçtiği ve sonrasındaki her şeyin süresi dolmuş bir token yüzünden başarısız olduğu bir çalıştırma üretir; üstelik başarısızlıklar uygulamanıza yazılır.
Bu başarısızlığı görür görmez tanımakta fayda var; kuyruk işlerini zincirleyenleri de aynı şey yakalıyor. Konu şu rehberde adım adım ele alınıyor: reCAPTCHA token süresinin dolması.
Bu yüzden paylaşılan token kalıbını yalnızca test kısaysa ya da token her yinelemede değil, rampa sırasında bir kez gerekiyorsa kullanın. Aksi halde on_start içinde kullanıcı başına çözüm yapın; saatlerce süren bir dayanıklılık testi içinse kullanıcının içinde belirli aralıklarla token’ı yenileyin.
import time
class Signup(HttpUser):
wait_time = between(1, 3)
def on_start(self):
self.token = fresh_token(SITEKEY, PAGE_URL)
self.solved_at = time.monotonic()
@task
def submit(self):
# Refresh before the token ages out, not after it fails.
if time.monotonic() - self.solved_at > 90:
self.token = fresh_token(SITEKEY, PAGE_URL)
self.solved_at = time.monotonic()
self.client.post("/signup", data={
"g-recaptcha-response": self.token,
})Yüz yirmi yerine doksan saniye; böylece yenileme, token hâlâ geçerliyken gerçekleşir.
Adım 5: Locust gevent tabanlıdır, bu yüzden senkron istemciyi kullanın
Locust her kullanıcıyı kendi greenlet’i içinde çalıştırır ve gevent kullanarak olay tabanlı çalışır. Dokümantasyonu, testlerinizi geri çağırmalarla değil de normal, bloke eden Python kodu olarak yazmanızı sağlayan şeyin bu olduğunu vurgular. Burada doğal stil bloke etmektir, bu yüzden başvurulacak istemci düz CapSkip istemcisidir.
Bir locustfile içinde AsyncCapSkip’e başvurmayın. Python’da bu, bir takma ad değil gerçek bir async uygulamasıdır; bu da onu bir asyncio programında doğru istemci yapar, ama Locust öyle bir program değildir. Onu bekleyen bir olay döngüsü yoktur ve bir greenlet içinde kullanıcı başına bir döngü başlatmak, gevent’in zaten yaptığı şeyi geri kazanmak için çok fazla mekanizma demektir.
Yoklama davranışı da burada yardımcı olur. İstemci sabit aralıkla yoklama yapmaz. Saniyenin dörtte biriyle başlar ve pollingInterval değerine doğru geri çekilir; böylece hızlı bir çözüm, sabit bir gecikmeyi beklemek yerine hızlıca döner. Birlikte rampalanan beş yüz greenlet boyunca bu fark, rampanızın büyük kısmını oluşturur. Başka bir yerde, bir asyncio programında aynı anda çözülecek bir CAPTCHA yığınınız varsa, o durum şurada ele alınıyor: Python’da paralel CAPTCHA çözme.
Adım 6: çözücüyü worker’ların erişebileceği bir yerde çalıştırmak
Yük üreticileri nadiren başka bir şeyle aynı makinede durur. Yük gerçek olsun diye kendi makinelerini, birkaç makineyi ya da bir container havuzunu alırlar. CapSkip Windows üzerinde çalışır, worker’larınız ise çalışmıyor olabilir.
İki bağlantı modu vardır. Local, 127.0.0.1 adresine bağlanır ve yalnızca o cihaza yanıt verir. Server, ağ adresinize veya genel IP adresinize bağlanır; böylece başka bir makine, bir container sunucusu ya da barındırılan bir runner aynı Windows makinesine API üzerinden ulaşabilir. İkisi de şurada bulunur: bağlantı ayarları. Server modu yalnızca çözücünün hangi adreste dinlediğini değiştirir. Donanım yine sizindir ve çözüm yine sayaçsızdır.
| Locust’un çalıştığı yer | Hangi bağlantı modu |
|---|---|
| CapSkip ile aynı Windows makinesi, tek süreç | Local modu, host 127.0.0.1 olarak kalır |
| Ağınızdaki diğer makinelerde worker süreçleri | Çözücünün LAN adresiyle Server modu |
| Container’lar veya barındırılan bir runner | Statik bir genel IP ve bir güvenlik duvarı kuralı ile Server modu |
Adresi locustfile yerine ortamdan okuyun. Python istemcisi CAPSKIP_HOST, CAPSKIP_PORT ve CAPSKIP_API_KEY değerlerini kendi başına okumaz, bu yüzden yukarıdaki çözücü bunları okuyup istemciye iletir; böylece aynı dosya hem dizüstü bilgisayarınızda hem de bir worker filosunda değişmeden çalışır.
Sık görülen hatalar ve anlamları
| Gördüğünüz | Neden | Düzeltme |
|---|---|---|
| Locust istatistik tablosunda çözücü satırları | Çözüm self.client üzerinden geçti | CapSkip istemcisini ya da düz bir requests.Session kullanın |
| Uygulamanın gerçekte yaptığının çok üzerinde yüzdelikler | Aynı neden. Çözüm süreleri ortalamaya dahil ediliyor | Aynı düzeltme. Başka hiçbir şeyin değişmesi gerekmez |
| Çözüm sayısı, worker sayınızın katı | test_start her node’da tetiklenir | Dinleyiciyi bir WorkerRunner kontrolüyle sınırlandırın |
| Çalıştırmanın birkaç dakika sonrasında her şey başarısız oluyor | Test başında tek bir token çözüldü ve süresi doldu | Kullanıcı başına çözüm yapın ya da task içinde yenileyin |
| Aynı anda her kullanıcıdan NetworkException | CapSkip loopback üzerinde, Locust ise başka yerde | Server moduna geçin ve CAPSKIP_HOST değerini ayarlayın |
| Dağıtık bir çalıştırma sırasında heartbeat uyarıları | Yavaş bir mesaj handler’ı runner’ı bloke ediyor | concurrent değerini True yaparak kaydedin |
| Bir ApiException içinde ERROR_WRONG_USER_KEY | Worker ortamında CAPSKIP_API_KEY tanımlı değil | Worker’larda ayarlayın ve onları yeniden başlatın |
Sonuncusunun kendi rehberi var, çünkü aynı yanıt hem eksik bir anahtarı hem de yalnızca yanlış olan bir anahtarı kapsıyor: ERROR_WRONG_USER_KEY nasıl düzeltilir.
FAQ
Test başına bir kez mi yoksa kullanıcı başına bir kez mi çözmeliyim?
Testin tamamı iki dakikanın altında değilse kullanıcı başına bir kez. Bir token, gerçek bir yük testi bitmeden çok önce geçerliliğini yitirir; bu yüzden paylaşılan token sürümü sessizce hata yolunuzun testine dönüşür. Kullanıcı başına çözüm aynı zamanda daha dürüst bir simülasyondur, çünkü gerçek kullanıcıların her biri kendi token’ını getirir. Bundan kaçınmanın tek nedeni çözüm başına faturalandırmadır ve kendi donanımınızda çalışan bir çözücü bu nedeni ortadan kaldırır.
Çözüm, saniyedeki istek sayıma dahil oluyor mu?
self.client üzerinden geçmediği sürece hayır. Locust istatistiklerini kendi oturumundan oluşturur; bu yüzden CapSkip istemcisiyle veya düz bir requests.Session ile gönderilen her şey rapora görünmez. Bu varsayılan davranıştır ve hiçbir yapılandırma gerektirmez.
Bu, FastHttpUser ile çalışır mı?
Evet ve hiçbir şey değişmez. FastHttpUser, istemciyi daha hızlı bir uygulamayla değiştirir; darboğaz yük üreticisinin kendisi olduğunda bunu yapmaya değer, ama çözüm zaten en başından beri o istemciden geçmiyordu. on_start metodu ve olay dinleyicileri aynı şekilde davranır.
Bunun k6 rehberinden farkı nedir?
Tavsiye neredeyse taban tabana zıt ve bunun iyi bir nedeni var. k6 bir Node paketi kuramaz; bu yüzden o rehber ham HTTP API üzerinden ilerler ve çözümü setup aşamasında bir kez yapar, çünkü orada tekrarlamak gerçekten zahmetlidir. Locust ise Python’dur, istemci normal şekilde kurulur ve kullanıcı başına çözüm hem kolay hem de daha doğrudur. Karşılaştırma şurada: k6 yük testi rehberi.
Kısa özet
Çözümü CapSkip istemcisiyle yapın; böylece istek ne self.client’a ne de istatistiklerinize ulaşır. Çağrıyı on_start içine koyun ki simüle edilen kullanıcı başına bir token olsun; çalıştırma yaklaşık doksan saniyeden uzun sürüyorsa token’ı task içinde yenileyin. Çözümü bir test_start dinleyicisinde yapıyorsanız, o olay her node’da tetiklendiği için dinleyiciyi bir WorkerRunner kontrolüyle sınırlandırın. Locust’un eşzamanlılığı gevent olduğundan senkron istemcide kalın. Yük üreticileri çözücünün kendi makinesinde değilse CapSkip’i Server modunda çalıştırın.
- İstemci arayüzü ve kapsadığı tüm CAPTCHA türleri için bakın: Python CAPTCHA çözücü sayfası.
- Onay kutusu doğrulamasının kendisi şurada anlatılıyor: reCAPTCHA v2 çözücü sayfası.
Rampayı boyutlandırmadan önce netleştirilecek bir nokta var: CapSkip, sınırsız captcha çözücü olarak zaten sahip olduğunuz donanım üzerinde çalışır; bin sanal kullanıcının her birinin kendi doğrulamasını çözmesi, on kullanıcının çözmesiyle tam olarak aynı maliyettedir.
