Tutorials

Tam Geçişten Önce CaptchaAI'yi Test Etme: Paralel Çalıştırma Kılavuzu

Sağlayıcı değiştirme kararını bir fiyat listesine bakarak vermeyin; bir haftalık ölçüme bakarak verin. Paralel çalıştırma (parallel run) tam olarak bunu sağlar: aynı CAPTCHA isteğini aynı saniyede hem mevcut servisinize hem de CaptchaAI'ye gönderir, iki yanıtı da kaydeder ve haftanın sonunda elinizde tahmin değil tablo olur — çözüm oranı, P95 süre, hata kodu dağılımı ve token'ın hedef sayfada gerçekten kabul edilip edilmediği.

Aşağıda sırayla metrik seti, test mimarisi, Python ve Node.js kurulumu ve aşamalı geçiş planı var.

Ölçmeden Önce: Hangi Sayılar Kararı Verir?

Koda geçmeden önce neyi ölçeceğinizi netleştirin. Tek bir "başarı oranı" rakamı yeterli değildir; gecikme dağılımı ve token'ın gerçekte işe yarayıp yaramadığı çoğu zaman daha belirleyicidir.

Metrik Nasıl ölçülür?
Başarı oranı successful_solves / total_attempts × 100
Ortalama çözüm süresi Görevi gönderdiğiniz andan token'ı aldığınız ana kadar geçen süre
P95 çözüm süresi Çözüm sürelerinin 95. yüzdelik dilimi
Hata koduna göre hata oranı Her hata kodunu ayrı ayrı sayın
Çözüm başına maliyet Toplam harcama / başarılı çözüm sayısı
Token geçerliliği Dönen token hedef sitede gerçekten çalışıyor mu?

İki metriğin altını çizmek gerekir. P95, kullanıcılarınızın gerçekten hissettiği süredir: ortalaması 9 saniye olan iki sağlayıcıdan birinin P95'i 14, diğerininki 45 saniye olabilir ve zaman aşımı yaşayan akış ikincisidir. Token geçerliliği ise en çok gözden kaçan metriktir: hızlıca dönen ama hedef sayfanın reddettiği bir token, log'unuzda "başarılı" görünse de üretimde işe yaramaz. Bu yüzden karşılaştırmayı res.php yanıtında değil, token'ı forma gönderdikten sonraki HTTP durum kodunda bitirin.

Neden Pazarlama Sayfasındaki Rakamlar Sizin Rakamlarınız Değil?

Bir pazarlama sayfasında yayımlanan oranlar başka birinin trafiğinden çıkmıştır. Sizin CAPTCHA yükünüzün kendine özgü koşulları vardır:

  • farklı sitekey'ler ve sayfa bağlamları,
  • kendi proxy yapılandırmalarınız,
  • coğrafi dağılım ve yoğunluk saatleri,
  • gerçek kullanıcı trafiğiyle otomasyon trafiğinizin karışımı.

Aynı reCAPTCHA v2, iki farklı sitede iki farklı davranır. Paralel test bu değişkenleri denklemden çıkarır: iki sağlayıcı da aynı isteği, aynı anda, aynı ağdan alır — geriye yalnızca sağlayıcı farkı kalır.

Türkiye pazarından bir senaryo

İstanbul'da çalışan bir e-ticaret otomasyon ekibini düşünün. Ödeme ve giriş akışlarının QA testlerini her gece Europe/Istanbul saatiyle 02.00'de çalıştırıyor, tr-TR locale'iyle üretilmiş test verisi kullanıyor ve CAPTCHA faturasını USD üzerinden ödüyor. TL'nin oynaklığı nedeniyle çözüm başına değil, thread bazında ve aylık sabit fiyatlı bir plan — örneğin BASIC ($15/ay, 5 thread) ya da yük büyüdüğünde ADVANCE ($90/ay, 50 thread) — bütçe planlaması açısından belirgin bir avantaj: gece koşusu iki katına çıksa da fatura satırı değişmez.

Ancak bu avantaj, çözüm oranı düşükse hiçbir şey ifade etmez. Ekip bu yüzden önce iki hafta paralel test yapıyor: iki sağlayıcı aynı gece koşusunda yan yana çalışıyor, sabah raporunda iki sütun karşılaştırılıyor.

Not: QA veya veri kazıma akışlarınız kişisel veri içeriyorsa toplanan veriler KVKK kapsamındadır. Paralel testi yalnızca yetkiniz olan sistemler ve kendi staging ortamlarınız üzerinde kurgulayın.

Test Mimarisi: Üç Katman

Uygulamanızla sağlayıcılar arasına ince bir yönlendirme katmanı koyarsınız. Kurulum üç parçadan oluşur:

  • uygulamanızdan gelen isteği karşılayan bir CAPTCHA yönlendirici,
  • her isteği iki sağlayıcıya birden ileten paralel çağrı,
  • iki yanıtı tek yerde biriktiren bir metrik toplayıcı.

Akış şu şekildedir:

                    ┌──────────────┐
                    │ Your App     │
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │ CAPTCHA      │
                    │ Router       │
                    └──┬───────┬───┘
                       │       │
              ┌────────▼──┐ ┌──▼────────┐
              │ Current   │ │ CaptchaAI │
              │ Provider  │ │           │
              └────────┬──┘ └──┬────────┘
                       │       │
                    ┌──▼───────▼──┐
                    │ Metrics     │
                    │ Collector   │
                    └─────────────┘

Katmanın kritik özelliği geriye dönüşlü olmasıdır: yönlendiriciyi tek bir ortam değişkeniyle eski sağlayıcıya sabitleyebiliyorsanız, aksilik çıktığında geri dönüş saniyeler sürer.

Python ile Paralel Test

Uygulamayı üç parça hâlinde kurun — her sağlayıcıyı tek arayüzde toplayan bir soyutlama, iki sağlayıcıyı eşzamanlı çalıştıran bir test çalıştırıcısı ve üretimde trafiği kademeli kaydıran bir trafik bölücü:

Adım 1: Sağlayıcı soyutlaması

Her iki sağlayıcı da 2Captcha uyumlu in.php / res.php arayüzünü kullandığı için tek bir CaptchaProvider sınıfı ikisini de yönetir: görevi gönderir, sonucu periyodik olarak sorgular ve geçen süreyi ölçer. SolveResult ise süreyi ve hata kodunu birlikte taşır:

import os
import time
import requests
from dataclasses import dataclass, field
from typing import Optional
from concurrent.futures import ThreadPoolExecutor


@dataclass
class SolveResult:
    provider: str
    success: bool
    solution: Optional[str] = None
    error: Optional[str] = None
    elapsed: float = 0.0
    cost: float = 0.0


class CaptchaProvider:
    def __init__(self, name, submit_url, result_url, api_key):
        self.name = name
        self.submit_url = submit_url
        self.result_url = result_url
        self.api_key = api_key
        self.session = requests.Session()

    def solve_recaptcha(self, sitekey, pageurl):
        start = time.time()

        resp = self.session.post(self.submit_url, data={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1
        })
        data = resp.json()
        if data.get("status") != 1:
            return SolveResult(
                provider=self.name, success=False,
                error=data.get("request"), elapsed=time.time() - start
            )

        captcha_id = data["request"]

        for _ in range(60):
            time.sleep(5)
            result = self.session.get(self.result_url, params={
                "key": self.api_key, "action": "get",
                "id": captcha_id, "json": 1
            }).json()

            if result.get("status") == 1:
                return SolveResult(
                    provider=self.name, success=True,
                    solution=result["request"], elapsed=time.time() - start
                )
            if result.get("request") != "CAPCHA_NOT_READY":
                return SolveResult(
                    provider=self.name, success=False,
                    error=result.get("request"), elapsed=time.time() - start
                )

        return SolveResult(
            provider=self.name, success=False,
            error="TIMEOUT", elapsed=time.time() - start
        )

Dikkat edilecek nokta: süre sayacını in.php isteğinden önce başlatın. Yalnızca res.php turlarını ölçerseniz gönderim gecikmesi raporunuzun dışında kalır ve iki sağlayıcı haksız biçimde eşitlenir.

Adım 2: Test çalıştırıcısı

ParallelTestRunner, her turda iki sağlayıcıyı ThreadPoolExecutor ile aynı anda tetikler ve turlar bittiğinde karşılaştırmalı bir özet üretir. Eşzamanlılık burada yöntemin kendisidir: ağdaki anlık bir dalgalanma iki sağlayıcıyı da aynı şekilde etkiler, kalan fark sağlayıcıya yazılabilir.

class ParallelTestRunner:
    def __init__(self, primary, challenger):
        self.primary = primary
        self.challenger = challenger
        self.results = {"primary": [], "challenger": []}

    def run_test(self, sitekey, pageurl, num_runs=20):
        print(f"Running {num_runs} parallel solves...")

        for i in range(num_runs):
            with ThreadPoolExecutor(max_workers=2) as executor:
                primary_future = executor.submit(
                    self.primary.solve_recaptcha, sitekey, pageurl
                )
                challenger_future = executor.submit(
                    self.challenger.solve_recaptcha, sitekey, pageurl
                )

                primary_result = primary_future.result()
                challenger_result = challenger_future.result()

            self.results["primary"].append(primary_result)
            self.results["challenger"].append(challenger_result)

            print(f"  Run {i+1}/{num_runs}: "
                  f"{self.primary.name}={'OK' if primary_result.success else 'FAIL'} "
                  f"({primary_result.elapsed:.1f}s) | "
                  f"{self.challenger.name}={'OK' if challenger_result.success else 'FAIL'} "
                  f"({challenger_result.elapsed:.1f}s)")

        return self.generate_report()

    def generate_report(self):
        report = {}
        for label, results in self.results.items():
            total = len(results)
            successes = sum(1 for r in results if r.success)
            times = [r.elapsed for r in results if r.success]
            errors = [r.error for r in results if not r.success]

            report[label] = {
                "provider": results[0].provider if results else "unknown",
                "total": total,
                "successes": successes,
                "success_rate": (successes / total * 100) if total else 0,
                "avg_time": sum(times) / len(times) if times else 0,
                "min_time": min(times) if times else 0,
                "max_time": max(times) if times else 0,
                "errors": errors
            }

        return report


# Usage
current = CaptchaProvider(
    name="CurrentProvider",
    submit_url="https://current-provider.com/in.php",
    result_url="https://current-provider.com/res.php",
    api_key="current_key"
)

captchaai = CaptchaProvider(
    name="CaptchaAI",
    submit_url="https://ocr.captchaai.com/in.php",
    result_url="https://ocr.captchaai.com/res.php",
    api_key=os.environ["CAPTCHAAI_API_KEY"]
)

runner = ParallelTestRunner(primary=current, challenger=captchaai)
report = runner.run_test(
    sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    pageurl="https://example.com/form",
    num_runs=20
)

for label, stats in report.items():
    print(f"\n{stats['provider']}:")
    print(f"  Success rate: {stats['success_rate']:.1f}%")
    print(f"  Avg time: {stats['avg_time']:.1f}s")
    print(f"  Min/Max: {stats['min_time']:.1f}s / {stats['max_time']:.1f}s")
    if stats['errors']:
        print(f"  Errors: {stats['errors']}")

Çıktıyı ekranda bırakmayın; tur sonuçlarını bir CSV dosyasına yazın. Hata kodlarını ayrı sütunda tutarsanız bir düşüklüğün zaman aşımından mı yoksa geçersiz sitekey'ten mi geldiğini sonradan ayırt edebilirsiniz.

Adım 3: Trafik bölme ile kademeli geçiş

Ölçüm iyi görünse de üretime tek hamlede geçmeyin. Trafiğin küçük bir yüzdesini CaptchaAI'ye yönlendirin; çözüm başarısız olursa istek otomatik olarak eski sağlayıcıya döner ve güven arttıkça yüzdeyi yükseltirsiniz:

import random


class TrafficSplitter:
    def __init__(self, primary, challenger, challenger_pct=10):
        self.primary = primary
        self.challenger = challenger
        self.challenger_pct = challenger_pct

    def solve(self, sitekey, pageurl):
        if random.randint(1, 100) <= self.challenger_pct:
            result = self.challenger.solve_recaptcha(sitekey, pageurl)
            if not result.success:
                # Fall back to primary on failure
                return self.primary.solve_recaptcha(sitekey, pageurl)
            return result
        return self.primary.solve_recaptcha(sitekey, pageurl)


# Start with 10%, increase as confidence builds
splitter = TrafficSplitter(current, captchaai, challenger_pct=10)
result = splitter.solve(sitekey="...", pageurl="...")

Yüzdeyi kodun içine gömmek yerine ortam değişkeninden okuyun; böylece geri dönüş için yeniden yayına alma (deploy) gerekmez.

Node.js ile Paralel Test

Aynı mantığı Node.js tarafında axios ile kurabilirsiniz. Promise.all iki sağlayıcıyı eşzamanlı çalıştırır ve her turun iki sonucunu birlikte toplar:

const axios = require("axios");

class CaptchaProvider {
  constructor(name, submitUrl, resultUrl, apiKey) {
    this.name = name;
    this.submitUrl = submitUrl;
    this.resultUrl = resultUrl;
    this.apiKey = apiKey;
  }

  async solveRecaptcha(sitekey, pageurl) {
    const start = Date.now();
    try {
      const submit = await axios.post(this.submitUrl, null, {
        params: { key: this.apiKey, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
      });
      if (submit.data.status !== 1) {
        return { provider: this.name, success: false, error: submit.data.request, elapsed: (Date.now() - start) / 1000 };
      }

      const captchaId = submit.data.request;
      for (let i = 0; i < 60; i++) {
        await new Promise((r) => setTimeout(r, 5000));
        const poll = await axios.get(this.resultUrl, {
          params: { key: this.apiKey, action: "get", id: captchaId, json: 1 },
        });
        if (poll.data.status === 1) {
          return { provider: this.name, success: true, solution: poll.data.request, elapsed: (Date.now() - start) / 1000 };
        }
        if (poll.data.request !== "CAPCHA_NOT_READY") {
          return { provider: this.name, success: false, error: poll.data.request, elapsed: (Date.now() - start) / 1000 };
        }
      }
      return { provider: this.name, success: false, error: "TIMEOUT", elapsed: (Date.now() - start) / 1000 };
    } catch (err) {
      return { provider: this.name, success: false, error: err.message, elapsed: (Date.now() - start) / 1000 };
    }
  }
}

async function parallelTest(current, captchaai, sitekey, pageurl, runs = 20) {
  const results = { current: [], captchaai: [] };

  for (let i = 0; i < runs; i++) {
    const [currentResult, captchaaiResult] = await Promise.all([
      current.solveRecaptcha(sitekey, pageurl),
      captchaai.solveRecaptcha(sitekey, pageurl),
    ]);

    results.current.push(currentResult);
    results.captchaai.push(captchaaiResult);

    console.log(`Run ${i + 1}/${runs}: ${current.name}=${currentResult.success ? "OK" : "FAIL"} ` +
      `(${currentResult.elapsed.toFixed(1)}s) | ${captchaai.name}=${captchaaiResult.success ? "OK" : "FAIL"} ` +
      `(${captchaaiResult.elapsed.toFixed(1)}s)`);
  }

  for (const [label, data] of Object.entries(results)) {
    const successes = data.filter((r) => r.success).length;
    const times = data.filter((r) => r.success).map((r) => r.elapsed);
    const avgTime = times.length ? times.reduce((a, b) => a + b, 0) / times.length : 0;
    console.log(`\n${label}: ${successes}/${runs} success (${((successes / runs) * 100).toFixed(1)}%), avg ${avgTime.toFixed(1)}s`);
  }
}

// Run
const currentProvider = new CaptchaProvider("CurrentProvider", "https://current-provider.com/in.php", "https://current-provider.com/res.php", "current_key");
const captchaai = new CaptchaProvider("CaptchaAI", "https://ocr.captchaai.com/in.php", "https://ocr.captchaai.com/res.php", process.env.CAPTCHAAI_API_KEY);

parallelTest(currentProvider, captchaai, "SITE_KEY", "https://example.com", 20);

Node.js tarafında sık yapılan hata, turları arka arkaya await ile çalıştırmaktır: ikinci sağlayıcı hep birincisinden sonra ölçülür ve sonuç, sağlayıcı farkını değil sıralamayı yansıtır.

Aşamalı Geçiş Planı

Geçişi tek seferde değil, aşamalara yayarak yapın. Her aşamanın sonunda metriklere bakın; yalnızca sayılar beklentinizi karşılıyorsa bir sonraki satıra geçin.

Aşama Süre Trafik dağılımı Hedef
1. Doğrulama 1 gün %0 canlı, yalnızca paralel API uyumluluğunu doğrulayın
2. Gölge test 3 gün %5 CaptchaAI (geri dönüşlü) Temel ölçümleri toplayın
3. Kademeli artış 1 hafta %25 → %50 → %75 Her seviyede izleyin
4. Tam geçiş — %100 CaptchaAI Eski sağlayıcıyı devre dışı bırakın

Gölge testte başarı oranı veya token geçerliliği düşük çıkarsa, yüzdeyi artırmadan önce hata kodlarını inceleyin. Sorun çoğu zaman sağlayıcıda değil, pageurl veya sitekey değerinin test ile üretim arasında farklı olmasındadır.

Sorun Giderme

Paralel testte en sık karşılaşılan durumlar ve pratik çözümleri:

Sorun Neden Çözüm
Paralel testte CaptchaAI daha yavaş görünüyor Ağ gecikmesi farkları Yerel makineden değil, üretim sunucularından test edin
Sağlayıcılar arasında başarı oranı farkı Çözümleyici havuzları sağlayıcıya göre değişir İstatistiksel anlamlılık için 50'den fazla çözüm çalıştırın
Token bir sağlayıcıda çalışıyor, diğerinde çalışmıyor Token'ın süresi doluyor Token'ı aldıktan hemen sonra kullanın
Paralel test maliyeti ikiye katlıyor Aynı CAPTCHA'yı iki kez çözme Bunu test dönemi bütçesi olarak görün; başarısız bir geçişten ucuzdur

Sık Sorulan Sorular

Paralel test kaç gün sürmeli?

En az bir tam hafta. Tek günlük veri, hafta içi ile hafta sonu farkını ve gece/gündüz yük ayrımını gizler. Sağlayıcı başına 50 çözümün altındaki bir örneklem, iki servis arasındaki farkı istatistiksel olarak ayırt etmeye yetmez.

Ortalama süre yerine neden P95'e bakmalıyım?

Çünkü zaman aşımı yaşayan istekler ortalamanın içinde kaybolur. Ortalaması 9 saniye görünen bir sağlayıcının P95'i 40 saniyeyse, isteklerinizin yirmide biri muhtemelen akışınızın zaman aşımı sınırını aşıyordur. Tabloda ortalamayı ve P95'i yan yana tutun.

CaptchaAI hangi CAPTCHA türlerini destekliyor; test matrisime neleri koyabilirim?

Paralel testinizi CaptchaAI'nin gerçekten çözdüğü türlerle kurun: reCAPTCHA v2 ve v3 (Enterprise varyantları dahil), Cloudflare Turnstile ve Cloudflare doğrulama akışı, GeeTest v3, görüntü/OCR, grid ve BLS. CaptchaFox (beta), Friendly Captcha (beta) ve Lemin (beta) henüz beta aşamasındadır. hCaptcha ile FunCaptcha (Arkose Labs) desteklenmiyor, GeeTest v4 için ise yalnızca "çok yakında" ifadesi geçerli — bu üçünü test matrisinize koymayın.

Test döneminde faturam ne kadar artar?

Test süresince her CAPTCHA iki kez çözüldüğü için sağlayıcı sayısı kadar maliyet oluşur. CaptchaAI tarafında planlar thread bazındadır ve her planda thread başına sınırsız çözüm bulunur; artış çözüm adedine değil, eşzamanlı yüke bağlıdır. Ayrı bir plan almadan önce mevcut thread sayınızın yeterli olup olmadığını ölçün: BASIC ($15/ay, 5 thread) küçük bir gece koşusu için genellikle yeterli olur.

Testi kendi bilgisayarımdan çalıştırabilir miyim?

Çalıştırabilirsiniz ama sonuçlar yanıltıcı olur: yerel makinenizin ağ gecikmesi ve çıkış noktası iki sağlayıcıyı farklı biçimde etkiler. Testi üretim sunucunuzdan ya da aynı bölgedeki bir sunucudan yürütün.

İlgili Makaleler

Sonraki Adım

Karşılaştırmayı bugün başlatın: CaptchaAI hesabınızı oluşturun, yönlendiriciyi kurun ve ilk haftanın verisini toplayın.

İlgili kılavuzlar:

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