Birden fazla müşteriye kazıma veya otomasyon hizmeti veriyorsanız, işi doğru kurmanın yolu her proje için ayrı CAPTCHA kodu yazmak değil, tüm müşterileri tek bir yeniden kullanılabilir pipeline üzerinden geçirmektir. Bir kuyruk, birkaç çözücü worker ve ortak bir sonuç deposu kurulduğunda yeni müşteri eklemek yalnızca birkaç satır yapılandırma demektir. Bu rehber o mimariyi çalışan Python ve Node.js koduyla kurar.
Bionluk veya Upwork üzerinden iş alan bir ajans için bu yapı iki sorunu birden çözer: her müşterinin eşzamanlılığını ayrı tutmak ve maliyeti öngörülebilir kılmak. CaptchaAI thread bazlı fiyatlandırdığı için aylık gideriniz plana sabitlenir — TL'nin oynaklığında bu ciddi bir avantajdır.
Yeniden kullanılabilir pipeline ne kazandırır?
Tek seferlik çözüm kodu her müşteride kopyalanıp çoğalır ve her biri ayrı ayrı bakım ister. Merkezî bir pipeline ise tüm mantığı tek yerde toplar; yeni müşteri eklemek kod yazmak değil, yapılandırma eklemek olur. Bu yapı üç somut kazanç sağlar:
- Tek bakım noktası: hata yönetimi, yeniden deneme ve sorgulama mantığı tek sınıfta yaşar; bir kez düzelttiğinizde tüm müşteriler için düzelir.
- Müşteri izolasyonu: her müşterinin eşzamanlılığı ve yapılandırması ayrıdır, dolayısıyla birinin yoğunluğu diğerini yavaşlatmaz.
- Öngörülebilir maliyet: tüm müşteriler aynı thread havuzunu paylaştığı için kapasiteyi tek bir planla planlar, sürprizle karşılaşmazsınız.
Pipeline mimarisi: kuyruk, worker ve sonuç deposu
┌──────────────┐ ┌───────────────┐ ┌──────────────┐
│ Client A │──▶ │ │ │ │
│ Client B │──▶ │ Task Queue │──▶ │ CaptchaAI │
│ Client C │──▶ │ │ │ API │
└──────────────┘ └───────────────┘ └──────────────┘
│ │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ Result Store │◀── │ Polling │
│ (Redis/DB) │ │ Workers │
└───────────────┘ └──────────────┘
Bileşenler:
- Görev alımı — müşteri kazıyıcılarından gelen çözüm isteklerini karşılar.
- Kuyruk — görevleri tamponlar ve müşteri başına eşzamanlılık sınırını uygular.
- Çözücü worker'lar — görevi CaptchaAI'ye gönderir ve sonucu sorgular.
- Sonuç deposu — çözülmüş token'ları tüketicinin alması için saklar.
Her görevin method alanı çözücüyü belirler; aynı pipeline'dan reCAPTCHA v2/v3, Cloudflare Turnstile ve Challenge, GeeTest v3, image/OCR, grid ve BLS geçirilebilir. hCaptcha ve FunCaptcha desteklenmez; CaptchaFox, Friendly Captcha ve Lemin yalnızca beta olarak sunulur.
Python ile CAPTCHA pipeline'ı
Çözücü sınıfı
Çekirdek sınıf gönderimi (in.php), sorgulamayı (res.php) ve slot doldurmayı tek yerde toplar.
import requests
import time
from dataclasses import dataclass
from typing import Optional
from collections import deque
from threading import Lock
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
@dataclass
class SolveRequest:
client_id: str
method: str
params: dict
callback: Optional[callable] = None
@dataclass
class SolveResult:
client_id: str
task_id: str
token: Optional[str] = None
error: Optional[str] = None
class CaptchaPipeline:
def __init__(self, api_key: str, max_concurrent: int = 10):
self.api_key = api_key
self.max_concurrent = max_concurrent
self.queue = deque()
self.active = {}
self.lock = Lock()
def enqueue(self, request: SolveRequest):
with self.lock:
self.queue.append(request)
def submit_task(self, request: SolveRequest) -> Optional[str]:
data = {
"key": self.api_key,
"method": request.method,
"json": 1,
**request.params
}
try:
resp = requests.post(SUBMIT_URL, data=data, timeout=15)
result = resp.json()
if result.get("status") == 1:
return result["request"]
else:
print(f"[{request.client_id}] Submit error: {result.get('error_text', result.get('request'))}")
return None
except requests.RequestException as e:
print(f"[{request.client_id}] Network error: {e}")
return None
def poll_result(self, task_id: str, max_wait: int = 120) -> Optional[str]:
elapsed = 0
interval = 5
while elapsed < max_wait:
time.sleep(interval)
elapsed += interval
try:
resp = requests.get(RESULT_URL, params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
}, timeout=10)
result = resp.json()
if result.get("status") == 1:
return result["request"]
elif result.get("request") == "CAPCHA_NOT_READY":
continue
else:
print(f"Poll error for {task_id}: {result.get('error_text', result.get('request'))}")
return None
except requests.RequestException:
continue
return None
def process_queue(self):
while self.queue or self.active:
# Fill active slots
with self.lock:
while self.queue and len(self.active) < self.max_concurrent:
request = self.queue.popleft()
task_id = self.submit_task(request)
if task_id:
self.active[task_id] = request
# Poll active tasks
completed = []
for task_id, request in list(self.active.items()):
token = self.poll_result(task_id, max_wait=10)
if token:
result = SolveResult(
client_id=request.client_id,
task_id=task_id,
token=token
)
if request.callback:
request.callback(result)
completed.append(task_id)
with self.lock:
for task_id in completed:
del self.active[task_id]
Birden fazla müşteriyi tek pipeline'da çalıştırma
Her müşteri kendi client_id ve parametreleriyle kuyruğa girer; callback token hazır olduğunda sonucu ait olduğu müşteriye bağlar.
pipeline = CaptchaPipeline(api_key="YOUR_API_KEY", max_concurrent=15)
# Client A — reCAPTCHA v2
pipeline.enqueue(SolveRequest(
client_id="client_a",
method="userrecaptcha",
params={
"googlekey": "6Le-SITEKEY-A",
"pageurl": "https://client-a-staging.example.com/qa-form"
},
callback=lambda r: print(f"[{r.client_id}] Solved: {r.token[:40]}...")
))
# Client B — Turnstile
pipeline.enqueue(SolveRequest(
client_id="client_b",
method="turnstile",
params={
"sitekey": "0x4AAAA-SITEKEY-B",
"pageurl": "https://client-b-target.com/login"
},
callback=lambda r: print(f"[{r.client_id}] Solved: {r.token[:40]}...")
))
pipeline.process_queue()
process_queue döngüsü aktif slotlar dolana kadar kuyruktan görev çeker; bir slot boşaldıkça sıradaki müşterinin görevi devreye girer. Böylece hiçbir müşteri diğerini aç bırakmadan tüm kapasite sürekli dolu kalır. Uygun thread ve plan eşlemesi için aşağıdaki tabloya bakın.
Node.js pipeline'ı
Node.js'te her enqueue bir Promise döndürür; Promise.allSettled ile tüm sonuçları tek seferde toplarsınız.
const axios = require("axios");
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
class CaptchaPipeline {
constructor(apiKey, maxConcurrent = 10) {
this.apiKey = apiKey;
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.activeCount = 0;
}
enqueue(clientId, method, params) {
return new Promise((resolve, reject) => {
this.queue.push({ clientId, method, params, resolve, reject });
this._processNext();
});
}
async _processNext() {
if (this.activeCount >= this.maxConcurrent || this.queue.length === 0) return;
this.activeCount++;
const task = this.queue.shift();
try {
const token = await this._solve(task);
task.resolve({ clientId: task.clientId, token });
} catch (err) {
task.reject(err);
} finally {
this.activeCount--;
this._processNext();
}
}
async _solve(task) {
const submitResp = await axios.post(SUBMIT_URL, null, {
params: {
key: this.apiKey,
method: task.method,
json: 1,
...task.params,
},
timeout: 15000,
});
if (submitResp.data.status !== 1) {
throw new Error(submitResp.data.error_text || submitResp.data.request);
}
const taskId = submitResp.data.request;
return this._poll(taskId);
}
async _poll(taskId, maxWait = 120000) {
const interval = 5000;
let elapsed = 0;
while (elapsed < maxWait) {
await new Promise((r) => setTimeout(r, interval));
elapsed += interval;
try {
const resp = await axios.get(RESULT_URL, {
params: {
key: this.apiKey,
action: "get",
id: taskId,
json: 1,
},
timeout: 10000,
});
if (resp.data.status === 1) return resp.data.request;
if (resp.data.request !== "CAPCHA_NOT_READY") {
throw new Error(resp.data.error_text || resp.data.request);
}
} catch (err) {
if (err.response) throw err;
}
}
throw new Error(`Timeout waiting for task ${taskId}`);
}
}
// Usage
(async () => {
const pipeline = new CaptchaPipeline("YOUR_API_KEY", 15);
const results = await Promise.allSettled([
pipeline.enqueue("client_a", "userrecaptcha", {
googlekey: "6Le-SITEKEY-A",
pageurl: "https://client-a-staging.example.com/qa-form",
}),
pipeline.enqueue("client_b", "turnstile", {
sitekey: "0x4AAAA-SITEKEY-B",
pageurl: "https://client-b-target.com/login",
}),
]);
results.forEach((r) => {
if (r.status === "fulfilled") {
console.log(`[${r.value.clientId}] Token: ${r.value.token.slice(0, 40)}...`);
} else {
console.error(`Failed: ${r.reason.message}`);
}
});
})();
Plan ve thread seçimi
max_concurrent toplamınızı seçtiğiniz planın thread sayısına göre belirleyin; bir thread tek seferde bir CAPTCHA tutar ve gerçek eşzamanlılık tavanınız aktif thread sayınızdır. Ajans profilinize göre kaba bir başlangıç eşlemesi:
| Plan | Aylık | Thread | Uygun profil |
|---|---|---|---|
| BASIC | $15 | 5 | Tek müşteri, düşük hacim |
| ADVANCE | $90 | 50 | Birkaç müşteriye paralel hizmet |
| PREMIUM | $170 | 100 | Yoğun, çok müşterili ajans |
Fiyatlar USD'dir ve çözüm başına değil thread başına faturalanır; her planda thread başına çözüm sayısı sınırsızdır. Bu yüzden hacminizi değil eşzamanlılığınızı planlamanız yeterli olur.
Müşteri bazlı yapılandırma
Proxy, varsayılan yöntem ve eşzamanlılık sınırı gibi ayarları müşteri başına ayrı tutun; böylece yeni bir müşteri eklemek yalnızca bir sözlük girdisi olur.
CLIENT_CONFIG = {
"client_a": {
"proxy": "host:port:user:pass",
"proxytype": "HTTP",
"max_concurrent": 5,
"default_method": "userrecaptcha"
},
"client_b": {
"proxy": None,
"proxytype": None,
"max_concurrent": 10,
"default_method": "turnstile"
}
}
def build_params(client_id, params):
config = CLIENT_CONFIG.get(client_id, {})
if config.get("proxy"):
params["proxy"] = config["proxy"]
params["proxytype"] = config["proxytype"]
return params
Hata yönetimi stratejisi
Her hata koduna sabit bir yanıt tanımlayın; böylece bir müşterinin sorunu tüm kuyruğa yayılmaz.
| Hata | Yanıt |
|---|---|
ERROR_ZERO_BALANCE |
Kuyruğu durdurun, ilgili tüm müşterileri uyarın |
ERROR_NO_SLOT_AVAILABLE |
Görevi gecikmeyle yeniden kuyruğa alın |
ERROR_WRONG_CAPTCHA_ID |
Görevi atın, hatayı günlüğe yazın |
ERROR_CAPTCHA_UNSOLVABLE |
Bir kez daha deneyin, sonra başarısız sayın |
| Ağ zaman aşımı | Üstel geri çekilme (exponential backoff) ile yeniden deneyin (en fazla 3 kez) |
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| Kuyruk durmadan büyüyor | Aktif slotlar dolu | max_concurrent değerini artırın veya worker ekleyin |
| Callback tetiklenmiyor | Görev sessizce başarısız oldu | Sorgulama döngüsündeki hata dönüşünü kontrol edin |
| Müşteriler arası token karışması | Ortak sonuç deposu | Sonuçları client_id + task_id ile anahtarlayın |
| Hız sınırı hataları (429) | Aynı anda çok fazla gönderim | Eşzamanlılığı düşürün, gönderimler arasına gecikme koyun |
Üretim öncesi kontrol listesi
Pipeline'ı canlıya almadan önce şu maddeleri tek tek doğrulayın:
- Sonuç deposu
client_id+task_idile anahtarlanıyor; müşteriler arası token karışması yok. - Müşterilerin
max_concurrenttoplamı plan thread sayınızın altında. - Zaman aşımı ve yeniden deneme sınırları her worker'da tanımlı.
- Kuyruk kalıcı (Redis veya veritabanı) ve servis yeniden başlatmasına dayanıklı.
- Kuyruğa yalnızca desteklenen CAPTCHA türleri giriyor; hCaptcha veya FunCaptcha görevleri filtreleniyor.
Sık sorulan sorular
max_concurrent değerini plan thread sayıma göre nasıl seçerim?
Müşterilerin max_concurrent toplamı planınızın thread sayısını aşmamalı. ADVANCE ($90/ay, 50 thread) ile beş müşteriye 10'ar görev ayırabilirsiniz; sınırı zorlarsanız ERROR_NO_SLOT_AVAILABLE görürsünüz.
Aynı pipeline birden fazla CAPTCHA türünü aynı anda çözebilir mi?
Evet. method alanı çözücüyü belirlediği için reCAPTCHA, Turnstile, GeeTest v3 ve image/OCR görevleri aynı kuyrukta yan yana ilerler; yalnızca desteklenen türlerle sınırlı kalın.
Müşteri verisi kazırken KVKK açısından neye dikkat etmeliyim?
Topladığınız veri kişisel veri içeriyorsa işleme dayanağınızı ve saklama süresini sözleşmeyle netleştirin; pipeline'ı yalnızca yetkili olduğunuz akışlarla sınırlayın.
Gece boyunca biriken kuyruğu nasıl korurum?
Kuyruğu Redis'te veya bir veritabanında kalıcı tutun. Servis yeniden başladığında bekleyen görevleri geri yükleyip devam edin; böylece hiçbir müşteri isteği kaybolmaz.
CaptchaAI ile müşteri pipeline'ınızı kurun
Tek anahtarla başlayın, ilk müşterinizi CLIENT_CONFIG'e ekleyin ve captchaai.com üzerinden hesabınızı açın.