middleware البروكسي في Scrapy: دليل الإعداد الكامل (2026)

نُشر في 4 يونيو 2026 · ≈11 دقيقة قراءة

لا يزال Scrapy هو حصان العمل لعمليات الزحف الإنتاجية في 2026 — ولا يزال الإطار الذي يربك الناس أكثر من غيره في إعداد البروكسي، لأن هناك أربعة أماكن مختلفة لتوصيل البروكسي، وثلاثة منها خاطئة لمعظم المشاريع. يمنحك هذا الدليل المكان الصحيح: middleware مخصص صغير مع توجيه لكل طلب، وجلسات لاصقة، وكشف الحظر، وسلوك إعادة محاولة معقول.

إذا كنت تستخدم requests/httpx/aiohttp العادية بدلًا من ذلك، راجع كيفية تناوب البروكسيات في Python. هذا المقال خاص بـ Scrapy.

النسخة المختصرة في 30 ثانية

لبوابة سكنية متناوبة، الإعداد الأدنى القابل للتطبيق هو سطر واحد لكل طلب — لا حاجة إلى middleware:

def start_requests(self):
    for url in self.urls:
        yield scrapy.Request(
            url,
            meta={"proxy": "http://USERNAME:[email protected]:913"},
        )

يقرأ HttpProxyMiddleware المدمج في Scrapy قيمة request.meta["proxy"] ويتولى المصادقة من عنوان URL. تتناوب البوابة عنوان IP الخارج نيابة عنك. إذا كان هذا كل ما تحتاجه، توقّف هنا. بقية هذا الدليل مخصصة لعندما تحتاج إلى التحكم: الجلسات اللاصقة، والتوجيه حسب البلد، والتناوب الواعي بالحظر، وضبط التزامن.

middleware بروكسي إنتاجي

ضع هذا في middlewares.py. يُخصّص جلسات لاصقة لكل نطاق، ويتناوب عند الحظر، ويُعلّم كل طلب حتى تتمكن من تصحيح الجلسة التي جلبت ماذا:

import random
import string

GATEWAY = "us.jibaoproxy.com:913"
USERNAME = "USERNAME"          # move to settings.py / env in real projects
PASSWORD = "PASSWORD"

def _new_session(n=8):
    return "".join(random.choices(string.ascii_lowercase + string.digits, k=n))

class JibaoProxyMiddleware:
    """Sticky session per domain; rotate session on ban."""

    def __init__(self):
        self.sessions = {}          # domain -> session id

    def _proxy_url(self, session_id):
        user = f"{USERNAME}-session-{session_id}"
        return f"http://{user}:{PASSWORD}@{GATEWAY}"

    def process_request(self, request, spider):
        domain = request.url.split("/")[2]
        session = self.sessions.setdefault(domain, _new_session())
        request.meta["proxy"] = self._proxy_url(session)
        request.meta["proxy_session"] = session

    def rotate(self, domain):
        """Call when a session is burned."""
        self.sessions[domain] = _new_session()

و middleware مُكمّل للتنزيل يكشف عمليات الحظر ويعيد المحاولة على جلسة جديدة:

from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

BAN_CODES = {403, 429}
BAN_MARKERS = (b"captcha", b"access denied", b"unusual traffic")

class BanAwareRetryMiddleware(RetryMiddleware):

    def process_response(self, request, response, spider):
        banned = (
            response.status in BAN_CODES
            or any(m in response.body[:2048].lower() for m in BAN_MARKERS)
        )
        if banned:
            domain = request.url.split("/")[2]
            proxy_mw = spider.crawler.engine.downloader.middleware.middlewares
            for mw in proxy_mw:
                if hasattr(mw, "rotate"):
                    mw.rotate(domain)        # burn the session
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return super().process_response(request, response, spider)

وصِّل كليهما في settings.py:

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.JibaoProxyMiddleware": 350,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": None,   # replace stock retry
    "myproject.middlewares.BanAwareRetryMiddleware": 550,
}
RETRY_TIMES = 2

الأولوية مهمة: يجب أن يعمل middleware البروكسي قبل HttpProxyMiddleware الخاص بـ Scrapy (750)، لذا فأي شيء أقل من 750 يعمل؛ القيمة 350 تُبقيه مبكرًا ويمكن التنبؤ به.

لاصق مقابل متناوب: أي وضع لأي spider

نوع الزحفالوضعالتنفيذ
حصاد الصفحات بلا حالةمتناوباسم مستخدم مجرّد، تتناوب البوابة لكل طلب
تسجيل الدخول + الزحف خلف المصادقةلاصق لكل حساب-session-{account_id}، لا تتناوب أبدًا في منتصف تسجيل الدخول
قوائم كثيفة التصفّحلاصق لكل نطاق، يتناوب عند الحظرالـ middleware أعلاه
تسعير خاص بالمنطقة الجغرافيةمتناوب + تثبيت بلدمعاملات بنمط USERNAME-country-de

معالجة أعمق لهذه المفاضلة: جلسات البروكسي اللاصقة مقابل المتناوبة.

إعدادات تزامن لا تتسبب في حظرك

إعدادات Scrapy الافتراضية مضبوطة للزحف المهذّب بعنوان IP واحد. خلف مجمّع متناوب يمكنك الدفع بقوة أكبر — لكن حدود كل نطاق لا تزال مهمة لأن الهدف يرى السلوك الإجمالي:

# settings.py - sane starting point behind a residential pool
CONCURRENT_REQUESTS = 64
CONCURRENT_REQUESTS_PER_DOMAIN = 8     # what the target experiences
DOWNLOAD_DELAY = 0.25                  # jitter applied per slot
RANDOMIZE_DOWNLOAD_DELAY = True        # 0.5x-1.5x the delay
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 6.0
DOWNLOAD_TIMEOUT = 30
ROBOTSTXT_OBEY = True

ارفع CONCURRENT_REQUESTS_PER_DOMAIN فقط بعد مراقبة معدّل 403 لديك عند المستوى الحالي لبضعة آلاف من الطلبات. الانتقال من 8 → 32 لأن "البروكسيات تتناوب على أي حال" هو الطريقة التي يستنزف بها الناس الجيجابايت في إعادات المحاولة.

انضباط النطاق الترددي (جيجابايت السكني تساوي نقودًا)

الأعطال الشائعة وما تعنيه فعلًا

407 Proxy Authentication Required

لم تصل بيانات الاعتماد إلى البروكسي. ضعها في عنوان URL (http://user:pass@host:port) داخل meta["proxy"] — فـ Scrapy يحلّلها ويضبط Proxy-Authorization نيابة عنك. ضبط الترويسة يدويًا و استخدام بيانات اعتماد عنوان URL يسبّب غرابة المصادقة المزدوجة؛ اختر واحدًا.

TunnelError: Could not open CONNECT tunnel

غالبًا ما يكون خطأً مطبعيًا في المضيف/المنفذ، أو هدف HTTPS عبر نقطة نهاية لا تسمح بـ CONNECT على ذلك المنفذ. تحقّق باستخدام curl -x خارج Scrapy أولًا.

الـ spider يعمل لمدة 10 دقائق، ثم يصبح كل شيء 403

تجاوزت جلستك اللاصقة الترحيب بها، أو أن معدّلك لكل نطاق ساخن جدًا. يعالج الـ middleware الواعي بالحظر أعلاه الحالة الأولى؛ خفّض CONCURRENT_REQUESTS_PER_DOMAIN للحالة الثانية. إذا كان هدفًا يفحص JA4، فقد تكون حزمة TLS الخاصة بـ Scrapy نفسها هي الدليل — راجع شرح JA3/JA4 لمعرفة لماذا لا يصلح أي بروكسي ذلك.

Free tool · no signup

تحقّق من قائمة البروكسي قبل الزحف

الصق نقاط النهاية في Proxy Checker الخاص بنا: يختبر الاتصال وزمن الاستجابة ومستوى إخفاء الهوية ونوع عنوان IP الخارج بالجملة — اكشف البروكسيات الميتة أو المُصنّفة خطأً قبل أن يُهدر Scrapy إعادات المحاولة عليها.

Check my proxies →

سئمت رعاية القوائم المجانية؟ بوابة سكنية واحدة تستبدلها كلها — احصل على رصيد مجاني بحجم 500 ميجابايت →

الخلاصة

وجّه عناكبك إلى مجمّع حقيقي

جلسات سكنية متناوبة ولاصقة على بوابة واحدة. رصيد مجاني بحجم 500 ميجابايت للزحف به.

ابدأ النسخة التجريبية المجانية

لجميع منتجات الـ IP · مجموعة ضخمة من العُقد المتاحة في أي وقت

سجّل الآن واحصل على ما يصل إلى 100% استرداد على إعادة الشحن

يحصل المستخدمون الجدد على 500MB عند التسجيل، بالإضافة إلى مكافأة على أول عملية شحن. العرض لفترة محدودة.