Việc chuyển đổi nhà cung cấp CAPTCHA mà không kiểm tra là rất rủi ro. Quá trình chạy song song sẽ gửi đồng thời các thử thách CAPTCHA giống nhau cho cả nhà cung cấp hiện tại của bạn và CaptchaAI, cung cấp cho bạn dữ liệu song song về tốc độ giải quyết, tốc độ và chi phí.
Tại sao thử nghiệm song song lại quan trọng
Điểm chuẩn trên trang tiếp thị không phản ánh kiểu lưu lượng truy cập cụ thể của bạn. CAPTCHA của bạn có các đặc điểm độc đáo — khóa trang web, cấu hình proxy, phân bố địa lý. Thử nghiệm song song cho thấy sự khác biệt thực sự về hiệu suất với khối lượng công việc thực tế của bạn.
Kiến trúc
┌──────────────┐
│ Your App │
└──────┬───────┘
│
┌──────▼───────┐
│ CAPTCHA │
│ Router │
└──┬───────┬───┘
│ │
┌────────▼──┐ ┌──▼────────┐
│ Current │ │ CaptchaAI │
│ Provider │ │ │
└────────┬──┘ └──┬────────┘
│ │
┌──▼───────▼──┐
│ Metrics │
│ Collector │
└─────────────┘
Triển khai Python
Trừu tượng hóa nhà cung cấp
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
)
Người chạy song song
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']}")
Chia lưu lượng truy cập
Để sản xuất, hãy định tuyến phần trăm lưu lượng truy cập đến CaptchaAI:
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="...")
Triển khai JavaScript
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);
Kế hoạch kiểm tra được đề xuất
| Giai đoạn | Thời lượng | Phân chia lưu lượng truy cập | Mục tiêu |
|---|---|---|---|
| 1. Xác thực | 1 ngày | 0% trực tiếp, chỉ song song | Xác minh khả năng tương thích API |
| 2. Kiểm tra bóng | 3 ngày | 5% cho CaptchaAI (có dự phòng) | Thu thập số liệu cơ bản |
| 3. Tăng tốc | 1 tuần | 25% -> 50% -> 75% | Giám sát ở mỗi cấp độ |
| 4. Cắt bỏ toàn bộ | — | 100% CaptchaAI | Ngừng hoạt động nhà cung cấp cũ |
Số liệu để so sánh
| Số liệu | Cách đo |
|---|---|
| Tỷ lệ thành công | successful_solves / total_attempts × 100 |
| Thời gian giải quyết trung bình | Thời gian từ khi gửi đến khi nhận được giải pháp |
| P95 giải quyết thời gian | Phân vị thứ 95 của số lần giải |
| Tỷ lệ lỗi theo loại | Đếm riêng từng mã lỗi |
| Chi phí cho mỗi lần giải quyết | Tổng chi tiêu/giải quyết thành công |
| Hiệu lực của mã thông báo | Mã thông báo được trả về có thực sự hoạt động trên trang đích không? |
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| CaptchaAI chậm hơn trong thử nghiệm song song | Sự khác biệt về độ trễ mạng | Kiểm tra từ máy chủ sản xuất, không phải máy cục bộ |
| Tỷ lệ thành công khác nhau | Nhóm bộ giải của nhà cung cấp khác nhau | Chạy 50+ giải quyết ý nghĩa thống kê |
| Các giải pháp hoạt động trên một nhà cung cấp nhưng không hoạt động trên nhà cung cấp khác | Thời gian hết hạn mã thông báo | Sử dụng token ngay sau khi nhận được chúng |
| Thử nghiệm song song tăng gấp đôi chi phí | Giải cùng một CAPTCHA hai lần | Ngân sách cho giai đoạn thử nghiệm; nó rẻ hơn so với việc di chuyển thất bại |
Câu hỏi thường gặp
Tôi cần bao nhiêu lần chạy thử để có dữ liệu đáng tin cậy?
Tối thiểu 50 giải pháp cho mỗi nhà cung cấp. Để có kết quả có ý nghĩa thống kê, hãy chạy hơn 100 phép giải vào các thời điểm khác nhau trong ngày để tính phương sai.
Tôi nên kiểm tra bằng proxy hay proxyless?
Kiểm tra cả hai nếu bạn sử dụng cả hai trong sản xuất. Chất lượng proxy ảnh hưởng đến tỷ lệ giải quyết khác nhau giữa các nhà cung cấp – hãy kiểm tra cấu hình proxy thực tế của bạn.
Điều gì sẽ xảy ra nếu CaptchaAI hoạt động kém hơn trên một loại CAPTCHA?
Chạy thử nghiệm theo từng loại cụ thể. Nhà cung cấp có thể vượt trội ở reCAPTCHA nhưng lại tụt hậu ở hCaptcha. Nếu bạn sử dụng nhiều loại, trọng số sẽ được tính theo phân bổ lưu lượng truy cập thực tế của bạn.
bài viết liên quan
- Xây dựng quy trình thử nghiệm tự động Captchaai
- Github Actions Kiểm tra Captchaai Cicd Captcha
- Cypress Captchaai E2E Kiểm tra Captcha
Các bước tiếp theo
Bắt đầu thử nghiệm song song không có rủi ro –tạo tài khoản CaptchaAI của bạnvà so sánh hiệu suất với dữ liệu thực.
Hướng dẫn liên quan: