مقدمه
خطای 403 Forbidden هنگام اسکریپینگ معمولاً یعنی سایت درخواست شما را بهعنوان یک ربات شناسایی کرده و صفحه را سرو نمیکند. در این راهنما مراحل تشخیص و چهار لایهٔ اصلی اصلاح را بهصورت عملی توضیح میدهم: تنظیم User-Agent، ارسال مجموعهٔ کامل هدرها، استفاده از پراکسیهای مسکونی/موبایل و تطبیق اثرانگشت TLS (JA3/JA4). پس از خواندن این مطلب باید بتوانید 403ها را سریعتر دیباگ کنید و برای هر حالت یک راهحل عملی پیادهسازی کنید.
مرور سریع — شش علت رایج و راهحل خلاصه
- اولین درخواست 403 ولی در مرورگر کار میکند: معمولاً User-Agent کتابخانه مشخص است → راهحل: از User-Agent مرورگر استفاده کنید.
- 403 حتی با User-Agent واقعی: هدرهای دیگر یا ترتیب هدرها ناهماهنگ است → راهحل: مجموعهٔ کامل هدرهای مرورگر را ارسال کنید.
- اول چند درخواست OK بعد 403: محدودسازی بر اساس IP → راهحل: کاهش نرخ درخواست، backoff نمایی، و چرخش پراکسی.
- 403 از همهٔ IPها حتی با هدرها: دامنهٔ پراکسی نشانهگذاری شده (datacenter) → راهحل: استفاده از پراکسی مسکونی یا موبایل.
- 403 از سیستمهایی مانند Cloudflare/DataDome: TLS/JA3 متفاوت است → راهحل: استفاده از کلاینتی که TLS را شبیهسازی میکند (مثلاً curl_cffi) یا پروکسی هوشمند.
- 403 بهخاطر نیاز به احراز هویت واقعی: URL واقعاً محافظتشده است → راهحل: احراز هویت (کوکی، OAuth، API key).
گام اول — تنظیم و چرخش User-Agent
کتابخانههای پیشفرض مثل requests یا axios اغلب User-Agent مشخصی دارند که بلافاصله شما را لو میدهد. برای درخواستهای کوچک میتوانید یک User-Agent ثابت شبیه مرورگر بگذارید، اما در اسکریپینگ در مقیاس باید مجموعهای از User-Agentها داشته باشید و آنها را بچرخانید.
مثال سادهٔ پیادهسازی در پایتون؛ این تابع برای هر درخواست یک User-Agent تصادفی برمیگرداند.
import requests
import random
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Mozilla/5.0 (iPad; CPU OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'
]
def fetch_with_random_ua(url):
headers = {'User-Agent': random.choice(USER_AGENTS)}
r = requests.get(url, headers=headers, timeout=15)
return r.status_code, r.text[:200]
توضیح:
- ورودی: url (رشته). خروجی: status_code و پیشنمایش متن.
- تابع خط اول لیست User-Agent را تعریف میکند، سپس برای هر فراخوانی یکی را انتخاب میکند.
- محدودیتها: اگر فقط User-Agent را تغییر دهید ولی بقیه هدرها ثابت باشند یا IP ضعیف باشد، سیستمهای پیشرفته هنوز شما را شناسایی میکنند.
گام دوم — بهینهسازی مجموعه هدرها و ترتیب آنها
سایتهای پیشرفته ترتیب هدرها، مقدار Accept, Accept-Language, Sec-Fetch-* و حتی Connection و Upgrade-Insecure-Requests را بررسی میکنند. تنها عوضکردن User-Agent کافی نیست؛ باید مجموعهای که مرورگر واقعی میفرستد را شبیهسازی کنید.
نمونهٔ هدر بهینهشده و نکات تطبیق:
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/124.0.0.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
}
r = requests.get('https://example.com', headers=HEADERS, timeout=15)
توضیح خطبهخط:
- User-Agent: باید با سایر هدرها سازگار باشد؛ مثلاً اگر User-Agent نشاندهنده macOS است، sec-ch-ua-platform یا مقدارهای مشابه باید منطبق باشند (در مثال بالا از مقادیر عمومی استفاده شده).
- Accept-Encoding و Connection رفتار انتقال را تعیین میکنند؛ ارسال اینها مشابه مرورگر شانس موفقیت را زیاد میکند.
- بهتر است هر هدر مجموعه را نیز بچرخانید نه فقط User-Agent.
گام سوم — تشخیص مشکل TLS (JA3/JA4) و راهحلها
اگر با هدرها و پراکسی مناسب هنوز از سیستمهایی مثل Cloudflare، DataDome یا Akamai 403 میگیرید، احتمال قوی مشکل اثرانگشت TLS است. اجزایی مانند لیست سویتها، ترتیب اکستنشنها و فریم HTTP/2 با الگوریتمهای تشخیص مقایسه میشوند و به یک hash مانند JA3/JA4 تبدیل میشوند.
برای تشخیص میتوانید از سرویسهای دیباگ TLS استفاده کنید (مثلاً نام سرویسهای عمومی برای آزمایش JA3 را جستجو کنید). اگر اثرانگشت شما مشابه python-requests است، باید کلاینت TLS را تغییر دهید یا از پروکسیهای هوشمند استفاده کنید.
راهحل در پایتون — نمونهٔ سریع با curl_cffi:
# pip install curl_cffi
from curl_cffi import requests
# impersonate باعث میشود TLS و HTTP/2 مشابه Chrome شود
r = requests.get('https://nowsecure.nl/', impersonate='chrome124', timeout=15)
print(r.status_code, len(r.text))
توضیح:
- کلاینت جایگزین از libcurl استفاده میکند که قادر است TLS handshake و ترتیب فریمها را مشابه مرورگر کند.
- پارامتر impersonate نسخهٔ مرورگر مورد نظر را مشخص میکند؛ نگهداری این تقلید بین نسخههای کروم نیازمند بهروزرسانیهای دورهای است.
- اگر نمیخواهید خودتان TLS fingerprinting را نگهداری کنید، و راهحل سادهتری میخواهید، از پروکسیهای هوشمند یا سرویسهای پروکسی که این کار را برای شما انجام میدهند استفاده کنید.
گام چهارم — پراکسیهای چرخان و مدیریت اعتبار IP
اگر سایت بر اساس IP محدودیت اعمال میکند یا محدودهٔ پراکسی شما بهعنوان دیتاسنتر شناخته شده، باید از یک استخر پراکسی چرخان متشکل از IPهای مسکونی یا موبایل استفاده کنید. پراکسی دیتاسنتر ارزانتر است اما احتمال بلاک شدن بیشتر است.
نمونهٔ ساده برای چرخش پراکسی با itertools.cycle:
import requests
from itertools import cycle
proxies_list = [
'http://user:pass@1.2.3.4:20000',
'http://user:pass@1.2.3.5:20000',
'http://user:pass@1.2.3.6:20000',
]
proxy_cycle = cycle(proxies_list)
for i in range(10):
proxy = next(proxy_cycle)
proxies = { 'http': proxy, 'https': proxy }
try:
r = requests.get('https://quotes.toscrape.com/page/1/', proxies=proxies, timeout=20)
print(i, r.status_code)
except Exception as e:
print('proxy failed', proxy, e)
نکات عملی:
- همیشه User-Agent و سایر هدرها را همراه هر پراکسی بچرخانید؛ پراکسی تنها بخشی از حل مسئله است.
- برای پایداری از retry با backoff نمایی استفاده کنید و وقتی سرویس Retry-After میدهد آن را رعایت کنید.
- پراکسی مسکونی گرچه گرانتر است اما برای سایتهای با سیستم امتیازدهی IP (rating) ضروری است.
نکات پیشرفته و راهحلهای چارچوبی
بسته به چارچوبی که استفاده میکنید، تغییرات خاصی لازم است:
- Scrapy: از middleware برای جایگزینی هدرها و مولد پراکسی استفاده کنید؛ برای TLS impersonation باید دانلود هندلر یا تغییر DOWNLOAD_HANDLERS داشته باشید.
- اگر سراغ مرورگر هدلس (Selenium / undetected-chromedriver) میروید، به تنظیم پراکسی در سطح مرورگر و مدیریت کوکیها دقت کنید؛ این روش هزینهبرتر اما مؤثر برای سایتهای مبتنی بر جاوااسکریپت و چالشهای Cloudflare است.
- FlareSolverr و مشابهها میتوانند بهعنوان پراکسی واسط که چالشهای جاوااسکریپت/Cloudflare را حل میکند عمل کنند؛ البته باید امنیت و مقیاسپذیری آنها را در نظر بگیرید.
نکات امنیتی، اخلاقی و قوانین
هرچند هدف فنی رفع 403 است، باید همیشه قوانین سایتها و قوانین محلی را رعایت کنید. بعضی از سایتها در قوانین استفادهشان اسکریپینگ را ممنوع کردهاند یا نیاز به توافق کتبی دارند. علاوه بر این، اجرای اسکریپینگ با نرخ بالا میتواند هزینهٔ سرور سایت هدف را افزایش دهد یا باعث اختلال برای کاربران واقعی شود.
جمعبندی
برای حل 403ها گامهای زیر را به ترتیب اجرا کنید: (1) User-Agent واقعی و چرخان، (2) مجموعهٔ کامل هدرهای مرورگر با سازگاری بین مقادیر، (3) پراکسیهای چرخان (ترجیحاً مسکونی/موبایل)، و (4) در صورت برخورد با Cloudflare/DataDome از کلاینتهایی که TLS را شبیهسازی میکنند (مثل curl_cffi) یا از پروکسی هوشمند استفاده کنید. همزمان از retry با backoff نمایی، رعایت Retry-After و چرخش همهٔ شناسهها (UA، هدرها، IP، و در صورت نیاز TLS) استفاده کنید تا در مقیاس نیز پایدار بمانید.
اگر خواستید میتوانم یک قالب آمادهٔ Scrapy یا یک اسکریپت پیادهسازی با مدیریت کامل retries، backoff و لاگینگ برای پروژهٔ شما آماده کنم.
آسان اسکریپ را در گوگل بهعنوان منبع ترجیحی انتخاب کن
مقالههای جدید ما زودتر و پررنگتر در نتایج گوگل و Discover برایت نمایش داده میشود. افزودن از تنظیمات گوگل





