دروس API

وكيل SOCKS5 + CaptchaAI: دليل الإعداد والتكوين

ضبط SOCKS5 مع CaptchaAI يتلخّص في ثلاثة قرارات: اكتب عنوان الخادم الوسيط بصيغة socks5h:// ليتولّى الخادم نفسه حلّ أسماء النطاقات، مرّر بيانات المصادقة بالطريقة التي يفهمها كل عميل — وهي ليست واحدة في requests وSelenium وPuppeteer — ثم أرسل بيانات البروكسي نفسها إلى CaptchaAI في طلب الإرسال حتى يجري الحل من عنوان IP الذي سيُرسَل النموذج منه بعد قليل.

وأكثر ما يُفسد هذا المسار ليس كود الحل، بل غياب حرف h من المخطط: يُحلّ اسم النطاق على جهازك بدل شبكة الوكيل، فيظهر الطلب قادماً من مكانين. يمشي الدليل على المسار كاملاً — Python المتزامن وغير المتزامن، Selenium، Node.js، Puppeteer — ثم يغلقه بتمرير الوكيل إلى الـ API.


هل تحتاج SOCKS5 أصلاً أم يكفيك بروكسي HTTP؟

بروكسي HTTP يكفي تماماً لأغلب عمليات جمع بيانات الويب البسيطة: طلب، استجابة، انتهى. تصبح الحاجة إلى SOCKS5 حقيقية في ثلاث حالات:

  • الصفحة تفتح قناة WebSocket — وهو نمط شائع في واجهات التحقق الحديثة وفي لوحات الحجز التي تحدّث المقاعد لحظياً.
  • مزوّد الوكيل لا يبيع سوى SOCKS5 — وهذه حال كثير من مزوّدي الوكلاء السكنيين في المنطقة.
  • تريد حركة مرور لا يعبث بها أحد — لا رؤوس مضافة ولا إعادة كتابة للطلب على مستوى التطبيق.

خارج هذه الحالات لن يمنحك التبديل مكسباً يُذكر في معدل الحل.

الفروق العملية بين البروتوكولين

المعيار وكيل HTTP/HTTPS وكيل SOCKS5
دعم البروتوكول HTTP/HTTPS فقط أي حركة TCP/UDP
تعديل الرؤوس قد يضيف X-Forwarded-For لا يعدّل الطلب
بصمة الطلب أوضح — تسرّب الرؤوس أقل وضوحاً
السرعة سريع أبطأ قليلاً
المصادقة Basic/Digest اسم المستخدم وكلمة المرور
حلّ DNS من جانب العميل من جانب الخادم عبر socks5h
دعم WebSocket محدود كامل

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


الخطوة 1: اضبط requests مع PySocks

ابدأ بتثبيت الحزمتين معاً — requests[socks] تضيف دعم المخطط، وpysocks هي التنفيذ الفعلي تحته:

pip install requests[socks] pysocks

المثال التالي يفصل بين مسارين لا يجب خلطهما: جلب الصفحة الهدف يمر عبر الخادم الوسيط، بينما يذهب طلب الحل إلى CaptchaAI مباشرة. لاحظ في السطور الأولى الفرق بين socks5h وsocks5 — الأول يمرّر حلّ DNS إلى الوكيل، وهو الخيار الموصى به هنا.

import requests
import time

SOCKS5_HOST = "proxy.example.com"
SOCKS5_PORT = 1080
SOCKS5_USER = "proxyuser"
SOCKS5_PASS = "proxypass"

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

# SOCKS5 proxy configuration
proxies = {
    "http": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
    "https": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
}
# socks5h = DNS resolved by proxy server (recommended)
# socks5  = DNS resolved locally


def fetch_through_socks(url):
    """Fetch URL through SOCKS5 proxy."""
    return requests.get(
        url,
        proxies=proxies,
        headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36"
        },
        timeout=30,
    )


def solve_captcha(site_url, sitekey):
    """Solve CAPTCHA via CaptchaAI (direct, no proxy needed)."""
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": site_url,
        "json": 1,
    })
    data = resp.json()
    if data["status"] != 1:
        raise Exception(f"Submit: {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")


# Full workflow
resp = fetch_through_socks("https://target.com/form")

import re
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if match:
    token = solve_captcha("https://target.com/form", match.group(1))
    # Submit with token through same proxy
    resp = requests.post(
        "https://target.com/submit",
        data={"g-recaptcha-response": token},
        proxies=proxies,
    )

النقطة الجوهرية في الكود أعلاه هي أن الرمز العائد من res.php يُرسَل مع النموذج عبر الجلسة نفسها التي جلبت الصفحة، أي من عنوان IP نفسه. أي انقطاع في هذه السلسلة — جلسة جديدة، أو وكيل مختلف عند الإرسال — يجعل الرمز صحيحاً لكن الطلب مرفوضاً.

التشغيل غير المتزامن عبر aiohttp

عند تشغيل عشرات الطلبات المتوازية، استبدل requests بـ aiohttp مع موصّل aiohttp_socks:

import aiohttp
import aiohttp_socks
import asyncio


async def fetch_async(url):
    connector = aiohttp_socks.ProxyConnector.from_url(
        f"socks5://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}"
    )

    async with aiohttp.ClientSession(connector=connector) as session:
        async with session.get(url) as resp:
            return await resp.text()


asyncio.run(fetch_async("https://target.com/form"))

الخطوة 2: Selenium خلف SOCKS5 وحلّ المصادقة

هنا يقع أشهر مأزق في هذا الإعداد: وسيط سطر الأوامر --proxy-server=socks5://host:port لا يقبل اسم مستخدم وكلمة مرور، لذا يعمل مع الوكلاء المفتوحين فقط. إن كان وكيلك يتطلب مصادقة — وهو الغالب في الاشتراكات المدفوعة — فاستخدم seleniumwire الذي يمرّر بيانات الاعتماد على مستوى الجلسة كما في الدالة الثانية:

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


def create_socks5_driver(host, port, username=None, password=None):
    options = webdriver.ChromeOptions()

    # SOCKS5 proxy (no auth via command line)
    options.add_argument(f"--proxy-server=socks5://{host}:{port}")

    # DNS through proxy
    options.add_argument("--host-resolver-rules=MAP * ~NOTFOUND, EXCLUDE 127.0.0.1")

    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_argument("--window-size=1920,1080")

    driver = webdriver.Chrome(options=options)
    return driver


# For authenticated SOCKS5, use seleniumwire
from seleniumwire import webdriver as sw_webdriver

def create_auth_socks5_driver():
    options = {
        "proxy": {
            "http": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
            "https": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
        }
    }

    chrome_options = sw_webdriver.ChromeOptions()
    chrome_options.add_argument("--disable-blink-features=AutomationControlled")

    return sw_webdriver.Chrome(
        seleniumwire_options=options,
        options=chrome_options,
    )


# Usage
driver = create_auth_socks5_driver()
driver.get("https://target.com/form")
time.sleep(3)

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

if sitekey:
    token = solve_captcha("https://target.com/form", sitekey)
    driver.execute_script(f"""
        document.querySelector('#g-recaptcha-response').value = '{token}';
    """)
    driver.find_element(By.CSS_SELECTOR, "form").submit()

driver.quit()

الخطوة 3: Node.js مع socks-proxy-agent

في Node.js يكفي وكيل واحد يُمرَّر إلى Axios عبر httpAgent وhttpsAgent معاً — نسيان أحدهما يعني أن جزءاً من حركة المرور سيخرج من شبكتك مباشرة. وكما في مثال Python، تبقى نداءات CaptchaAI خارج الوكيل:

const { SocksProxyAgent } = require("socks-proxy-agent");
const axios = require("axios");

const CAPTCHAAI_KEY = "YOUR_API_KEY";

const socksAgent = new SocksProxyAgent(
  "socks5h://proxyuser:[email protected]:1080"
);

async function fetchViaSocks(url) {
  return axios.get(url, {
    httpsAgent: socksAgent,
    httpAgent: socksAgent,
    headers: {
      "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0.0.0",
    },
  });
}

async function solveCaptcha(siteUrl, sitekey) {
  // CaptchaAI calls don't go through SOCKS proxy
  const submit = await axios.post(
    "https://ocr.captchaai.com/in.php",
    null,
    {
      params: {
        key: CAPTCHAAI_KEY,
        method: "userrecaptcha",
        googlekey: sitekey,
        pageurl: siteUrl,
        json: 1,
      },
    }
  );

  const taskId = submit.data.request;

  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");
}

الخطوة 4: تشغيل Puppeteer عبر SOCKS5

يستقبل المتصفح عنوان الوكيل عند الإقلاع، بينما تُمرَّر بيانات الاعتماد لاحقاً عبر page.authenticate. رتّب الاستدعاءين بهذا الترتيب دائماً: الإقلاع أولاً، ثم المصادقة، ثم goto:

const puppeteer = require("puppeteer");

async function launchWithSocks5() {
  const browser = await puppeteer.launch({
    args: [
      "--proxy-server=socks5://proxy.example.com:1080",
      "--no-sandbox",
      "--window-size=1920,1080",
    ],
  });

  const page = await browser.newPage();

  // Authenticate if needed
  await page.authenticate({
    username: "proxyuser",
    password: "proxypass",
  });

  await page.goto("https://target.com/form", { waitUntil: "networkidle0" });

  const sitekey = await page.evaluate(() =>
    document.querySelector("[data-sitekey]")?.getAttribute("data-sitekey")
  );

  if (sitekey) {
    const token = await solveCaptcha(page.url(), sitekey);
    await page.evaluate((t) => {
      document.querySelector("#g-recaptcha-response").value = t;
    }, token);
  }

  await browser.close();
}

الخطوة 5: مرّر الوكيل إلى CaptchaAI لمطابقة عنوان IP

حتى الآن كان الحل يجري من شبكة الخدمة والإرسال من شبكة وكيلك. لتوحيدهما، أضف معاملين إلى طلب in.php: proxy بالصيغة type:host:port:user:pass وproxytype=SOCKS5. عندها يتم الحل من عنوان IP نفسه الذي سيصل منه النموذج، وهو ما تنتظره المواقع التي تربط الرمز بجلسة الزائر:

def solve_with_proxy(site_url, sitekey, proxy_url):
    """Pass proxy to CaptchaAI for IP-matched solving."""
    # Format: type:host:port:user:pass
    proxy_param = f"socks5:{SOCKS5_HOST}:{SOCKS5_PORT}:{SOCKS5_USER}:{SOCKS5_PASS}"

    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": site_url,
        "proxy": proxy_param,
        "proxytype": "SOCKS5",
        "json": 1,
    })

    data = resp.json()
    if data["status"] != 1:
        raise Exception(f"Submit: {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":
            return data["request"]

    raise TimeoutError("Timeout")

مثال تشغيلي: اختبارات ليلية لبوابة حجز خليجية

فريق ضمان جودة في الرياض يشغّل اختبارات ليلية من خادم CI في فرانكفورت، على بوابة حجز تعرض reCAPTCHA v2 وCloudflare Turnstile لكل زائر من خارج المنطقة. ترتيب العمل الذي يجعل الاختبارات مستقرة:

  1. توجيه جلسات المتصفح عبر وكيل SOCKS5 بمخرج داخل السعودية، بصيغة socks5h حتى لا يخرج استعلام DNS من شبكة فرانكفورت.
  2. تمرير بيانات الوكيل نفسها في طلب in.php مع proxytype=SOCKS5.
  3. ضبط عدد الجلسات المتوازية على ذروة التزامن الحقيقية، لا على إجمالي عدد الاختبارات.

المحاسبة في CaptchaAI تقوم على عدد الـ threads المتزامنة مع حلول غير محدودة داخل الخطة، لذا تكفي خطة STANDARD — $30 شهرياً بـ15 thread — لتشغيل 12 جلسة متوازية، ويرفع موسمُ الحجوزات التوازي إلى 40 جلسة فينتقل الفريق إلى ADVANCE — $90 شهرياً بـ50 thread — دون أي رسوم إضافية لكل عملية حل. الـ thread يتحرر لحظة انتهاء الحل ليستقبل الطلب التالي — احسب على الذروة اللحظية، لا على حجم الليلة كاملة.


أعطال متكررة وكيف تُقرأ

العرَض السبب الأرجح الإجراء
رفض الاتصال فوراً مضيف أو منفذ خاطئ تأكد أن خادم SOCKS5 يعمل على المنفذ المعلن
الموقع يرى موقعك الحقيقي استخدام socks5:// بدل socks5h:// حوّل المخطط إلى socks5h:// ليتم حلّ DNS على الوكيل
فشل المصادقة بيانات اعتماد خاطئة أو رموز خاصة غير مُرمّزة اختبر مباشرة بـ curl --socks5 قبل تشغيل السكربت
الرمز صحيح لكن النموذج مرفوض الحل جرى من عنوان IP مختلف عن عنوان الإرسال مرّر الوكيل نفسه في in.php عبر proxytype
يعمل مع curl ولا يعمل مع Chrome وسيط سطر الأوامر لا يقبل بيانات اعتماد استخدم seleniumwire أو وكيلاً محلياً بلا مصادقة
بطء ملحوظ في كل طلب مخرج الوكيل بعيد جغرافياً عن الموقع الهدف اختر مخرجاً أقرب إلى الخادم الهدف
انقطاع قناة WebSocket خادم SOCKS5 بلا دعم UDP انتقل إلى خادم يدعم UDP

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

ما الفرق بين socks5 وsocks5h عملياً؟

في socks5 يُحلّ اسم النطاق على جهازك ثم يُرسل عنوان IP إلى الوكيل، وفي socks5h يُرسل الاسم كما هو ليحلّه الوكيل. الفرق حرف واحد، لكنه يحدّد ما إذا كان استعلام DNS سيكشف موقعك الفعلي. اعتمد socks5h افتراضياً.

هل يجب أن تمر نداءات CaptchaAI نفسها عبر الوكيل؟

لا. طلبات in.php وres.php تذهب مباشرة إلى الخدمة، وإرسالها عبر الوكيل يضيف زمناً بلا فائدة. الوكيل يخص حركة المرور مع الموقع الهدف فقط، أما مطابقة عنوان IP فتُنجَز بمعامل proxy داخل الطلب.

كيف أتحقق من الوكيل قبل تشغيل السكربت؟

نفّذ طلباً واحداً بـ curl --socks5-hostname نحو خدمة تُظهر عنوان IP الخارج، وقارنه بالمخرج المتوقع من مزوّدك. ظهور عنوانك المحلي يعني خطأ في المخطط أو حركة مرور تخرج خارج الوكيل.

هل أحتاج وكيلاً منفصلاً لكل thread في خطتي؟

لا. الـ thread وحدة تزامن داخل CaptchaAI، والوكيل مورد شبكي منفصل: وكيل واحد لكل جلسة متصفح، وthread واحد لكل اختبار CAPTCHA قيد المعالجة في اللحظة نفسها.

ماذا يحدث إذا تغيّر عنوان IP الوكيل بين الحل والإرسال؟

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


اقرأ أيضاً


جاهز لتشغيل المسار كاملاً؟ أنشئ مفتاح الـ API الخاص بك وشغّل أول طلب حل عبر وكيل SOCKS5 اليوم.

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