

خطای 403 Forbidden در هنگام اسکریپینگ با Scrapy یکی از رایجترین و در عین حال گمراهکنندهترین خطاهاست؛ لاگها معمولاً فقط میگویند «Ignored response <403 ...>» و دلیل دقیق را نشان نمیدهند. در این راهنما قدمبهقدم روشهای تشخیص و رفع خطای 403 را با تمرکز روی Scrapy، تنظیم هدرها، چرخش یوزر-اجنت و پراکسیها توضیح میدهیم. پس از خواندن این مقاله شما خواهید توانست:
قبل از هر راهحلی باید تشخیص دهیم 403 ناشی از چیست. دو حالت معمول وجود دارد:
روشهای تشخیص سریع:
بهصورت پیشفرض میدلویر خطای HTTP در Scrapy پاسخهای 403 را از مسیر معمول callback حذف میکند. برای دیدن بدنه و عیبیابی باید اجازه دهید این پاسخها در callback ظاهر شوند. دو روش متداول:
class MySpider(scrapy.Spider):
name = 'myspider'
# حالا پاسخهای 403 به callback شما فرستاده میشوند
handle_httpstatus_list = [403]
def start_requests(self):
yield scrapy.Request('https://example.com/protected', callback=self.parse)
def parse(self, response):
if response.status == 403:
# اکنون میتوانید body را ببینید و آنالیز کنید
self.logger.info('Got 403 body: %s', response.text[:200])
توضیح کوتاه: handle_httpstatus_list یک لیست از کدهای HTTP است که Scrapy نباید آنها را نادیده بگیرد. پس از این، در parse میتوانید response.status و response.text را بررسی کنید.
ارسال درخواستها با فواصل زمانی ثابت باعث شناسایی آسان میشود. Scrapy تنظیمات سادهای برای این دارد:
# settings.py
DOWNLOAD_DELAY = 2 # به ثانیه
# به طور پیشفرض RANDOMIZE_DOWNLOAD_DELAY = True است
شرح: اگر DOWNLOAD_DELAY روی 2 قرار گیرد، Scrapy بین 1 و 3 ثانیه بهطور تصادفی بین هر درخواست صبر میکند (حدوداً 0.5 تا 1.5 برابر مقدار). برای پروژههای کوچک، افزایش این مقدار احتمال بن شدن را کاهش میدهد.
اگر سرور از روی User-Agent شما را شناسایی کند، سادهترین راه ارسال یک یوزر-اجنت واقعی یا بهتر: چرخش بین یک مجموعهٔ بزرگ از یوزر-اجنتهاست.
روش ساده (در settings.py):
# settings.py
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0'
این روش برای تراکنشهای کوچک کافی است اما اگر همه درخواستها یک یوزر-اجنت داشته باشند، تشخیص دشوار نیست.
روش بهتر: استفاده از یک middleware که یوزر-اجنت را برای هر درخواست تصادفی میکند. نمونههای متداول در اکوسیستم Scrapy وجود دارد؛ نصب معمولاً با pip است:
pip install scrapy-fake-useragent
نمونهٔ پیکربندی (در settings.py):
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400,
'scrapy_fake_useragent.middleware.RetryUserAgentMiddleware': 401,
}
FAKEUSERAGENT_PROVIDERS = [
'scrapy_fake_useragent.providers.FakeUserAgentProvider',
'scrapy_fake_useragent.providers.FakerProvider',
'scrapy_fake_useragent.providers.FixedUserAgentProvider',
]
# USER_AGENT را میتوان به عنوان fallback نگه داشت
USER_AGENT = ''
شرح: اولین middleware یوزر-اجنت تصادفی را انتخاب میکند، و در صورت خطا middleware retry مربوط، دوباره با یوزر-اجنت جدید تلاش میکند.
بسیاری از سیستمهای ضد-بات نه تنها User-Agent را نگاه میکنند، بلکه سازگاری سایر هدرها را با آن بررسی میکنند (مثل Accept، Accept-Language، Sec-Fetch-* و غیره). ارسال هدرهای یک مرورگر واقعی شانس بلاک شدن را کاهش میدهد.
مثال: افزودن هدرهای بهدستآمده از یک مرورگر معمول:
# example_spider.py
class BookSpider(scrapy.Spider):
name = 'bookspider'
url_list = ['http://books.toscrape.com']
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
}
def start_requests(self):
for url in self.url_list:
yield scrapy.Request(url=url, callback=self.parse, headers=self.HEADERS)
def parse(self, response):
# پردازش معمول
pass
توضیح: اینجا هدرها را روی هر درخواست میفرستیم تا رفتار درخواستها به نمونههای واقعی نزدیک شود. مهم است که هدرها با یوزر-اجنت همخوانی داشته باشند؛ یعنی اگر یوزر-اجنت یک مرورگر Chrome است، هدرهای Chrome را ارسال کنید.
اگر آیپیهای شما قبلاً بن شده باشند یا حجم درخواستها بالا باشد، نیاز به پراکسی چرخشی دارید. یکی از middlewareهای رایج برای Scrapy، scrapy-rotating-proxies است.
pip install scrapy-rotating-proxies
نمونهٔ پیکربندی:
# settings.py
ROTATING_PROXY_LIST = [
'proxy1.example:8000',
'proxy2.example:8031',
'proxy3.example:8032',
]
DOWNLOADER_MIDDLEWARES.update({
'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'rotating_proxies.middlewares.BanDetectionMiddleware': 620,
})
شرح: با این پیکربندی هر درخواست از یکی از پراکسیهای لیست عبور میکند و میدلویری که شناسایی بن را انجام میدهد، پراکسیهای مشکلدار را حذف یا دور میزند.
Cloudflare یکی از رایجترین سرویسهای ضد-بات است. وقتی با صفحه یا جوابهایی مواجه شدید که شامل challenge یا ریدایرکت جاوااسکریپتی هستند، احتمالاً Cloudflare در کار است. راهکارها شامل استفاده از پراکسیهایی که این چالشها را حل میکنند، یا سرویسهای مخصوص bypass است. معمولاً سطوح امنیتی مختلفی وجود دارد (از کم تا بالا) که به ترتیب منابع و هزینهٔ بیشتری برای عبور نیاز دارند؛ انتخاب درست بستگی به سطح بررسی و حجم درخواست شما دارد.
معمولاً باید چند روش را همزمان به کار ببرید: تأخیر تصادفی، یوزر-اجنت چرخشی، هدرهای بهینه و پراکسی چرخشی. یک جریان کاری نمونه:
خطای 403 در Scrapy معمولاً یا به دلیل نیاز به احراز هویت است یا به این خاطر که وبسایت شما را بهعنوان یک ربات شناسایی کرده است. برای عیبیابی ابتدا بدنهٔ پاسخ را ببینید، سپس از تکنیکهایی مثل تأخیر تصادفی، یوزر-اجنتهای چرخشی، بهینهسازی هدرها و پراکسیهای چرخشی استفاده کنید. اجرای همزمان این روشها در محیط تولید، همراه با لاگبرداری و مانیتورینگ، بهترین شانس را برای کاهش 403 و پایدارسازی اسکریپینگ شما فراهم میکند.


