مقدمه
در این مقاله یاد میگیرید چطور در پروژههای اسکریپینگ با Scrapy از شناسایی شدن جلوگیری کنید و رفتار هدر User-Agent را بهصورت امن و پایدار مدیریت کنید. این راهنما برای توسعهدهندگان پایتون در سطح متوسط طراحی شده و شامل روشهای ساده تا تولیدی (production-ready)، مثالهای کد و بهترین روشها برای چرخش، ذخیره و استفاده از هزاران User-Agent خواهد بود.
User-Agent چیست و چرا باید آن را مدیریت کنیم؟
User-Agent یک رشته در هدر درخواست HTTP است که اطلاعاتی دربارهٔ مرورگر، سیستمعامل و گاهی دستگاه ارسالکننده ارائه میدهد. سرورها از این هدر برای تحلیل رفتاری و اعمال قواعد ضدربات استفاده میکنند؛ در نتیجه ارسال مقدار پیشفرض یا ثابت میتواند باعث مسدود شدن درخواستها شود.
مثال یک User-Agent واقعی:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36نکته: مقدار پیشفرض Scrapy بهصورت Scrapy/VERSION (+https://scrapy.org) است که نشانهٔ واضحی از اجرا شدن اسکرِیپر میدهد و باید آن را تغییر داد.
روشها برای تعیین User-Agent در Scrapy
روش ۱ — تعیین User-Agent پیشفرض در settings.py
سادهترین روش، تنظیم USER_AGENT در فایل settings.py است. این مقدار در همهٔ درخواستهای تولیدشده توسط اسپایدر بهعنوان مقدار پیشفرض فرستاده میشود.
# settings.py
USER_AGENT = 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'مزایا:
- سادگی پیادهسازی.
معایب:
- همهٔ درخواستها یک User-Agent یکسان خواهند داشت که الگوی قابل ردیابی ایجاد میکند.
روش ۲ — اضافه کردن هدر به هر درخواست
میتوانید هدر را در متد start_requests یا هنگام ساخت Request مشخص کنید تا کنترل دقیقتری داشته باشید:
# myspider.py (نمونه)
def start_requests(self):
for url in self.start_urls:
yield Request(url=url, callback=self.parse,
headers={"User-Agent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"})توضیح ورودی/خروجی:
- ورودی: self.start_urls لیستی از URLها.
- خروجی: تولید کنندهٔ Request با هدر مشخص.
با وجود انعطاف، این روش هم اگر ثابت باقی بماند قابل تشخیص است؛ بنابراین بهتر است از چرخش (rotation) استفاده کنیم.
چرخش User-Agent (Rotation)
یک روش مرسوم این است که فهرستی از User-Agentها را داشته باشیم و برای هر درخواست یک مقدار تصادفی انتخاب کنیم. مثال ساده:
# myspider.py
import random
user_agent_list = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/93.0.4577.82 Safari/537.36',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 Version/14.0.3 Mobile/15E148 Safari/604.1',
'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)'
]
def start_requests(self):
for url in self.start_urls:
ua = random.choice(user_agent_list)
yield Request(url=url, callback=self.parse, headers={"User-Agent": ua})نکات فنی:
- تضمین کنید فهرست متنوع باشد (مقدارهای موبایل، دسکتاپ، مرورگرهای مختلف).
- این رویکرد ساده است اما نگهداری لیست بزرگ رویهای دستی است و تکرار کد در چند اسپایدر مشکلساز میشود.
چرا از Middleware استفاده کنیم؟
برای پروژههای واقعی بهتر است یک Downloader Middleware بسازید تا مسئول تعیین هدرها باشد. مزایا:
- مرکزیسازی منطق User-Agent
- قابلیت فعال/غیرفعال کردن با تنظیمات
- قابلیت کشینگ و بارگذاری از یک API یا فایل بدون تکرار در هر اسپایدر
نمونه Middleware: ادغام با یک API برای دریافت فهرست User-Agentها
در این نمونه ما از یک سرویس بیرونی (مثلاً یک Fake User-Agent API) برای دریافت فهرستی از User-Agentها استفاده میکنیم. توجه: برای امنیت و مقیاسپذیری بهتر است از فراخوانی همزمان در مسیرهای I/O سنگین خودداری کرده و لیست را یکبار در ابتدای اجرا دریافت و کش کنید.
# middlewares.py
from random import randint
import requests
class ScrapeOpsFakeUserAgentMiddleware:
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings)
def __init__(self, settings):
# تنظیمات از settings.py خوانده میشود
self.scrapeops_api_key = settings.get('SCRAPEOPS_API_KEY')
self.scrapeops_endpoint = settings.get('SCRAPEOPS_FAKE_USER_AGENT_ENDPOINT')
self.scrapeops_active = settings.get('SCRAPEOPS_FAKE_USER_AGENT_ENABLED', False)
self.num_results = settings.get('SCRAPEOPS_NUM_RESULTS')
self.user_agents_list = []
# دریافت فهرست User-Agentها در زمان ساخت Middleware
if self.scrapeops_active and self.scrapeops_api_key and self.scrapeops_endpoint:
self._get_user_agents_list()
def _get_user_agents_list(self):
payload = {'api_key': self.scrapeops_api_key}
if self.num_results is not None:
payload['num_results'] = self.num_results
# توجه: این فراخوانی blocking است؛ برای پروژههای بزرگ از نسخهٔ async یا کش استفاده کنید
response = requests.get(self.scrapeops_endpoint, params=payload)
json_response = response.json()
self.user_agents_list = json_response.get('result', [])
def _get_random_user_agent(self):
if not self.user_agents_list:
return None
random_index = randint(0, len(self.user_agents_list) - 1)
return self.user_agents_list[random_index]
def process_request(self, request, spider):
random_user_agent = self._get_random_user_agent()
if random_user_agent:
request.headers['User-Agent'] = random_user_agentتوضیح اجزا:
- from_crawler: متدی که Scrapy برای ساخت Middleware فراخوانی میکند؛ تنظیمات را به سازنده منتقل میکند.
- __init__: متد سازنده که کلید API، نقطهٔ انتهایی و وضعیت فعال بودن را میخواند و در صورت فعال بودن فهرست را بازیابی میکند.
- _get_user_agents_list: درخواست به API ارسال کرده و فهرست را در self.user_agents_list ذخیره میکند.
- _get_random_user_agent: یک مقدار تصادفی از لیست برمیگرداند.
- process_request: برای هر درخواست قبل از ارسال اجرا شده و هدر User-Agent را تنظیم میکند.
نکات توسعه و بهترین روشها:
- فراخوانی همزمان: استفاده از requests.get درون middleware ممکن است بلوکهکننده باشد. در پروژههای مقیاسپذیر از فراخوانیهای غیرهمزمان یا بارگذاری لیست در ابتدای فرآیند (قبل از شروع Crawl) استفاده کنید.
- کشینگ و fallback: اگر API پاسخ نداد، لیست محلی یا مقدار پیشفرض داشته باشید.
- محدودیت نرخ و خطایابی: برای فراخوانی API از retry، timeout و بررسی کد وضعیت HTTP استفاده کنید.
- پایبندی به قوانین وب: پیش از اسکریپ کردن مطمئن شوید قوانین سایت را نقض نمیکنید—از robots.txt و سیاستهای سرویسدهنده مطلع باشید.
تنظیمات فعالسازی در settings.py
برای فعالکردن این middleware کافی است در فایل settings.py مقدار کلیدها را قرار دهید و middleware را اضافه کنید:
# settings.py
SCRAPEOPS_API_KEY = 'YOUR_API_KEY'
SCRAPEOPS_FAKE_USER_AGENT_ENABLED = True
SCRAPEOPS_FAKE_USER_AGENT_ENDPOINT = 'YOUR_FAKE_UA_API_ENDPOINT'
SCRAPEOPS_NUM_RESULTS = 50
DOWNLOADER_MIDDLEWARES = {
'YOUR_PROJECT_NAME.middlewares.ScrapeOpsFakeUserAgentMiddleware': 400,
}در اسپایدر هم میتوانید با custom_settings این گزینهها را بهصورت محلی فعال کنید.
ترکیب با پراکسی، کنترل نرخ و مدیریت خطا
مدیریت User-Agent تنها بخشی از راهکار جلوگیری از بلاک شدن است. نکات تکمیلی:
- پراکسی: از پراکسی روتیتشده برای توزیع درخواستها بین آدرسهای IP متفاوت استفاده کنید.
- کنترل نرخ (rate limiting): بین درخواستها تأخیر منطقی قرار دهید (مثلاً DOWNLOAD_DELAY و concurrency محدود).
- ردیابی و لاگینگ: وضعیت موفقیت/ خطا برای هر User-Agent و IP را لاگ کنید تا الگوهای با بلاک بالا را حذف یا کاهش دهید.
- هدرهای تکمیلی: علاوه بر User-Agent، هدرهایی مثل Accept-Language و Accept-Encoding را نیز به شکلی طبیعی تنظیم کنید.
خطرات و نکات امنیتی
ارسال دادههای حساس به سرویسهای ثالث (مثل کلید API یا دادهٔ پاسخ) نیازمند احتیاط است. نکات:
- کلیدهای API را در کنترل نسخه قرار ندهید؛ از متغیرهای محیطی یا سرویسهای secret management استفاده کنید.
- کنترل دسترسی به لاگها: لاگها ممکن است User-Agentها و URLها را ثبت کنند؛ مراقب افشای اطلاعات باشید.
- قوانین و اخلاق: همواره قوانین سایت و قوانین محلی را رعایت کنید؛ اسکریپینگ بدون مجوز میتواند پیگرد قانونی داشته باشد.
جمعبندی
برای اجرای موفق اسکریپینگ با Scrapy باید User-Agentها را مدیریت و چرخش دهید تا شناسایی و مسدودیت کاهش یابد. برای پروژههای کوچک میتوان از تنظیمات ساده یا انتخاب تصادفی از یک لیست محلی استفاده کرد، اما برای پروژههای تولیدی استفاده از یک middleware متمرکز، کشینگ، مدیریت خطا و ترکیب با پراکسی و کنترل نرخ، ضروری است. همچنین مطمئن شوید کلیدهای API و تنظیمات حساس را امن نگه دارید و از الگوهای غیرمسئولانه اجتناب کنید.





