المرجع

الانتقال من AZCaptcha إلى CaptchaAI دون توقف الإنتاج

الانتقال من AZCaptcha إلى CaptchaAI لا يعني إعادة كتابة سكربتاتك من الصفر. تغيّر عنوان النطاق الأساسي ومفتاح الـ API، وتبقى بقية الطلب كما هي؛ فالطرفان يتحدثان لغة API متوافقة مع 2Captcha، ولهذا ينحصر التعديل في سطور معدودة. في الأسطر التالية تجد الخطوات العملية أولاً، ثم جداول المرجع الكامل، وطريقة تشغيل المزوّدَين جنباً إلى جنب قبل تحويل حركة الإنتاج بالكامل.

ما الذي يتغيّر فعلياً في الكود؟

الفكرة كلها تنحصر في تعديلين صغيرين، وكل ما عداهما يبقى على حاله:

  • عنوان النطاق الأساسي: من azcaptcha.com إلى ocr.captchaai.com في نقطتَي in.php وres.php.
  • مصدر المفتاح: مفتاح CaptchaAI جديد، ويُفضَّل قراءته من متغيّر بيئة بدل كتابته داخل الملف.
  • بقية الطلب: أسماء المعاملات وصيغة الاستجابة ومنطق الاستطلاع الدوري تبقى كما هي.

متى يستحق الانتقال؟

معظم فرق الأتمتة وجمع البيانات في المنطقة العربية تبحث عن أمرين: نتائج مستقرة وتكلفة يمكن التنبؤ بها. إن كنت تشغّل عمليات ليلية لمراقبة الأسعار أو اختبار جودة على متجر إلكتروني، فإن نموذج الدفع لكل عملية حل يجعل فاتورتك تتأرجح مع حجم العمل. يعتمد CaptchaAI بدلاً من ذلك على التسعير حسب عدد الـ Threads المتزامنة مع حلول غير محدودة لكل thread خلال الشهر، وتبدأ الباقات من BASIC بسعر 15 دولاراً شهرياً و5 threads، فتُحسب الميزانية مقدماً بحسب معدل الطلب المتزامن لا بحسب عدد عمليات الحل.

قبل أن تبدّل أي شيء، احسم هذه النقاط:

  • الاستقرار: هل يتساوى معدل الحل على بيئتك الفعلية أو يتحسّن؟
  • الأنواع: هل النوع الذي تستخدمه فعلاً مدعوم لدى CaptchaAI؟
  • التكلفة: هل التسعير حسب الـ Threads أنسب لحجم طلبك المتزامن؟

ملاحظة: لا تحوّل الإنتاج بناءً على انطباع أو على محاولة أو محاولتين؛ اتّخذ القرار من أرقام الاختبار المتوازي في الخطوة الرابعة.

خطوات الانتقال العملية

الخطوة 1: احصل على مفتاح CaptchaAI API

  1. سجّل حساباً على captchaai.com
  2. أضف رصيداً إلى حسابك
  3. انسخ مفتاح الـ API من لوحة التحكم

الخطوة 2: حدّث الكود

التعديل الجوهري سطران: عنوان النطاق ومصدر المفتاح. لاحظ في النسخة "بعد" أننا نقرأ المفتاح من متغيّر بيئة بدل كتابته داخل الملف، وهي عادة أفضل لأمن بيانات الاعتماد.

Python — قبل الانتقال (AZCaptcha)

import requests

API_KEY = "your_azcaptcha_key"

def solve_recaptcha(sitekey, pageurl):
    # Submit
    resp = requests.post("https://azcaptcha.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()
    if data["status"] != 1:
        return {"error": data["request"]}

    captcha_id = data["request"]

    # Poll
    import time
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://azcaptcha.com/res.php", params={
            "key": API_KEY, "action": "get", "id": captcha_id, "json": 1
        }).json()
        if result["status"] == 1:
            return {"solution": result["request"]}
        if result["request"] != "CAPCHA_NOT_READY":
            return {"error": result["request"]}

    return {"error": "TIMEOUT"}

Python — بعد الانتقال (CaptchaAI)

import os
import time
import requests

API_KEY = os.environ["CAPTCHAAI_API_KEY"]  # Changed: use env var

def solve_recaptcha(sitekey, pageurl):
    # Submit — only URL changed
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()
    if data.get("status") != 1:
        return {"error": data.get("request")}

    captcha_id = data["request"]

    # Poll — only URL changed
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": captcha_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return {"solution": result["request"]}
        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}

    return {"error": "TIMEOUT"}

JavaScript — قبل الانتقال (AZCaptcha)

const axios = require("axios");
const API_KEY = "your_azcaptcha_key";

async function solveRecaptcha(sitekey, pageurl) {
  const submit = await axios.post("https://azcaptcha.com/in.php", null, {
    params: { key: API_KEY, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
  });
  if (submit.data.status !== 1) return { error: submit.data.request };

  const captchaId = submit.data.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://azcaptcha.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (poll.data.status === 1) return { solution: poll.data.request };
    if (poll.data.request !== "CAPCHA_NOT_READY") return { error: poll.data.request };
  }
  return { error: "TIMEOUT" };
}

JavaScript — بعد الانتقال (CaptchaAI)

const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;  // Changed: env var

async function solveRecaptcha(sitekey, pageurl) {
  // Only URLs changed
  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: { key: API_KEY, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
  });
  if (submit.data.status !== 1) return { error: submit.data.request };

  const captchaId = submit.data.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (poll.data.status === 1) return { solution: poll.data.request };
    if (poll.data.request !== "CAPCHA_NOT_READY") return { error: poll.data.request };
  }
  return { error: "TIMEOUT" };
}

الخطوة 3: اعزل المزوّد خلف واجهة موحّدة

بدل توزيع عنوان النطاق في عشرات الملفات، اجمع منطق الإرسال والاستطلاع في فئة واحدة. عندها يصبح التبديل بين خدمتين مجرّد تغيير سطر واحد، وهو ما يجعل أي انتقال مستقبلي أقل هشاشة:

import os
import time
import requests


class CaptchaProvider:
    def __init__(self, base_url, api_key):
        self.submit_url = f"{base_url}/in.php"
        self.result_url = f"{base_url}/res.php"
        self.api_key = api_key
        self.session = requests.Session()

    def solve(self, sitekey, pageurl, method="userrecaptcha"):
        resp = self.session.post(self.submit_url, data={
            "key": self.api_key,
            "method": method,
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1
        })
        data = resp.json()
        if data.get("status") != 1:
            return {"error": data.get("request")}

        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 {"solution": result["request"]}
            if result.get("request") != "CAPCHA_NOT_READY":
                return {"error": result.get("request")}
        return {"error": "TIMEOUT"}


# Switch by changing one line:
# provider = CaptchaProvider("https://azcaptcha.com", "old_key")
provider = CaptchaProvider(
    "https://ocr.captchaai.com",
    os.environ["CAPTCHAAI_API_KEY"]
)

الخطوة 4: شغّل اختباراً متوازياً

لا تحوّل الإنتاج بناءً على انطباع. شغّل المزوّدَين على الطلبات نفسها وقارن معدل النجاح وزمن الحل على بيئتك الفعلية:

def parallel_test(sitekey, pageurl, runs=10):
    azcaptcha = CaptchaProvider("https://azcaptcha.com", "old_key")
    captchaai = CaptchaProvider(
        "https://ocr.captchaai.com",
        os.environ["CAPTCHAAI_API_KEY"]
    )

    results = {"azcaptcha": [], "captchaai": []}

    for i in range(runs):
        start = time.time()
        az_result = azcaptcha.solve(sitekey, pageurl)
        results["azcaptcha"].append({
            "success": "solution" in az_result,
            "time": time.time() - start
        })

        start = time.time()
        cai_result = captchaai.solve(sitekey, pageurl)
        results["captchaai"].append({
            "success": "solution" in cai_result,
            "time": time.time() - start
        })

    for provider, data in results.items():
        successes = sum(1 for r in data if r["success"])
        avg_time = sum(r["time"] for r in data) / len(data)
        print(f"{provider}: {successes}/{runs} success, {avg_time:.1f}s avg")

تنبيه منهجي: النتائج تختلف بحسب بيئة التشغيل والحجم والتوقيت. لعيّنة ذات دلالة، شغّل 50 طلباً أو أكثر على النوع الذي يهمّك فعلاً، لا عشرة فقط.

بعد أن تؤكد الأرقام أن CaptchaAI مساوٍ أو أفضل، ارفع نسبة الحركة المحوَّلة تدريجياً حتى تكتمل النقلة.

خريطة نقاط النهاية والمعاملات

نقاط النهاية متطابقة في البنية؛ يتغيّر النطاق فقط. هذا الجدول هو مرجع البحث والاستبدال الأول في مشروعك:

العملية AZCaptcha CaptchaAI
إرسال المهمة https://azcaptcha.com/in.php https://ocr.captchaai.com/in.php
الحصول على النتيجة https://azcaptcha.com/res.php https://ocr.captchaai.com/res.php
التحقق من الرصيد res.php?action=getbalance res.php?action=getbalance
الإبلاغ عن حل خاطئ res.php?action=reportbad res.php?action=reportbad

أما المعاملات فالفرق العملي الوحيد فيها هو المفتاح نفسه؛ بنية الطلب تبقى كما هي:

المعامل AZCaptcha CaptchaAI ملاحظات
key مفتاح الـ API مفتاح الـ API مفتاح مختلف — احصل عليه من captchaai.com
method userrecaptcha userrecaptcha مطابق
googlekey مفتاح الموقع مفتاح الموقع مطابق
pageurl عنوان URL للصفحة عنوان URL للصفحة مطابق
json 1 1 مطابق
proxy user:pass@host:port user:pass@host:port نفس الصيغة
proxytype HTTP/SOCKS5 HTTP/SOCKS5 مطابق

قائمة تحقق قبل تحويل الإنتاج

  • أنشئ حساب CaptchaAI وموّله ☐
  • استبدل عنوان النطاق الأساسي في جميع الملفات ☐
  • حدّث مفتاح الـ API (استخدم متغيّر بيئة) ☐
  • شغّل اختباراً متوازياً (10 حلول فأكثر) ☐
  • قارن معدلات النجاح ☐
  • قارن أوقات الحل ☐
  • حدّث المراقبة والتنبيهات لنقاط النهاية الجديدة ☐
  • بدّل حركة الإنتاج تدريجياً ☐
  • راقب لمدة 24 ساعة ☐
  • أوقف مفتاح AZCaptcha ☐

معالجة المشكلات الشائعة

قاعدة عامة: سجّل نوع الحل وزمن الاستجابة ورمز الخطأ معاً حتى يكون التشخيص موثوقاً عند أي عطل.

المشكلة السبب المحتمل الإجراء
ERROR_KEY_DOES_NOT_EXIST مفتاح API غير صحيح تحقق من المفتاح المنسوخ من لوحة تحكم CaptchaAI
ERROR_ZERO_BALANCE لا يوجد رصيد في الحساب الجديد أضف رصيداً على captchaai.com
اختلاف رموز الأخطاء بين الخدمتين فروق طفيفة في تسميات الأخطاء معظم الرموز متطابقة؛ أنشئ خريطة بسيطة للرموز المختلفة
اختلاف معدل الحل تجمّعات حل مختلفة شغّل 50 طلباً فأكثر للحصول على مقارنة ذات دلالة
الاختبار ينجح لكن الإنتاج يفشل اختلاف الجلسة أو الرؤوس أو سياق البروكسي أعد استخدام الشروط التي نجحت في الاختبار داخل سير العمل الفعلي

عند تكرار أي خطأ، افصل بين ما يخص المفتاح والرصيد وما يخص النوع والمعاملات قبل أن تفتح تذكرة دعم.

أسئلة شائعة

هل يمكن تشغيل AZCaptcha وCaptchaAI جنباً إلى جنب أثناء الترحيل؟

نعم، وهذا هو النهج الموصى به. أبقِ المزوّد القديم يعمل وحوّل نسبة صغيرة من الحركة إلى CaptchaAI أولاً، ثم ارفعها تدريجياً بعد أن تؤكد أرقام الاختبار المتوازي أن النتائج مساوية أو أفضل. الواجهة الموحّدة في الخطوة 3 تجعل هذا التبديل التدريجي أمراً بسيطاً على مرحلتين:

  • مرحلة القياس: نسبة صغيرة من الطلبات على CaptchaAI للمقارنة.
  • مرحلة التحويل: رفع النسبة تدريجياً حتى 100% ثم إيقاف المفتاح القديم.

ما أنواع CAPTCHA التي يحلّها CaptchaAI؟

يحلّ CaptchaAI اختبارات reCAPTCHA v2 وv3 (بما فيها Enterprise)، وCloudflare Turnstile وCloudflare Challenge، وGeeTest v3، والصور/OCR والشبكة، وBLS. أما hCaptcha وFunCaptcha فغير مدعومَين حالياً، وGeeTest v4 قيد الإضافة. وهناك دعم قيد التجربة (beta) لكل من:

  • CaptchaFox
  • Friendly Captcha
  • Lemin

تأكد من أن النوع المستخدم في مشروعك مدعوم قبل الانتقال.

كيف أتعامل مع اختلاف رموز الأخطاء؟

معظم رموز الأخطاء متطابقة بين الخدمتين لأن كليهما يتبع نمط 2Captcha. تعامَل مع الاختلافات القليلة بخريطة رموز مركزية في الكود بدل التحقق المتناثر، وهكذا يبقى التشخيص واضحاً حتى مع تغيّر المزوّد.

هل تتغير طريقة حساب التكلفة بعد الانتقال؟

نعم، وغالباً لصالح الميزانية. يعتمد CaptchaAI على التسعير حسب عدد الـ Threads المتزامنة مع حلول غير محدودة لكل thread شهرياً، بينما تُحسب كثير من الخدمات لكل عملية حل. تبدأ الباقات من BASIC بسعر 15 دولاراً شهرياً و5 threads، فتصبح التكلفة قابلة للتنبؤ بدل أن تتأرجح مع الحجم.

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

أدلة ذات صلة

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