Scrapy Proxy Middleware: راهنمای کامل پیکربندی (۲۰۲۶)

منتشر شده در 4 ژوئن 2026 · زمان مطالعه ≈ 9 دقیقه

Scrapy در سال ۲۰۲۶ همچنان اسب‌بارکش کرال‌های production است — و همچنان فریم‌ورکی که راه‌اندازی پروکسی در آن بیشترین سردرگمی را ایجاد می‌کند، چون چهار جای مختلف برای وصل کردن پروکسی وجود دارد و سه‌تای آن‌ها برای اکثر پروژه‌ها اشتباه است. این راهنما همان یک گزینه درست را به شما می‌دهد: یک middleware سفارشی کوچک با مسیریابی به‌ازای هر درخواست، sticky session ها، تشخیص بن، و رفتار retry معقول.

اگر به‌جای آن از requests/httpx/aiohttp ساده استفاده می‌کنید، چگونه پروکسی‌ها را در پایتون چرخش دهیم را ببینید. این مقاله مخصوص Scrapy است.

نسخه ۳۰ ثانیه‌ای

برای یک گیت‌وی مسکونی چرخشی، حداقل راه‌اندازی قابل‌اجرا یک خط به‌ازای هر درخواست است — بدون نیاز به 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 خروجی را برای شما چرخش می‌دهد. اگر فقط همین را لازم دارید، همین‌جا بایستید. بقیه این راهنما برای وقتی است که به کنترل نیاز دارید: sticky session ها، مسیریابی کشوری، چرخش آگاه از بن، و تنظیم همروندی.

یک Proxy Middleware برای Production

این را در middlewares.py بگذارید. به‌ازای هر دامنه sticky session اختصاص می‌دهد، هنگام بن چرخش می‌دهد، و هر درخواست را برچسب می‌زند تا بتوانید دیباگ کنید کدام session چه چیزی را گرفته است:

import random
import string

GATEWAY = "us.jibaoproxy.com:913"
USERNAME = "USERNAME"          # در پروژه‌های واقعی به settings.py / env منتقل کنید
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()

و یک downloader middleware همراه که بن‌ها را تشخیص می‌دهد و روی یک session تازه دوباره تلاش می‌کند:

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

اولویت اهمیت دارد: proxy middleware باید پیش از HttpProxyMiddleware خود Scrapy (مقدار 750) اجرا شود، پس هر چیزی زیر 750 کار می‌کند؛ مقدار 350 آن را زود و قابل‌پیش‌بینی نگه می‌دارد.

Sticky در برابر Rotating: کدام حالت برای کدام spider

نوع کرالحالتپیاده‌سازی
برداشت صفحات بدون stateچرخشیusername ساده، گیت‌وی به‌ازای هر درخواست چرخش می‌دهد
ورود + کرال پشت احراز هویتsticky به‌ازای هر حساب-session-{account_id}، هرگز وسط ورود چرخش ندهید
لیست‌های پُر صفحه‌بندیsticky به‌ازای دامنه، چرخش هنگام بنهمان middleware بالا
قیمت‌گذاری مخصوص جغرافیاچرخشی + پین کشورپارامترهایی به سبک USERNAME-country-de

بررسی عمیق‌تر این مصالحه: Sticky در برابر Rotating Proxy Sessions.

تنظیمات همروندی که بنتان نمی‌کنند

پیش‌فرض‌های Scrapy برای کرالِ مؤدبانه تک‌IP تنظیم شده‌اند. پشت یک استخر چرخشی می‌توانید خیلی محکم‌تر فشار بیاورید — اما محدودیت‌های به‌ازای هر دامنه همچنان مهم‌اند چون هدف رفتار تجمیعی را می‌بیند:

# settings.py - نقطه شروع معقول پشت یک استخر مسکونی
CONCURRENT_REQUESTS = 64
CONCURRENT_REQUESTS_PER_DOMAIN = 8     # چیزی که هدف تجربه می‌کند
DOWNLOAD_DELAY = 0.25                  # jitter به‌ازای هر slot اعمال می‌شود
RANDOMIZE_DOWNLOAD_DELAY = True        # 0.5x-1.5x تأخیر
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 6.0
DOWNLOAD_TIMEOUT = 30
ROBOTSTXT_OBEY = True

CONCURRENT_REQUESTS_PER_DOMAIN را فقط بعد از آنکه نرخ ۴۰۳ خود را در سطح فعلی برای چند هزار درخواست تماشا کردید بالا ببرید. بردن از 8 به 32 با این بهانه که «پروکسی‌ها به‌هرحال چرخش می‌کنند» همان راهی است که آدم‌ها روی retry ها گیگابایت می‌سوزانند.

انضباط پهنای باند (گیگابایت مسکونی یعنی پول)

خطاهای رایج و معنای واقعی‌شان

407 Proxy Authentication Required

اطلاعات احراز هویت به پروکسی نرسیده. آن‌ها را در URL (http://user:pass@host:port) داخل meta["proxy"] بگذارید — Scrapy آن را پارس می‌کند و Proxy-Authorization را برایتان ست می‌کند. ست کردن دستی هدر به‌علاوه استفاده از اطلاعات احراز هویت در URL باعث رفتار عجیب double-auth می‌شود؛ یکی را انتخاب کنید.

TunnelError: Could not open CONNECT tunnel

تقریباً همیشه host/port غلط تایپ‌شده است، یا هدف HTTPS از طریق endpoint ای که روی آن پورت اجازه CONNECT نمی‌دهد. اول با curl -x بیرون از Scrapy بررسی کنید.

spider ده دقیقه کار می‌کند، بعد همه چیز ۴۰۳ می‌شود

sticky session شما بیش از حد زنده مانده، یا نرخ به‌ازای دامنه‌تان خیلی داغ است. middleware آگاه از بنِ بالا حالت اول را مدیریت می‌کند؛ برای حالت دوم CONCURRENT_REQUESTS_PER_DOMAIN را پایین بیاورید. اگر هدف JA4 چک می‌کند، خودِ استک TLS مربوط به Scrapy ممکن است لو دهنده باشد — ببینید توضیح JA3/JA4 که چرا هیچ پروکسی‌ای آن را درست نمی‌کند.

ابزار رایگان · بدون ثبت‌نام

پیش از کرال، فهرست پروکسی‌تان را اعتبارسنجی کنید

endpoint ها را در Proxy Checker ما بچسبانید: به‌صورت انبوه اتصال، تأخیر، سطح ناشناسی و نوع IP خروجی را تست می‌کند — پروکسی‌های مرده یا برچسب‌اشتباه را قبل از آنکه Scrapy روی retry ها هدر دهد بگیرید.

پروکسی‌هایم را چک کن →

از پرستاری فهرست‌های رایگان خسته شده‌اید؟ یک گیت‌وی مسکونی جای همه آن‌ها را می‌گیرد — ۵ دلار اعتبار رایگان بگیرید →

جمع‌بندی

spider هایتان را به یک استخر واقعی وصل کنید

session های مسکونی چرخشی و sticky روی یک گیت‌وی. ۵ دلار اعتبار رایگان برای کرال کردن.

شروع تست رایگان

برای همه محصولات IP · هزاران نود همیشه در دسترس

همین حالا ثبت‌نام کنید و تا ۱۰۰٪ کش‌بک شارژ بگیرید

کاربران جدید با ثبت‌نام 500MB هدیه می‌گیرند، به‌علاوه بونوس اولین شارژ. پیشنهاد محدود.