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 ها، مسیریابی کشوری، چرخش آگاه از بن، و تنظیم همروندی.
این را در 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 آن را زود و قابلپیشبینی نگه میدارد.
| نوع کرال | حالت | پیادهسازی |
|---|---|---|
| برداشت صفحات بدون 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 ها گیگابایت میسوزانند.
IMAGES_STORE را طوری تنظیم کنید که از طریق یک پروکسی دیتاسنتر یا اتصال مستقیم بگیرند اگر CDN اهمیتی نمیدهد — مدیا بیشترِ گیگابایت شماست و بهندرت محافظت میشود.HTTPCACHE_ENABLED = True در حالی که روی parser ها کار میکنید، پاسخها را بهجای واکشی مجدد از استخر، از روی دیسک بازپخش میکند. این یک تنظیم معمولاً قبض پهنای باند یک پروژه را نصف میکند.COMPRESSION_ENABLED = True را روشن نگه دارید (پیشفرض) — HTML فشردهشده با gzip روی سیم ۵ تا ۱۰ برابر کوچکتر است.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 بررسی کنید.
sticky session شما بیش از حد زنده مانده، یا نرخ بهازای دامنهتان خیلی داغ است. middleware آگاه از بنِ بالا حالت اول را مدیریت میکند؛ برای حالت دوم CONCURRENT_REQUESTS_PER_DOMAIN را پایین بیاورید. اگر هدف JA4 چک میکند، خودِ استک TLS مربوط به Scrapy ممکن است لو دهنده باشد — ببینید توضیح JA3/JA4 که چرا هیچ پروکسیای آن را درست نمیکند.
meta["proxy"] با URL گیتوی کل راهاندازی است.session های مسکونی چرخشی و sticky روی یک گیتوی. ۵ دلار اعتبار رایگان برای کرال کردن.
شروع تست رایگانکاربران جدید با ثبتنام 500MB هدیه میگیرند، بهعلاوه بونوس اولین شارژ. پیشنهاد محدود.