Bir CAPTCHA sağlayıcısını değiştirmenin en güvenli yolu, eskisini kapatmadan yenisini onun yanında çalıştırmaktır. Paralel çalıştırma (parallel run), aynı CAPTCHA doğrulamalarını hem mevcut servisinize hem de CaptchaAI'ye aynı anda gönderir; böylece çözüm oranını, hızı ve maliyeti pazarlama vaatlerine değil, kendi trafiğinizden toplanan gerçek verilere bakarak değerlendirirsiniz. Bu rehber, paralel testi Python ve Node.js ile nasıl kuracağınızı ve hangi metrikleri karşılaştıracağınızı adım adım gösterir.
Neden Pazarlama Karşılaştırmaları Yeterli Değil?
Bir pazarlama sayfasındaki başarı oranları sizin trafik desenlerinizi yansıtmaz. Sitenizin CAPTCHA'larını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ı ile bot trafiğinizin karışımı.
Aynı reCAPTCHA v2, iki farklı sitede iki farklı davranış gösterebilir. Paralel test bu değişkenleri ortadan kaldırır, çünkü iki sağlayıcı da tam olarak aynı isteği aynı anda ve aynı koşullarda alır — geriye yalnızca sağlayıcı performansının farkı kalır.
Somut bir senaryo düşünün: Türkiye'deki bir e-ticaret otomasyon ekibi, ödeme ve giriş akışlarının QA testlerini her gece çalıştırıyor ve faturasını USD üzerinden ödüyor. TL'nin oynaklığı nedeniyle, thread bazlı ve aylık sabit fiyatlı bir plan — örneğin BASIC ($15/ay, 5 thread) — bütçe öngörülebilirliği açısından cazip. Ama fiyatı taahhüt etmeden önce ekip, kendi CAPTCHA yüküyle CaptchaAI'nin çözüm oranını ve gecikmesini mevcut sağlayıcıyla yan yana görmek istiyor. Paralel test tam olarak bunu sağlar: karar, tahmin değil ölçüm üzerine kurulur.
Hangi Metrikleri Karşılaştırmalısınız?
Koda geçmeden önce neyi ölçeceğinizi netleştirin. İyi bir karşılaştırma, tek bir "başarı oranı" rakamından fazlasını içerir; 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? |
Ortalama süre yanıltıcı olabilir; asıl fark çoğu zaman P95 dilimde ortaya çıkar. Token geçerliliği ise gözden kaçan ama kritik bir metriktir: bir sağlayıcı hızlı yanıt verip geçersiz bir token döndürürse, "başarılı" görünen çözüm üretimde işe yaramaz.
Paralel Test Mimarisi
Paralel test için uygulamanızla sağlayıcılar arasına ince bir yönlendirme katmanı koyarsınız. Kurulum üç katmandan oluşur:
- uygulamanızdan gelen isteği alan bir CAPTCHA yönlendirici,
- her isteği iki sağlayıcıya birden ileten paralel çağrı,
- sonuçları tek yerde toplayan bir metrik toplayıcı.
Akış aşağıdaki gibi görünür:
┌──────────────┐
│ Your App │
└──────┬───────┘
│
┌──────▼───────┐
│ CAPTCHA │
│ Router │
└──┬───────┬───┘
│ │
┌────────▼──┐ ┌──▼────────┐
│ Current │ │ CaptchaAI │
│ Provider │ │ │
└────────┬──┘ └──┬────────┘
│ │
┌──▼───────▼──┐
│ Metrics │
│ Collector │
└─────────────┘
Python ile Paralel Test
Aşağıdaki uygulama üç parçadan oluşur:
- her sağlayıcıyı tek bir arayüzde toplayan bir soyutlama,
- iki sağlayıcıyı eşzamanlı çalıştıran bir test çalıştırıcısı,
- üretimde trafiği kademeli kaydıran bir trafik bölücü.
Parçaları sırasıyla ekleyin; her biri bir öncekinin üzerine kurulur.
Sağlayıcı Soyutlaması
Her iki sağlayıcı da 2Captcha uyumlu in.php / res.php API'sini kullandığından, tek bir CaptchaProvider sınıfı ikisini de yönetebilir. Sınıf görevi gönderir, sonucu sorgular ve geçen süreyi ölçer:
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
)
Paralel 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 rapor üretir. Aynı anda çalıştırmak, ağ koşullarının iki sağlayıcıyı da eşit etkilemesini sağlar:
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']}")
Trafik Bölme ile Kademeli Geçiş
Üretimde riski düşürmek için trafiğin yalnızca bir yüzdesini CaptchaAI'ye yönlendirin; başarısızlık durumunda istek otomatik olarak eski sağlayıcıya döner. Güven arttıkça yüzdeyi kademeli 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="...")
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 sonuçlarını 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);
Aşamalı Geçiş Planı
Tek seferde geçiş yapmak yerine riski aşamalara yayın. Aşağıdaki plan, salt doğrulamadan tam geçişe kadar güvenli bir ilerleme sunar; her aşamada durup metriklere bakın ve yalnızca sayılar iyiyse bir sonraki adıma 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 |
Bir sonraki seviyeye yalnızca metrikler beklentinizi karşılıyorsa geçin; gölge testte başarı oranı veya token geçerliliği düşükse yüzdeyi artırmadan önce hata kodlarını inceleyin.
Sık Karşılaşılan Sorunlar ve Çözümleri
Paralel testte en çok görülen 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
Güvenilir sonuç için kaç çözüm gerekir?
Sağlayıcı başına en az 50 çözüm. İstatistiksel olarak anlamlı bir karşılaştırma için, varyansı hesaba katmak amacıyla günün farklı saatlerinde 100'den fazla çözüm çalıştırın. Tek bir yoğun saatte yapılan test, gerçek performansı yanlış temsil edebilir.
Paralel test maliyeti gerçekten ikiye mi katlar?
Evet, test süresince her CAPTCHA iki kez çözülür. Ancak bu yalnızca geçici test dönemi için geçerlidir ve bir sigorta gibi düşünülmelidir — yanlış bir sağlayıcıya tam geçiş yapıp üretimde başarısız olmaktan çok daha ucuzdur. Thread bazlı planlarda çözüm başına ayrı ücret alınmadığı için, maliyet artışı yalnızca eşzamanlı yükle sınırlı kalır.
Testi yerel makinemden mi yoksa üretim ortamından mı çalıştırmalıyım?
Üretim ortamından çalıştırın. Yerel makinenizin ağ gecikmesi ve coğrafi konumu, sağlayıcıların gerçek performansını çarpıtır. Testi canlı sisteminizin bulunduğu sunucudan ya da aynı bölgedeki bir sunucudan yürütün ki ölçtüğünüz süreler üretimdekiyle eşleşsin.
CaptchaAI hangi CAPTCHA türlerini paralel testte destekler?
Paralel testinizi CaptchaAI'nin gerçekten çözdüğü türlerle kurun: reCAPTCHA v2 ve v3 (Enterprise dahil), Cloudflare Turnstile ve Cloudflare doğrulama akışı, GeeTest v3, görüntü/OCR, grid ve BLS. CaptchaFox, Friendly Captcha ve Lemin beta aşamasındadır. hCaptcha ve FunCaptcha (Arkose Labs) desteklenmez; bu türleri test matrisinize koymayın. Birden fazla tür kullanıyorsanız sonuçları gerçek trafik dağılımınıza göre ağırlıklandırın.
İlgili Makaleler
- Otomatik CAPTCHA test pipeline'ı kurma rehberi
- GitHub Actions ile CI/CD CAPTCHA testleri
- Cypress ile uçtan uca CAPTCHA testleri
Sonraki Adım
Riski minimuma indiren bir paralel testle başlayın: CaptchaAI hesabınızı oluşturun ve performansı gerçek verinizle karşılaştırın.
İlgili kılavuzlar: