الدروس التطبيقية

اختبار CaptchaAI قبل الترحيل الكامل: دليل التشغيل الموازي

يعد تبديل موفري CAPTCHA بدون اختبار أمرًا محفوفًا بالمخاطر. يرسل التشغيل المتوازي نفس تحديات CAPTCHA إلى كل من المزود الحالي وCaptchaAI في وقت واحد، مما يوفر لك بيانات جنبًا إلى جنب حول معدل الحل والسرعة والتكلفة.

ملاحظة: تستند الأرقام الواردة في هذه المقالة إلى اختبارات داخلية في ظروف محددة. قد تختلف النتائج حسب نوع CAPTCHA والموقع وبيئة التشغيل. يُنصح بإجراء قياسات مستقلة في بيئتك قبل الاعتماد على هذه الأرقام.

لماذا يهم الاختبار الموازي

لا تعكس المعايير الموجودة على صفحة التسويق أنماط حركة المرور الخاصة بك. تتميز اختبارات CAPTCHA الخاصة بك بخصائص فريدة - مفاتيح الموقع، وتكوينات الوكيل، والتوزيع الجغرافي. يكشف الاختبار الموازي عن اختلافات حقيقية في الأداء مع عبء العمل الفعلي لديك.

الهندسة المعمارية

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

تنفيذ بايثون

تجريد المزود

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
        )

عداء الموازي

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']}")

تقسيم حركة المرور

بالنسبة للإنتاج، قم بتوجيه نسبة من حركة المرور إلى 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="...")

تنفيذ 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);

خطة الاختبار الموصى بها

المرحلة المدة تقسيم حركة المرور هدف
1. التحقق من الصحة يوم واحد 0% مباشر، بالتوازي فقط التحقق من توافق واجهة برمجة التطبيقات
2. اختبار الظل 3 أيام 5% إلى CaptchaAI (مع خيار احتياطي) جمع المقاييس الأساسية
3. تكثيف 1 أسبوع 25% ← 50% ← 75% مراقبة على كل مستوى
4. التحويل الكامل 100% CaptchaAI الاستغناء عن الموفر القديم

مقاييس للمقارنة

متري كيفية القياس
معدل النجاح successful_solves / total_attempts × 100
متوسط وقت الحل الوقت من تاريخ الإرسال إلى استلام الحل
P95 حل الوقت النسبة المئوية 95 من مرات الحل
معدل الخطأ حسب النوع قم بحساب كل رمز خطأ على حدة
التكلفة لكل حل إجمالي الإنفاق / الحلول الناجحة
صلاحية الرمز المميز هل يعمل الرمز المميز الذي تم إرجاعه بالفعل على الموقع المستهدف؟

استكشاف الأخطاء وإصلاحها

المشكلة السبب الإجراء
يُنشأ الرمز المميز لكن الجهة المستهدفة ترفضه مفتاح الموقع أو الصفحة أو سياق الجلسة لا يتطابق التقط المعلمات من جديد وأعد استخدام الرمز داخل جلسة HTTP أو المتصفح نفسها
تنتهي عملية الاستطلاع بمهلة الفاصل الزمني أو وقت الانتظار أو معالجة الأخطاء صارمة أكثر من اللازم استطلع كل 5 إلى 10 ثوانٍ وافصل بين انتهاء المهلة والأخطاء الفعلية وسجّل السبب
ينجح المثال محليًا لكنه يفشل داخل سير العمل رد النداء أو حقل النموذج أو حقن الرمز مفقود في السلسلة الفعلية تحقق من المسار الكامل بين أداة الحل والطلب النهائي إلى الموقع المستهدف

الخطوات التالية

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.