التكاملات

Smartproxy + CaptchaAI: إعداد الوكيل السكني لحل اختبار CAPTCHA

تكامل Smartproxy مع CaptchaAI ينجح أو يفشل عند تفصيلة واحدة: أن يكون العنوان الذي حمّل الصفحة هو نفسه العنوان الذي يرسل النموذج. بوابة Smartproxy السكنية تتكفّل بمسار الخروج وتثبيت الجلسة، بينما يستقبل CaptchaAI ما يظهر من reCAPTCHA v2 أو Cloudflare Turnstile عبر in.php ويعيد التوكن من res.php.

لذلك يبدأ الترتيب هنا من ربط البوابة، ثم تثبيت الجلسة حتى لحظة الإرسال، قبل استهداف الدولة والتشغيل المتوازي وحساب عدد الـ threads. الترتيب مقصود: توكن صحيح تماماً يُرفض إذا تبدّل العنوان في منتصف الدورة، وهو أكثر عطل يتكرّر في هذا المسار.

ما الذي تعالجه كل طبقة

فصل المسؤوليات منذ البداية يوفّر ساعات تشخيص لاحقاً:

  • Smartproxy — مسار خروج الطلب: عنوان سكني، تثبيت الجلسة، اختيار الدولة، وإدارة استهلاك عرض النطاق.
  • CaptchaAI — حل التحدي نفسه: reCAPTCHA v2 وv3 ونسختاهما Enterprise، وCloudflare Turnstile وCloudflare Challenge، وGeeTest v3، وتحديات الصور وOCR والصور الشبكية وBLS، إضافة إلى CaptchaFox وFriendly Captcha وLemin في مرحلة beta.
  • سكربت الأتمتة عندك — اكتشاف مفتاح الموقع، إرسال المهمة، الاستطلاع الدوري للنتيجة، ثم حقن التوكن في النموذج.

البروكسي لا يحلّ تحدياً، وخدمة الحل لا تُصلح سمعة عنوان IP رديئة. أغلب حالات الفشل التي تصل إلى الدعم سببها الخلط بين هذين الدورين.

خريطة شبكات Smartproxy وأيها يناسب سير عمل CAPTCHA

الشبكة حجم المجمّع الأنسب لـ معدّل ظهور CAPTCHA
سكنية أكثر من 55 مليون IP جمع بيانات عام منخفض
مركز بيانات أكثر من 100 ألف IP نقل سريع لبيانات مفتوحة متوسط إلى مرتفع
جوّال أكثر من 10 مليون IP مواقع تخدم الجوال أساساً منخفض جداً
ISP عناوين ثابتة بجودة سكنية جلسات طويلة ومتصلة منخفض

القاعدة العملية: ابدأ من الشبكة السكنية مع أي هدف يعرض تحدياً، واحتفظ بمركز البيانات للطلبات التي لا تمرّ بطبقة تحقق. أما الشبكة الجوّالة فخيار متأخر وأعلى تكلفة.

الخطوة 1: اربط Python ببوابة Smartproxy السكنية

بيانات الاعتماد ونقطة النهاية تجدها في لوحة تحكم Smartproxy، ومفتاح الـ API في حساب CaptchaAI. الكود التالي يفصل الدورين بوضوح: fetch_page تمرّ عبر البروكسي، بينما solve_captcha تخاطب CaptchaAI مباشرة — أرسل المهمة إلى in.php، ثم استطلع res.php كل خمس ثوانٍ حتى تعود النتيجة.

import requests
import time

SMARTPROXY_USER = "spuser"
SMARTPROXY_PASS = "sppassword"
SMARTPROXY_HOST = "gate.smartproxy.com"
SMARTPROXY_PORT = 10001

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"

proxies = {
    "http": f"http://{SMARTPROXY_USER}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}",
    "https": f"http://{SMARTPROXY_USER}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}",
}


def fetch_page(url):
    return requests.get(url, proxies=proxies, timeout=30)


def solve_captcha(site_url, sitekey, captcha_type="recaptcha_v2"):
    submit_data = {
        "key": CAPTCHAAI_KEY,
        "pageurl": site_url,
        "json": 1,
    }

    if captcha_type == "turnstile":
        submit_data["method"] = "turnstile"
        submit_data["sitekey"] = sitekey
    else:
        submit_data["method"] = "userrecaptcha"
        submit_data["googlekey"] = sitekey

    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=submit_data)
    data = resp.json()
    if data["status"] != 1:
        raise Exception(f"Submit failed: {data['request']}")

    task_id = data["request"]

    for _ in range(60):
        time.sleep(5)
        resp = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY,
            "action": "get",
            "id": task_id,
            "json": 1,
        })
        data = resp.json()
        if data["request"] == "CAPCHA_NOT_READY":
            continue
        if data["status"] == 1:
            return data["request"]
        raise Exception(f"Solve: {data['request']}")

    raise TimeoutError("Timeout")

تفصيلة يغفلها كثيرون: قيمة method تتغيّر بحسب النوع — userrecaptcha مع googlekey لـ reCAPTCHA v2، وturnstile مع sitekey لـ Cloudflare Turnstile. الناتج في الحالتين نص واحد يوضع في حقل g-recaptcha-response داخل النموذج قبل إرساله.

الخطوة 2: ثبّت عنوان IP حتى لحظة الإرسال

الجلسة الثابتة تُخرج الطلبات المتتابعة من العنوان نفسه لمدة تحدّدها أنت. أضف -session-ID و-sessionduration إلى اسم المستخدم، واستخدم المعرّف ذاته طوال الدورة:

import random
import string


def get_sticky_proxy(session_duration_minutes=10):
    """Create a sticky session proxy (same IP for duration)."""
    session_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))

    proxy_url = (
        f"http://{SMARTPROXY_USER}"
        f"-session-{session_id}"
        f"-sessionduration-{session_duration_minutes}"
        f":{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
    )

    return {"http": proxy_url, "https": proxy_url}


# Use same IP for entire CAPTCHA workflow
sticky = get_sticky_proxy(session_duration_minutes=10)

# Page load
resp = requests.get("https://target.com/form", proxies=sticky)

# Solve CAPTCHA
token = solve_captcha("https://target.com/form", "SITEKEY_HERE")

# Submit with same IP
resp = requests.post(
    "https://target.com/submit",
    data={"g-recaptcha-response": token},
    proxies=sticky,
)

ثلاثة قرارات تحسم نجاح هذه الخطوة:

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

الخطوة 3: اختر الدولة التي يتوقّعها الموقع الهدف

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

# Smartproxy country targeting via username
def get_country_proxy(country_code):
    proxy_url = (
        f"http://{SMARTPROXY_USER}"
        f"-country-{country_code}"
        f":{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
    )
    return {"http": proxy_url, "https": proxy_url}

# US proxy
us_proxy = get_country_proxy("us")

# UK proxy
uk_proxy = get_country_proxy("gb")

# Germany proxy
de_proxy = get_country_proxy("de")

راجع قائمة الدول المتاحة في لوحة التحكم قبل تثبيت رمز بعينه في الإنتاج، وسجّل رمز الدولة مع نتيجة كل محاولة — هذه أسرع طريقة لمعرفة أي وجهة ترفع معدّل ظهور التحدي.

الخطوة 4: احمل الجلسة نفسها داخل متصفح Selenium

المواقع التي تبني النموذج بـ JavaScript تحتاج متصفحاً حقيقياً، والمبدأ لا يتغيّر: مرّر رمز الدولة ومعرّف الجلسة في اسم المستخدم، اقرأ data-sitekey من الصفحة، أرسله إلى CaptchaAI، ثم احقن التوكن قبل إرسال النموذج.

from selenium import webdriver
from selenium.webdriver.common.by import By


def create_smartproxy_driver(country=None, sticky_session=None):
    proxy_user = SMARTPROXY_USER
    if country:
        proxy_user += f"-country-{country}"
    if sticky_session:
        proxy_user += f"-session-{sticky_session}"

    proxy_url = f"{proxy_user}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"

    options = webdriver.ChromeOptions()
    options.add_argument(f"--proxy-server=http://{SMARTPROXY_HOST}:{SMARTPROXY_PORT}")
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_argument("--window-size=1920,1080")

    # For authenticated proxies, use seleniumwire or extension
    return webdriver.Chrome(options=options)


def scrape_with_captcha(url, country="us"):
    session_id = "".join(random.choices(string.ascii_lowercase, k=8))
    driver = create_smartproxy_driver(country=country, sticky_session=session_id)

    try:
        driver.get(url)
        time.sleep(3)

        sitekey = driver.execute_script(
            "return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
        )

        if sitekey:
            token = solve_captcha(url, sitekey)
            driver.execute_script(f"""
                document.querySelector('#g-recaptcha-response').value = '{token}';
            """)
            driver.find_element(By.CSS_SELECTOR, "form").submit()
            time.sleep(3)

        return driver.page_source

    finally:
        driver.quit()

مصادقة البروكسي باسم مستخدم وكلمة مرور لا يقبلها Chrome عبر --proxy-server وحده، لذا استعن بـ seleniumwire أو بامتداد يحمل بيانات الاعتماد — وهو ما تشير إليه الملاحظة داخل الكود.

نفس النمط في Node.js

الفرق التي تعمل على Node.js تستخدم البنية ذاتها: وكيل HTTP للطلبات الموجّهة إلى الهدف، ونداءات مباشرة إلى CaptchaAI للحل.

const axios = require("axios");
const HttpsProxyAgent = require("https-proxy-agent");

const CAPTCHAAI_KEY = "YOUR_API_KEY";

function getSmartproxyAgent(options = {}) {
  let user = "spuser";
  if (options.country) user += `-country-${options.country}`;
  if (options.session) user += `-session-${options.session}`;

  return new HttpsProxyAgent(
    `http://${user}:[email protected]:10001`
  );
}

async function scrapeWithCaptcha(url, sitekey) {
  const agent = getSmartproxyAgent({
    country: "us",
    session: `sess-${Date.now()}`,
  });

  // Fetch page through proxy
  const pageResp = await axios.get(url, { httpsAgent: agent });

  // Solve CAPTCHA via CaptchaAI (no proxy needed)
  const submitResp = await axios.post(
    "https://ocr.captchaai.com/in.php",
    null,
    {
      params: {
        key: CAPTCHAAI_KEY,
        method: "userrecaptcha",
        googlekey: sitekey,
        pageurl: url,
        json: 1,
      },
    }
  );

  const taskId = submitResp.data.request;

  // Poll for result
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: {
        key: CAPTCHAAI_KEY,
        action: "get",
        id: taskId,
        json: 1,
      },
    });

    if (result.data.request === "CAPCHA_NOT_READY") continue;
    if (result.data.status === 1) return result.data.request;
  }

  throw new Error("Timeout");
}

لاحظ أن نداءي in.php وres.php هنا لا يمرّان عبر البروكسي، وهو توفير ملموس في عرض النطاق المدفوع مع الحجم.

الخطوة 5: التشغيل المتوازي وحساب عدد الـ threads

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

from concurrent.futures import ThreadPoolExecutor, as_completed


def process_url(url):
    session_id = "".join(random.choices(string.ascii_lowercase, k=8))
    proxy = get_sticky_proxy(10)

    try:
        resp = requests.get(url, proxies=proxy, timeout=30)

        # Check if CAPTCHA is present (simplified detection)
        if "data-sitekey" in resp.text:
            import re
            match = re.search(r'data-sitekey="([^"]+)"', resp.text)
            if match:
                sitekey = match.group(1)
                token = solve_captcha(url, sitekey)
                return {"url": url, "status": "solved", "token": token[:30]}

        return {"url": url, "status": "no_captcha"}

    except Exception as e:
        return {"url": url, "status": "error", "error": str(e)}


urls = [
    "https://site1.com/page",
    "https://site2.com/page",
    "https://site3.com/page",
]

with ThreadPoolExecutor(max_workers=5) as executor:
    futures = {executor.submit(process_url, u): u for u in urls}

    for future in as_completed(futures):
        result = future.result()
        print(f"[{result['status']}] {result['url']}")

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

جدول الأعطال الشائعة وحلولها

العَرَض السبب المرجّح الإجراء
407 Proxy Authentication Required تنسيق اسم المستخدم أو كلمة المرور غير صحيح انسخ بيانات الاعتماد من لوحة تحكم Smartproxy كما هي
تغيّر عنوان IP في منتصف الدورة الجلسة غير مثبّتة أضف -session-ID إلى اسم المستخدم
تحدٍّ يظهر في كل طلب استخدام بوابة مركز البيانات انتقل إلى البوابة السكنية
بطء واضح في الاستجابة ازدحام على الوجهة الجغرافية المختارة جرّب دولة أو مدينة أخرى
رفض التوكن بعد حل ناجح تبدّل العنوان بين التحميل والإرسال أطل مدة الجلسة الثابتة

قائمة تحقق قبل أول دفعة إنتاج

  1. مفتاح الـ API وبيانات البروكسي في متغيّرات البيئة، لا داخل ملف الكود.
  2. معرّف جلسة مستقل لكل خيط تنفيذ، ومدة تغطّي الدورة كاملة.
  3. مهلة انتهاء واضحة لحلقة الاستطلاع، مع إعادة المحاولة بتراجع أسي عند الأخطاء المؤقتة.
  4. تسجيل رمز الدولة ومعرّف المهمة ونتيجة كل محاولة في سجل واحد قابل للمراجعة.
  5. مراقبة الرصيد وعدد الـ threads المستهلكة قبل رفع حجم الدفعة.

الأسئلة الشائعة

هل أحتاج البروكسي في كل خطوة أم عند تحميل الصفحة فقط؟

الطلبات الموجّهة إلى الموقع الهدف تمرّ عبر Smartproxy، أما نداءات in.php وres.php فتخاطب CaptchaAI مباشرة ولا تحتاج بروكسي. وإذا كان الهدف يربط التحقق بعنوان الزائر، مرّر معاملات البروكسي داخل طلب الإرسال ليتم الحل من العنوان نفسه.

لماذا يُرفض توكن صحيح بعد نجاح الحل؟

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

ما أنواع CAPTCHA التي يغطّيها هذا المسار؟

reCAPTCHA v2 وv3 ونسختاهما Enterprise، وCloudflare Turnstile وCloudflare Challenge، وGeeTest v3، وتحديات الصور وOCR والصور الشبكية، وBLS. أما hCaptcha وFunCaptcha فليسا مدعومين، وGeeTest v4 معلن ضمن القادم قريباً ولم يُتَح بعد. وتأتي CaptchaFox وFriendly Captcha وLemin في مرحلة beta.

كم thread أحتاج لمعالجة 500 صفحة في الساعة؟

اقسم الحجم على ما ينجزه thread واحد. إذا استغرقت الدورة الكاملة — تحميل عبر البروكسي، ثم حل، ثم إرسال — نحو 30 ثانية، فإن thread واحداً يعالج قرابة 120 صفحة في الساعة، وخمسة threads تقارب 600. ابدأ من تقدير محافظ، وراقب طول قائمة الانتظار قبل رفع الخطة.

أدلة ذات صلة

جهّز الطبقتين مرة واحدة: افتح حساب CaptchaAI واحصل على مفتاح الـ API ثم شغّل أول سكربت عبر بوابتك السكنية.

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