Windows’ta FastAPI CAPTCHA Çözücü Servisi Nasıl Çalıştırılır

FastAPI CAPTCHA çözücü servisi çalıştırmak için uygulama başladığında tek bir AsyncCapSkip istemcisi oluşturun. Bir sitekey ve bir sayfa URL’si alan bir POST endpoint’i sunun. Her çözümü asyncio.timeout ve bir Semaphore içine alın ve SDK’nın istisnalarını HTTP durum kodlarına eşleyin. Herhangi bir dildeki scraper’lar, worker’lar ve script’ler bu durumda tek bir iç adresi çağırır ve CapSkip’in portunu ya da anahtarını hiç görmez. CapSkip kendi Windows makinenizde çalışır, dolayısıyla servis çözüm başına bir fatura eklemez. Bu rehber servisi yaklaşık 100 satırda oluşturur ve CapSkip yavaş, kapalı ya da sessiz kaldığında her parçanın ne yaptığını gösterir.
Neye ihtiyacınız var
- reCAPTCHA çözücüsü açık hâlde bir Windows bilgisayarında çalışan CapSkip. İlk başlatmayı kurulum kılavuzu adım adım anlatıyor.
- Python 3.11 veya üzeri, çünkü kod asyncio.timeout kullanır. Aşağıdaki her şey Windows 11 üzerinde Python 3.14.0, capskip 1.3.0, fastapi 0.150.0 ve uvicorn 0.54.0 ile çalıştırıldı.
- Bir PowerShell istemi. Komutlar PowerShell sözdizimini kullanır.
- reCAPTCHA v2 onay kutusu olan bir sayfa. Servis yalnızca sitekey’ine ve adresine ihtiyaç duyar.
- CapSkip için bir adres. Local modunda 127.0.0.1 üzerinden yalnızca o cihaza yanıt verir. Server modunda ise ağ adresinizi veya genel IP’nizi dinler, böylece başka bir makinedeki bir servis onu API üzerinden çağırabilir. İkisi de şurada ayarlanır: bağlantı ayarları.
Bu rehberdeki sayılar, aynı bilgisayardaki sahte (mock) bir CapSkip’ten gelir. FastAPI, uvicorn ve SDK gerçek olanlardır. Mock her çözümü belirli bir süre, genellikle iki saniye bekletir; bu yüzden gerçek çözüm süreleriniz farklı olacaktır. Burada hiçbir şey canlı bir CapSkip’e ya da canlı bir siteye karşı çalıştırılmadı.
CapSkip’in önüne neden bir servis koymalısınız
Tek bir Python script’i CAPTCHA çözüyorsa SDK’yı o script’ten çağırın. Python CAPTCHA çözücü sayfası bunu ele alıyor, paralel çözme rehberi ise tek bir süreçte birçok çözümü ele alıyor. Birkaç çağıran tek bir CapSkip’i paylaştığında bir servis karşılığını verir. O zaman dört iş tek bir yerde toplanır.
- Tek bir son süre. Python SDK varsayılan olarak 300 saniyeye kadar bekler. Düz istemcisi hiçbir HTTP zaman aşımı ayarlamaz. Çağıranların her biri kendi zamanlayıcısını taşımamalı.
- Tek bir sınır. CapSkip aynı anda sabit sayıda çözüm çalıştırır ve fazla işler onun içinde bekler. Kontrol ettiğiniz bir kuyruğu anlamak daha kolaydır.
- Tek bir anahtar. Çağıranlar bir servis anahtarı alır. CapSkip’in adresi ve API anahtarı servisin ayarlarında kalır.
- Herhangi bir dil. Bir C# worker’ı, bir PHP sitesi ve bir PowerShell görevi HTTP ve JSON ile konuşur.
1. Adım: paketleri kurun
Proje başına bir tane tutuyorsanız önce bir klasör ve bir sanal ortam oluşturun. capskip paketi, async istemcinin kullandığı httpx’i de getirir.
pip install capskip fastapi uvicorn
2. Adım: ayarları ortam değişkenleri olarak belirleyin
SDK hiçbir ortam değişkenini kendisi okumaz, bu yüzden servis onları okur ve her değeri ona iletir. Onları servisi başlatacak PowerShell penceresinde ayarlayın. İki yer tutucu anahtarı da değiştirin. YOUR_CAPSKIP_KEY, CapSkip ayarlarındaki API anahtarıdır ve CapSkip’te anahtar doğrulaması kapalıyken herhangi bir metin işe yarar. YOUR_API_KEY, çağıranlarınız için uydurduğunuz bir anahtardır.
# Where CapSkip listens and which key it expects. $env:CAPSKIP_HOST = "127.0.0.1" $env:CAPSKIP_PORT = "8080" $env:CAPSKIP_API_KEY = "YOUR_CAPSKIP_KEY" # Match Max. Threads in the CapSkip settings. $env:CAPSKIP_MAX_THREADS = "10" # The key your callers must send to this service. $env:SOLVER_API_KEY = "YOUR_API_KEY" # Seconds a caller may wait, queue time included. $env:SOLVE_DEADLINE = "120"
Host olarak 127.0.0.1 kullanın. Bu istemciyle reddedilen bir bağlantı 127.0.0.1 üzerinde yaklaşık 2.4 saniye, localhost üzerinde yaklaşık 2.7 saniye sürdü. Düz requests istemcisi daha önceki bir testte localhost üzerinde yaklaşık 4 saniye sürdü.
3. Adım: ayarları okuyun
main.py dosyasını oluşturun. 3. ile 10. adımlardaki bloklar o tek dosyanın ardışık parçalarıdır. Sondaki tam dosya, import satırlarını en üstte birleştirir.
import asyncio
import os
from contextlib import asynccontextmanager
from capskip import AsyncCapSkip
from fastapi import FastAPI
# The SDK reads no env vars, so pass each setting in by hand.
HOST = os.getenv("CAPSKIP_HOST", "127.0.0.1")
PORT = int(os.getenv("CAPSKIP_PORT", "8080"))
KEY = os.getenv("CAPSKIP_API_KEY", "capskip")
MAX_THREADS = int(os.getenv("CAPSKIP_MAX_THREADS", "10"))
DEADLINE = float(os.getenv("SOLVE_DEADLINE", "120"))4. Adım: uygulama başladığında tek bir istemci ve tek bir geçit oluşturun
Bir lifespan fonksiyonu başlangıçta bir kez çalışır. İstemciyi ve bir Semaphore oluşturur ve ikisini de uygulama durumuna (app state) koyar; böylece her istek onları paylaşır. Semaphore’a CapSkip thread’i başına bir slot verin; bu, CapSkip’teki Max. Threads ayarıdır (varsayılanı 10).
AsyncCapSkip, Python’da gerçek bir async istemcidir. httpx üzerine kuruludur ve asyncio.sleep ile yoklama yapar, bu yüzden yoklamalar arasındaki bekleme event loop’u bloke etmez. Çağrıların kendisi biraz CPU harcar; bunu Bilinmesi gereken bir maliyet başlıklı bölüm açıklıyor. Düz CapSkip istemcisi requests’i zaman aşımı olmadan çağırır, bu yüzden sessiz kalan bir CapSkip bir thread’i sonsuza kadar tutar. recaptchaTimeout değerini son süreye ayarlamak, SDK’nın kendi saatini bizimkine eşit tutar. İkisi berabere kaldığında önce asyncio.timeout tetiklenir.
@asynccontextmanager
async def lifespan(app: FastAPI):
# One client and one gate, shared by every request.
app.state.cap = AsyncCapSkip(
apiKey=KEY, host=HOST, port=PORT,
recaptchaTimeout=DEADLINE,
)
app.state.slots = asyncio.Semaphore(MAX_THREADS)
yield
app = FastAPI(lifespan=lifespan)5. Adım: servis anahtarını denetleyin
Bir ağ adresindeki korumasız bir çözüm endpoint’i, porta ulaşabilen herkes için ücretsiz bir çözücüdür. Bu dependency, X-API-Key başlığını, baytların nerede farklı olduğuna bakmadan aynı sürede çalışan secrets.compare_digest ile karşılaştırır. Eksik, boş, yanlış, daha uzun ve ASCII olmayan bir anahtar mock üzerinde 401 aldı. Doğru anahtar 200 aldı.
Üstteki denetim, servisin boş bir anahtarla başlamasını engeller. O olmasa, başlıksız bir istek boş bir dizeye varsayılır ve eşleşirdi. Dependency gövde denetlenmeden önce çalışır, bu yüzden anahtarı olmayan ve gövdesi doğrulamadan geçmeyen bir istek 422 değil 401 alır. Geçerli JSON olmayan bir gövde yine de 422 alır, çünkü FastAPI önce JSON’u çözer. Anahtarı olmayan çağıranlar şemanız hakkında çok az şey öğrenir.
import secrets
from fastapi import Depends, Header, HTTPException
SERVICE_KEY = os.environ["SOLVER_API_KEY"]
if not SERVICE_KEY:
raise SystemExit("SOLVER_API_KEY must not be empty")
def require_key(x_api_key: str = Header(default="")):
good = secrets.compare_digest(
x_api_key.encode(), SERVICE_KEY.encode())
if not good:
raise HTTPException(
401, "missing or wrong X-API-Key",
headers={"WWW-Authenticate": "APIKey"})6. Adım: isteği doğrulayın ve son süreyi belirleyin
İstek modeli boş bir sitekey’i ya da http ile başlamayan bir URL’yi reddeder. FastAPI hatalı bir gövdeye kendi 422 yanıtıyla karşılık verir ve her sorunu listeler.
Son süre önemli kısımdır. asyncio.timeout, slot beklemesini ve çözümü birlikte sarar. Hafif yüklü bir servis toplamda DEADLINE saniye içinde yanıt verir. Zaman aşımı yalnızca çözümü sarsaydı, bir istek saati daha başlamadan dakikalarca kuyrukta bekleyebilirdi. Ağır yük altında yanıt daha geç gelebilir; bunu Bilinmesi gereken bir maliyet başlıklı bölüm açıklıyor.
from fastapi import Request
from pydantic import BaseModel, Field
class SolveRequest(BaseModel):
sitekey: str = Field(min_length=1)
url: str = Field(pattern=r"^https?://")
async def solve_v2(state, sitekey: str, url: str) -> str:
# The deadline also covers the wait for a free slot.
async with asyncio.timeout(DEADLINE):
async with state.slots:
result = await state.cap.recaptcha(sitekey, url)
return result["code"]7. Adım: çözüm endpoint’ini ekleyin
Endpoint üç satırdır. Önce anahtar dependency’si çalışır, sonra çözüm gelir, sonra token JSON olarak geri döner.
@app.post("/solve", dependencies=[Depends(require_key)])
async def solve(body: SolveRequest, request: Request):
token = await solve_v2(request.app.state, body.sitekey, body.url)
return {"token": token}8. Adım: SDK’nın istisnalarını durum kodlarına eşleyin
SDK dört istisna sınıfı fırlatır; hepsi CapSkipError altındadır ve hepsi capskip içinden import edilebilir. Temel sınıftaki tek bir işleyici her birini bir durum koduna ve kısa bir hata adına çevirir. Ayrıntı log’a gider ve çağırana asla gitmez, böylece CapSkip’in mesajları gizli kalır.
import logging
from capskip import ApiException, CapSkipError, NetworkException
from capskip import TimeoutException, ValidationException
from fastapi.responses import JSONResponse
STATUS = {
NetworkException: (503, "capskip_unreachable"),
TimeoutException: (504, "capskip_timeout"),
ValidationException: (422, "bad_parameters"),
ApiException: (502, "capskip_error"),
}@app.exception_handler(CapSkipError)
async def capskip_error(request: Request, exc: CapSkipError):
# Log the detail here, keep it away from the caller.
logging.getLogger("uvicorn.error").warning("%r", exc)
status, code = STATUS.get(type(exc), (502, "capskip_error"))
if "UNSOLVABLE" in str(exc):
status, code = 422, "unsolvable"
return JSONResponse({"error": code}, status)Aşağıdaki her satır mock’a karşı bilerek tetiklendi.
| Durum | Ne oldu | Ne kadar sürdü |
|---|---|---|
| 503 | CapSkip kapalı, bu yüzden bağlantı reddedildi | 2.2 s |
| 503 | CapSkip bağlantıyı kabul etti ve gönderime hiç yanıt vermedi | 5.2 s |
| 422 | CapSkip ERROR_CAPTCHA_UNSOLVABLE bildirdi | ölçülmedi |
| 502 | CapSkip servisin kendi API anahtarını reddetti | ölçülmedi |
| 504 | Sessiz bir CapSkip yoklanırken son süre geçti (tek istek, 10 s son süre) | 10.0 s |
502 bilerek seçildi. Reddedilen bir CapSkip anahtarı çağıranın değil servisin hatasıdır. 5.2 saniyelik satır, async istemcinin miras aldığı httpx’in 5 saniyelik varsayılan zaman aşımıdır. Bir yoklamaya yanıt gelmemesi farklıdır. SDK başarısız olan bir yoklamayı henüz hazır değil olarak değerlendirir ve yeniden dener, bu yüzden onu yalnızca son süre bitirir. Yalnızca bir sitekey ve bir URL ile bir ValidationException oluşamaz. Bunun için, bu servisin hiç göndermediği, v2 çözümünde bir action gibi ek seçenekler gerekir.
9. Adım: son süreyi ayrı yakalayın
asyncio.timeout, Python’un yerleşik TimeoutError sınıfını fırlatır. SDK’nın TimeoutException sınıfı farklı bir sınıftır ve yerleşik olanı yakalamak onu yakalamaz. 8. adımdaki işleyici SDK’nın TimeoutException sınıfını zaten capskip_timeout adlı bir 504’e eşler. Bu ikinci işleyici son süreyi kapsar ve hata adı çağırana hangi saatin bittiğini söyler.
@app.exception_handler(TimeoutError)
async def deadline_hit(request: Request, exc: TimeoutError):
# asyncio.timeout raises the built-in TimeoutError. The SDK's
# TimeoutException is a different class, so it needs its own entry.
return JSONResponse({"error": "deadline_exceeded"}, 504)10. Adım: bir sağlık denetimi ekleyin
CapSkip’te ping çağrısı yoktur, bu yüzden denetim portuna bir TCP bağlantısı açar ve üç saniye sonra vazgeçer. CapSkip kapalıyken yaklaşık 2 saniye sonra 503 yanıtı verdi, mock çalışırken ise hemen 200 verdi. Bir izleme script’ine servisin ayakta olduğunu ama CapSkip’in olmadığını söyler. Bir çözümün çalıştığını kanıtlamaz.
@app.get("/health")
async def health():
# CapSkip has no ping call, so check that its port accepts a
# connection.
try:
_, writer = await asyncio.wait_for(
asyncio.open_connection(HOST, PORT), timeout=3)
except (OSError, TimeoutError):
return JSONResponse({"ok": False}, 503)
writer.close()
return {"ok": True}Windows’ta çalıştırın
uvicorn’u main.py dosyasını içeren klasörden başlatın. 127.0.0.1’e bağlandığında servis yalnızca o bilgisayarda yanıt verir.
python -m uvicorn main:app --host 127.0.0.1 --port 8000
Başka makinelerdeki çağıranlar için host değerini, her ağ adaptörü anlamına gelen 0.0.0.0 olarak değiştirin. Test bilgisayarında bu, bilgisayarın LAN adresinde yanıt verdi ve 127.0.0.1’e bağlama onu reddetti. Önce gerçek bir servis anahtarı ayarlayın ve Windows’un port için hangi güvenlik duvarı kuralını oluşturduğunu kontrol edin. Servis anahtarı düz HTTP üzerinden açık metin olarak gider, bu yüzden bunu yalnızca güvendiğiniz bir ağda kullanın. Güvenlik duvarı istemi burada gözlemlenmedi ve başka bir makineden erişim test edilmedi.
Tek bir worker çalıştırın. workers seçeneği 2 ve her biri için sınır 3 iken mock aynı anda en fazla 6 çözüm gördü. Her worker, kendi Semaphore’u olan ayrı bir süreçtir, bu yüzden sınır katlanır. reload seçeneğini siz düzenleme yaparken kullanmak güvenlidir. Çözümün ortasında dosyaya dokunmak çalışan isteği düşürmedi ve 1.5 saniye sonraki yeni bir istek de başarılı oldu.
Belge sayfaları varsayılan olarak açıktır. Servis bir ağ adresini dinlediğinde şema herkese açık olmasın diye uygulamayı docs_url ve openapi_url None olarak ayarlanmış şekilde oluşturun.
Servisi CapSkip’ten ayrı bir makinede çalıştırın
Servisin aynı bilgisayarda durması gerekmez. CapSkip’i, kontrol ettiğiniz bir Windows makinesinde, tercihen statik bir genel IP ile Server moduna alın. uvicorn’u başlatmadan önce CAPSKIP_HOST ve CAPSKIP_PORT değerlerini o adrese ayarlayın. Üç uyarı geçerlidir. CapSkip’te API key validation’ı açın ve CAPSKIP_API_KEY içinde aynı anahtarı kullanın. SDK düz HTTP ile konuşur, bu yüzden bu anahtar ağda açık metin olarak taşınır. CapSkip ayrıca portu için her adrese izin veren bir kural ekler, bu yüzden ona ulaşmaması gereken her adres için bir engelleme kuralı ekleyin. Kurulum kılavuzunun bağlantı ayarları bölümü iki modu da listeler.
PowerShell’den çağırın
Çağıran sitekey’i, sayfa URL’sini ve servis anahtarını gönderir. Yanıt, token adlı tek alanı olan bir JSON nesnesidir. Token’ı yaklaşık iki dakika içinde kullanın ve yalnızca bir kez kullanın. Sürenin nasıl işlediğini token süre sonu rehberi anlatıyor. Aşağıdaki adres, servisiniz için bir yer tutucudur.
Invoke-RestMethod -Method Post -Uri http://192.168.1.50:8000/solve `
-Headers @{ "X-API-Key" = "YOUR_API_KEY" } `
-ContentType "application/json" `
-Body '{"sitekey":"YOUR_SITEKEY","url":"https://example.com/page-with-recaptcha"}'Sondaki eğik çizgiyle /solve/ adresine yapılan bir POST, 307 yönlendirmesi alır. Windows PowerShell 5.1 üzerindeki Invoke-RestMethod onu izledi ve gövdeyi ile başlığı korudu. curl.exe tercih ediyorsanız JSON’u stdin üzerinden verin. Her zamanki tek tırnaklı JSON argümanı PowerShell 5.1’de başarısız olur, çünkü içteki çift tırnaklar silinir ve servis bir JSON hatasıyla yanıt verdi.
# curl.exe reads the JSON from stdin, so no quote escaping is needed.
$body = '{"sitekey":"YOUR_SITEKEY","url":"https://example.com/page-with-recaptcha"}'
$body | curl.exe -s http://192.168.1.50:8000/solve `
-H "X-API-Key: YOUR_API_KEY" `
-H "Content-Type: application/json" `
--data-binary "@-"Geçidin yaptığı ve yapmadığı şeyler
Mock’un thread sınırı 3 idi ve on istek aynı anda geldi, her biri 2 saniyelik bir çözüm gerektiriyordu. Geçit mock’u aynı anda üç çözümde tuttu ve on isteğin hepsi 200 döndü. Çalışan bir geçit olmadan on işin hepsi CapSkip’e ulaştı ve orada kuyruğa girdi. Mock’ta bunlardan dördü, üç saniyelik bekleme sınırını aştıktan sonra vazgeçti ve çözülemez olarak döndü. Bu yanıt mock’un kendi modelidir ve gerçek bir CapSkip’te doğrulanmadı.
Slot beklemesi, bir nedenle son sürenin içinde yer alır. 5 saniyelik son süre, 1 saniyelik çözüm ve aynı anda on istekle altısı 200, dördü 504 döndü. Bir istek CapSkip’e hiç ulaşmadı ve ulaşan dokuzdan üçü çözümün ortasında terk edildi.
Bir slot, çözümün kendisinden biraz daha uzun tutulur. SDK hemen yoklama yapar, sonra 0.25, 0.5, 1, 2 ve 4 saniye bekler, ardından yoklamalar arasında 5 saniye bekler. Bu, yoklamaları 0, 0.25, 0.75, 1.75, 3.75, 7.75 ve 12.75 saniyeye yerleştirir ve slot, bir yoklama yanıtı okuyana kadar alınmış kalır. Bir yoklamadan hemen sonra ortaya çıkan bir sonuç bir sonrakini bekler. 7.75 saniyedeki yoklama, 8 saniyelik bir çözümü çeyrek saniyeyle ıskalar. Test bilgisayarında bir sonraki bölümde anlatılan yavaş çağrılar o yoklamayı 8 saniyenin ötesine itti, bu yüzden yanıt yaklaşık 8.4 ila 8.9 saniyede okundu. Bu maliyet kaldırıldığında aynı çözüm 12.75 saniyedeki yoklamayı bekledi ve varsayılan 5 pollingInterval değeriyle 12.9 saniye sonra, 1 değeriyle ise 8.9 saniye sonra döndü. Çözümler uzunsa ve kuyruk uzunsa daha düşük bir pollingInterval slot’ları daha erken boşaltır.
İki sınır kalıyor. Bağlantıyı kapatan bir çağıran kendi işleyicisini iptal etmez, bu yüzden sahipsiz kalan çözüm bitene kadar slot’unu tutar. Tek bir slot ve 4 saniyelik bir çözümle sonraki istek toplamda 8.3 saniye sürdü; boş bir slot varken bu 4.7 saniyeydi. Mock da son süre geçtikten sonra terk edilmiş bir işte çalışmaya devam eder ve SDK’da iptal çağrısı yoktur. Bu nedenle slot sayısı birkaç saniyeliğine CapSkip’in gerçek thread’lerinin önüne geçebilir. Bu mock’ta modellenmiştir. Gerçek uygulama test edilmedi.
Bilinmesi gereken bir maliyet
AsyncCapSkip, gönderdiği her istek için, her yoklama dahil, yeni bir httpx istemcisi oluşturur. Test bilgisayarında bu, her seferinde yaklaşık 141 ms bloke eden CPU süresine mal oldu; bunun çoğu, düz bir HTTP adresinin hiç ihtiyaç duymadığı sertifika paketini yüklemekten geliyordu. Aynı anda başlayan on adet 2 saniyelik çözüm yaklaşık 6 saniyede bitti ve yoğunluk sırasında gönderilen /openapi.json isteği en fazla yaklaşık 1.9 saniye bekledi. Aynı on çözüm senkron bir endpoint üzerinden 3.8 saniye sürdü ve en kötü durum birkaç milisaniyeydi, ama bu yol takılı kalan bir çağrıyı sonlandıramaz. Bir thread’in etrafına sarılan bir son süre thread’i takılı bırakır. 41 takılı isteğin ardından servis 41 takılı thread tuttu ve bunlar süreç yeniden başlayana kadar kalır.
Bloke olan döngü, yük altında saatlerin de kaymasına yol açar. 2 saniyelik son süreyle sessiz kalan bir CapSkip’e karşı, aynı anda gelen 12 istek 2.6 ila 2.9 saniyede 8 yanıt ve 8.5 saniyede capskip_timeout adlı 4 yanıt verdi. 41 istekle en yavaşı 13.9 saniye sürdü. Tek bir istek son süresini tutturdu. 8.5 saniyelik yanıtların nedeni izole edilemedi, ama bloke olan döngü bunun bir parçasıdır ve yalnızca SDK da aynı kaymayı gösterdi. Bir avuç çağıran için bu duraksama zararsızdır. Aynı anda süren çözüm sayısıyla büyür, bu yüzden slot sayısını CapSkip’in gerçekten çalıştırabileceği düzeyde tutun.
Tam main.py
Bu, 3. ile 10. adımların, import satırları en üstte toplanmış hâlidir. Onu main.py olarak kaydedin.
import asyncio
import logging
import os
import secrets
from contextlib import asynccontextmanager
from capskip import ApiException, AsyncCapSkip, CapSkipError
from capskip import NetworkException, TimeoutException, ValidationException
from fastapi import Depends, FastAPI, Header, HTTPException, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
# The SDK reads no env vars, so pass each setting in by hand.
HOST = os.getenv("CAPSKIP_HOST", "127.0.0.1")
PORT = int(os.getenv("CAPSKIP_PORT", "8080"))
KEY = os.getenv("CAPSKIP_API_KEY", "capskip")
MAX_THREADS = int(os.getenv("CAPSKIP_MAX_THREADS", "10"))
DEADLINE = float(os.getenv("SOLVE_DEADLINE", "120"))
@asynccontextmanager
async def lifespan(app: FastAPI):
# One client and one gate, shared by every request.
app.state.cap = AsyncCapSkip(
apiKey=KEY, host=HOST, port=PORT,
recaptchaTimeout=DEADLINE,
)
app.state.slots = asyncio.Semaphore(MAX_THREADS)
yield
app = FastAPI(lifespan=lifespan)
SERVICE_KEY = os.environ["SOLVER_API_KEY"]
if not SERVICE_KEY:
raise SystemExit("SOLVER_API_KEY must not be empty")
def require_key(x_api_key: str = Header(default="")):
good = secrets.compare_digest(
x_api_key.encode(), SERVICE_KEY.encode())
if not good:
raise HTTPException(
401, "missing or wrong X-API-Key",
headers={"WWW-Authenticate": "APIKey"})
class SolveRequest(BaseModel):
sitekey: str = Field(min_length=1)
url: str = Field(pattern=r"^https?://")
async def solve_v2(state, sitekey: str, url: str) -> str:
# The deadline also covers the wait for a free slot.
async with asyncio.timeout(DEADLINE):
async with state.slots:
result = await state.cap.recaptcha(sitekey, url)
return result["code"]
@app.post("/solve", dependencies=[Depends(require_key)])
async def solve(body: SolveRequest, request: Request):
token = await solve_v2(request.app.state, body.sitekey, body.url)
return {"token": token}
STATUS = {
NetworkException: (503, "capskip_unreachable"),
TimeoutException: (504, "capskip_timeout"),
ValidationException: (422, "bad_parameters"),
ApiException: (502, "capskip_error"),
}
@app.exception_handler(CapSkipError)
async def capskip_error(request: Request, exc: CapSkipError):
# Log the detail here, keep it away from the caller.
logging.getLogger("uvicorn.error").warning("%r", exc)
status, code = STATUS.get(type(exc), (502, "capskip_error"))
if "UNSOLVABLE" in str(exc):
status, code = 422, "unsolvable"
return JSONResponse({"error": code}, status)
@app.exception_handler(TimeoutError)
async def deadline_hit(request: Request, exc: TimeoutError):
# asyncio.timeout raises the built-in TimeoutError. The SDK's
# TimeoutException is a different class, so it needs its own entry.
return JSONResponse({"error": "deadline_exceeded"}, 504)
@app.get("/health")
async def health():
# CapSkip has no ping call, so check that its port accepts a
# connection.
try:
_, writer = await asyncio.wait_for(
asyncio.open_connection(HOST, PORT), timeout=3)
except (OSError, TimeoutError):
return JSONResponse({"ok": False}, 503)
writer.close()
return {"ok": True}FastAPI CAPTCHA çözücüde sık görülen hatalar
| Gördüğünüz | Neden | Düzeltme |
|---|---|---|
| Servis başlangıçta çıkıyor | SOLVER_API_KEY ayarlı değil ya da boş. Python bu durumda PowerShell penceresinde onun için bir KeyError yazdırır. | Aynı pencerede ayarlayın |
| Her çağrı 401 döndürüyor | X-API-Key başlığı SOLVER_API_KEY değerine eşit değil | CapSkip’in değil, servis anahtarını gönderin |
| deadline_exceeded ile 504 | Slot beklemesi artı çözüm son süreyi aştı | SOLVE_DEADLINE değerini yükseltin ya da CapSkip’teki Max. Threads ile CAPSKIP_MAX_THREADS değerini birlikte yükseltin |
| capskip_timeout ile 504 | SDK’nın yoklama sınırı önce doldu, genellikle yük altında | Aynı anda süren istek sayısını azaltın ya da SOLVE_DEADLINE değerini yükseltin |
| capskip_unreachable ile 503 | CapSkip’in portunda hiçbir şey bağlantı kabul etmiyor | CapSkip’i başlatın ve Local veya Server modunu kontrol edin |
| Sınır beklenenden yüksek | Birden fazla uvicorn worker’ı | Tek bir worker çalıştırın |
| curl.exe kaynaklı json_invalid ile 422 | PowerShell 5.1 tırnakları sildi | JSON’u stdin üzerinden aktarın |
FastAPI’nin kendi lifespan belgeleri başlangıç kancasını ele alır, her CapSkip hata kodu ise şurada listelenir: API belgeleri.
FAQ
Yalnızca tek bir script CAPTCHA çözüyorsa bir servise ihtiyacım var mı?
Hayır. SDK’yı o script’ten çağırın. Servis, birkaç çağıran ya da başka dillerdeki çağıranlar tek bir CapSkip’i paylaştığında ve tek bir son süre, tek bir sınır ve tek bir anahtar istediğinizde işe yarar. Doğrudan çağrıyı reCAPTCHA v2 Python rehberi gösteriyor.
Servisi bir bulut platformunda barındırabilir miyim?
Evet, ama barındırılan bir servis başkasının makinesinde çalışır, bu yüzden bilgisayarınızdaki 127.0.0.1’e ulaşamaz. CapSkip’i kontrol ettiğiniz bir Windows makinesinde, tercihen statik bir genel IP ile Server modunda çalıştırın ve CAPSKIP_HOST değerini o adrese ayarlayın. API key validation’ı açın, diğer her adres için bir engelleme kuralı ekleyin ve anahtarın düz HTTP üzerinden açık metin olarak gittiğini unutmayın. Bu rehber için hiçbir barındırma platformu test edilmedi.
Endpoint bunun yerine düz bir def fonksiyonu olmalı mı?
Bu iş için hayır. Düz bir def endpoint’i bir thread içinde çalışır ve düz CapSkip istemcisinin takılı kalan bir çağrıyı sonlandırmanın bir yolu yoktur. Takılı kalan bir çağrı hiç dönmez, bu yüzden çağıran kendi zaman aşımına kadar bekler. Onu bir son süreyle sarmak zamanında yanıt verir, ama thread takılı kalır. Async istemci temiz biçimde sonlanır.
Aynı endpoint reCAPTCHA v3’ü çözebilir mi?
Yazıldığı hâliyle hayır. Servis v2 yöntemini çağırır. v3 için, SDK’nın reCAPTCHA yönteminin kabul ettiği gibi çağrıya bir version ve bir action ekleyin. Hiçbir score değeri göndermeyin, çünkü CapSkip’te score ayarı yoktur. Bu rehberin kullandığı onay kutusu türünü reCAPTCHA v2 çözücü sayfası ele alıyor.
Kısa özet
FastAPI CAPTCHA çözücü servisi çalıştırmak için tek bir AsyncCapSkip, tek bir Semaphore ve bekleme ile çözümün etrafında tek bir asyncio.timeout tutun. Önce servis anahtarını denetleyin. SDK’nın TimeoutException sınıfını yerleşik TimeoutError sınıfından ayrı yakalayın. Tek bir worker çalıştırın. Server modunda çalışıyorsa CapSkip için bir engelleme kuralı ekleyin.
- Python paketinin desteklediği tüm CAPTCHA türleri: Python CAPTCHA çözücü sayfası.
- Onay kutulu çözücünün nasıl çalıştığı: reCAPTCHA v2 çözücü sayfası.
- Tek bir script’te birçok çözüm: Python’da paralel CAPTCHA çözme.
Paylaşılan bir servis, kimse çözümleri saymadan bütün bir ekibe hizmet verebilir. CapSkip, kendi Windows makinenizde captcha atlatma işini yapar, bu yüzden servisin arkasındaki her çözüm çağrı başına ücretten uzak kalır.
