Tutorials

CaptchaAI ile Müşteri CAPTCHA Ardışık Düzenleri Oluşturma

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:

  1. Görev alımı — müşteri kazıyıcılarından gelen çözüm isteklerini karşılar.
  2. Kuyruk — görevleri tamponlar ve müşteri başına eşzamanlılık sınırını uygular.
  3. Çözücü worker'lar — görevi CaptchaAI'ye gönderir ve sonucu sorgular.
  4. 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_id ile anahtarlanıyor; müşteriler arası token karışması yok.
  • Müşterilerin max_concurrent toplamı 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.


İlgili rehberler

Bu makale için yorumlar devre dışı bırakılmıştır.