رایجترین سطرهای لاگ یک اسکرپر پاسخهای موفق 200 نیستند، بلکه کدهای 403، 429 و 503 هستند که میان آنها سر برمیآورند. هر یک از این کدها مشکل متفاوتی را توصیف میکند و واکنش متفاوتی لازم دارد. اسکریپتی که با همه یکسان رفتار کند و «سه بار دوباره تلاش کن»، یا بر مسدودسازی دائمی پافشاری میکند و اوضاع را بدتر میکند، یا در مشکلی موقت که پس از چند ثانیه برطرف میشد داده از دست میدهد.
در این نوشته توضیح میدهیم کدهای وضعیت HTTP چگونه خوانده میشوند، رایجترین کدهای اسکرپینگ یعنی 403، 407، 429 و 503 چه معنایی دارند و چه علتهای محتملی پشت آنهاست. سپس به 502، 504 و خطاهای اتصال و شیوه به کار بردن هدر Retry-After میپردازیم و در یک جدول میآوریم کدام کدها را دوباره تلاش کنیم و در کدام متوقف شویم. در پایان نمونهای آزموده در پایتون آمده که این تصمیمها را اجرا میکند.
کدهای وضعیت HTTP چگونه خوانده میشوند؟
هر پاسخ HTTP با یک کد وضعیت سهرقمی آغاز میشود. رقم نخست دسته پاسخ را تعیین میکند. کدها در بخش 15 از RFC 9110 تعریف شدهاند؛ فهرست کدهای وضعیت MDN هم برای هر کد توضیح کوتاه و نمونه دارد.
| دسته | معنا | در زبان اسکرپینگ |
|---|---|---|
1xx | اطلاعاتی، درخواست در جریان است | در عمل نمیبینید |
2xx | موفقیت | صفحه رسید، اما محتوایش را هم بررسی کنید |
3xx | هدایت | آدرس عوض شده یا به صفحه ورود فرستاده شدهاید |
4xx | مشکل سمت کلاینت | درخواست، هویت یا سرعت شما مشکل دارد |
5xx | مشکل سمت سرور | سرور، دروازه یا پروکسی نتوانسته درخواست را کامل کند |
مهمترین تمایز برای اسکرپینگ این است: بیشتر کدهای 4xx با فرستادن دوباره عین همان درخواست همان نتیجه را میدهند. باید چیزی در درخواست را تغییر دهید. بیشتر کدهای 5xx موقتیاند و همان درخواست پس از مدتی انتظار ممکن است موفق شود. 429 استثناست: کدی از دسته 4xx است اما با انتظار برطرف میشود.
هشدار دیگری که مستقل از کد صادق است: دیدن 200 به این معنا نیست که داده مورد نظرتان را گرفتهاید. سامانههای محافظت ربات اغلب صفحه بررسی را با کد 200 برمیگردانند. پیش از موفق دانستن پاسخ، بررسی کنید صفحه عنصر مورد انتظاری (عنوان محصول، فیلد قیمت) داشته باشد.
کد 403 Forbidden یعنی چه؟
403 میگوید سرور درخواست شما را فهمیده اما از انجام آن سر باز میزند. بر اساس RFC 9110 سرور مجبور نیست دلیل را بگوید. تفاوت آن با 401 Unauthorized که برای نبودن احراز هویت به کار میرود این است که در 403 افزودن اطلاعات ورود معمولاً نتیجه را تغییر نمیدهد.
علتهای محتمل 403 در اسکرپینگ:
- شهرت یا نوع IP. سایتهایی که ترافیک آدرسهای دیتاسنتر را محدود میکنند مستقیم به این آدرسها
403میدهند. - محدودیت جغرافیایی. سایت فقط از برخی کشورها اجازه دسترسی میدهد.
- هدرهای ناقص یا ناسازگار. مقدار پیشفرض
User-Agentکتابخانه یا ترکیب هدری که هیچ مرورگری نمیفرستد. - قاعده فایروال برنامه وب (WAF). درخواست با یک قاعده امنیتی تطبیق یافته است.
- مسدودسازی به دلیل ترافیک قبلی شما. آدرس IP که مدت طولانی از محدودیت نرخ بگذرد، پس از مدتی ممکن است بهجای
429پاسخ403بگیرد. - صفحهای که واقعاً مجوز لازم دارد. مسیری که بدون ورود قابل دسترسی نیست.
چه باید کرد: همان درخواست را بیدرنگ دوباره نفرستید. آدرس را با همان IP در مرورگر واقعی باز کنید. اگر در مرورگر باز شود مشکل در خود درخواست شماست (هدر، سرعت)؛ اگر نه، مشکل IP یا موقعیت است. علتهای مسدودسازی و راهحلهای مشروع را در وب اسکرپینگ بدون مسدود شدن مفصل توضیح دادهایم.
کد 407 Proxy Authentication Required یعنی چه؟
407 نه از سایت مقصد، بلکه از سرور پروکسی میآید. یعنی پروکسی میخواهد پیش از ارسال درخواست به مقصد هویت خود را ثابت کنید. پس وقتی 407 میبینید، باید اتصال پروکسی خود را بررسی کنید نه قواعد سایت مقصد را.
علتهای محتمل:
- نام کاربری یا رمز نادرست است.
- رمز نویسههای خاصی مانند
@،:یا/دارد که درون آدرس پروکسی کدگذاری نشدهاند (@به%40). - روش لیست سفید IP به کار میرود اما آدرس IP مبدأ اتصال در فهرست نیست.
- کتابخانه اطلاعات ورود درون آدرس را به پروکسی نمیفرستد.
- موجودی یا سهمیه ترافیک حساب تمام شده است.
کتابخانهها 407 را به شکلهای متفاوتی نشان میدهند. در درخواستهای HTTPS تونل پروکسی ساخته نمیشود، پس اغلب بهجای شیء پاسخ استثنا میگیرید: Python Requests خطای ProxyError میدهد و HTTPX هم ProxyError؛ در Node.js، Axios خطایی با کد وضعیت 407 برمیگرداند. تفاوت کتابخانهها را با نمونههای آزموده در استفاده از پروکسی در Node.js نشان دادهایم.
چه باید کرد: دوباره تلاش نکنید؛ پیکربندی را اصلاح کنید. در خروجی curl -v بررسی کنید هدر Proxy-Authorization فرستاده میشود یا نه؛ خود فرمان در استفاده از پروکسی با cURL آمده است. دو روش احراز هویت و تشخیص 407 را گامبهگام در احراز هویت پروکسی: نام کاربری و رمز یا لیست سفید IP بررسی کردهایم.
کد 429 Too Many Requests یعنی چه؟
429 میگوید در بازه زمانی مشخصی درخواستهای بیش از حد فرستادهاید. این کد در بخش 4 از RFC 6585 تعریف شده و آنجا آمده که سرور میتواند همراه پاسخ هدر Retry-After بفرستد تا بگوید چقدر صبر کنید.
اینکه محدودیت نرخ بر اساس چه چیزی شمرده میشود از سایتی به سایت دیگر متفاوت است:
- بر اساس آدرس IP: درخواستهای یک آدرس جمع زده میشوند.
- بر اساس نشست یا کوکی: درخواستهای یک نشست جمع زده میشوند؛ عوض کردن IP شمارنده را صفر نمیکند.
- بر اساس کلید API: در APIهای رسمی محدودیت معمولاً به کلید بسته است.
- بر اساس endpoint: در endpointهای سنگین مانند صفحههای جستوجو، محدودیت از صفحههای محصول کمتر است.
برخی APIها سهمیه باقیمانده را با هدرهایی مانند X-RateLimit-Remaining و X-RateLimit-Reset گزارش میکنند. این نامهای هدر استاندارد نیستند و از سرویسی به سرویس دیگر فرق میکنند؛ مستندات API مورد استفاده خود را ببینید.
چه باید کرد: اگر Retry-After هست، همان مدت صبر کنید. اگر نه، backoff نمایی به کار ببرید: در تلاش نخست 1 ثانیه، سپس 2، 4 و 8 ثانیه و به هر انتظار جزئی تصادفی بیفزایید. همزمان همروندی را کم کنید؛ اگر صبر کنید و با همان سرعت ادامه دهید، بهزودی دوباره 429 میگیرید.
کد 503 Service Unavailable یعنی چه؟
503 میگوید سرور اکنون نمیتواند درخواست را پاسخ دهد، اما وضعیت موقتی است. نگهداری، بار بیش از حد یا از کار افتادن سرور برنامه پشت آن علتهای معمولاند. بر اساس RFC 9110 سرور میتواند با Retry-After بگوید کی دوباره تلاش کنید.
در اسکرپینگ، 503 دو چهره متفاوت دارد:
- بار واقعی یا نگهداری. سایت به همه
503برمیگرداند. جز صبر کاری نمیتوان کرد. - مسدودسازی موقت از سوی محافظت ربات. برخی سامانههای محافظتی به ترافیک مشکوک
503همراه صفحه بررسی برمیگردانند. اگر بدنه صفحه بررسی دارد، مشکل بار سرور نیست بلکه خود ترافیک شماست.
چه باید کرد: دو حالت را با نگاه به بدنه از هم جدا کنید. در بار واقعی با Retry-After یا backoff نمایی صبر کنید. اگر صفحه بررسی دیدید، بهجای تلاش دوباره سرعت و هویت کلاینت خود را بازبینی کنید.
خطاهای 502، 504 و اتصال
اسکرپری که از پروکسی استفاده میکند، افزون بر پاسخهای سایت مقصد، با خطاهای خود پروکسی هم روبهرو میشود. این کدها برای یافتن حلقه مشکلدار زنجیره مهماند.
500 Internal Server Error: در برنامه مقصد خطایی رخ داده است. گاهی ویژه یک صفحه است. میتوان یکی دو بار دوباره تلاش کرد؛ اگر ادامه یافت، از آن آدرس بگذرید.502 Bad Gateway: دروازهای میانی (پروکسی، CDN یا ریورس پروکسی) از سرور پشتش پاسخ معتبری نگرفته است. در زمینه پروکسی ممکن است یعنی پروکسی به مقصد نرسیده است. پس از انتظاری کوتاه دوباره تلاش کنید.504 Gateway Timeout: دروازه در زمان لازم از سرور پشتش پاسخ نگرفته است. وقتی مقصد کند است یا نقطه خروج پروکسی دور است دیده میشود. دوباره تلاش کنید.408 Request Timeout: سرور در انتظار کامل شدن درخواست به timeout رسیده است. دوباره تلاش کنید.- خطاهای اتصال: کدی نیست و کلاینت استثنا میدهد. اتصال رد شد (
ECONNREFUSED)، اتصال بازنشانی شد (ECONNRESET)، timeout، میزبان یافت نشد (ENOTFOUND) و مانند اینها. اگر آدرس یا پورت نادرست باشد هیچ تلاش دوبارهای درستش نمیکند؛ در وضعیتهای موقتی مانند قطعی شبکه، تلاش دوباره کمک میکند. - کدهای ویژه CDN: برخی CDNها کدهای غیراستاندارد در بازه
520تا526به کار میبرند. اینها معمولاً مشکلات اتصال میان CDN و سرور خود سایت را توصیف میکنند.
404 Not Found و 410 Gone میگویند صفحه وجود ندارد. بهجای تلاش دوباره، آدرس را از فهرست خود حذف کنید؛ اگر ناگهان 404 فراوان دیدید، ممکن است ساختار URL سایت عوض شده باشد.
Retry-After را چگونه به کار ببریم؟
هدر Retry-After به دو شکل میآید:
- تعداد ثانیه:
Retry-After: 120یعنی 120 ثانیه صبر کنید. - تاریخ HTTP:
Retry-After: Wed, 16 Sep 2026 07:28:00 GMTیعنی پس از این تاریخ تلاش کنید.
چهار قاعده برای به کار بردن درست آن:
- اگر هدر هست، حدس نزنید؛ از آن پیروی کنید. زمانی که سرور میدهد از هر backoff که محاسبه کنید دقیقتر است.
- سقف بگذارید. اگر هدر زمان طولانی مانند چند ساعت بگوید، بهجای معطل کردن اسکریپت در آن مدت، آدرس را به اجرای بعدی بسپارید.
- انتظار را بر سایت اعمال کنید نه فقط بر آن درخواست. اگر درخواستی را که
429گرفته نگه دارید اما موازی به فرستادن درخواستهای دیگر به همان سایت ادامه دهید، محدودیت همچنان نقض میشود. - شکل تاریخ را هم تحلیل کنید. کدی که فقط عدد انتظار دارد، هدری را که به شکل تاریخ بیاید نادیده میگیرد. تابع پایتونی را که هر دو شکل را مدیریت میکند در وب اسکرپینگ بدون مسدود شدن آوردهایم.
کدام کدها را دوباره تلاش کنیم و در کدام متوقف شویم؟
| کد | معنا | علت محتمل در اسکرپینگ | چه باید کرد |
|---|---|---|---|
200 | موفقیت | صفحه رسید، اما ممکن است صفحه بررسی باشد | وجود عنصر مورد انتظار در محتوا را بررسی کنید |
301 یا 302 | هدایت | آدرس عوض شده یا به صفحه ورود فرستاده شدهاید | هدایت را دنبال کنید؛ اگر صفحه ورود است نشست را بررسی کنید |
400 | درخواست نادرست | پارامتر یا بدنه خراب | متوقف شوید و درخواست را اصلاح کنید |
401 | احراز هویت لازم | ورود یا کلید API ناقص | متوقف شوید و اطلاعات ورود بیفزایید |
403 | رد شد | نوع IP، موقعیت، هدر، قاعده WAF | متوقف شوید و تشخیص دهید |
404 یا 410 | صفحه یافت نشد | محصول حذفشده، ساختار URL تغییرکرده | از فهرست حذف کنید |
407 | احراز هویت پروکسی لازم | رمز نادرست، نویسه کدگذارینشده، لیست سفید | متوقف شوید و تنظیمات پروکسی را اصلاح کنید |
408 | timeout درخواست | اتصال کند | دوباره تلاش کنید |
429 | درخواست بیش از حد | عبور از محدودیت نرخ | به اندازه Retry-After صبر کنید، کند شوید |
500 | خطای سرور | خطا در برنامه مقصد | چند بار تلاش کنید، سپس بگذرید |
502 | دروازه نادرست | پروکسی یا CDN به مقصد نرسید | پس از انتظاری کوتاه دوباره تلاش کنید |
503 | خدمت در دسترس نیست | بار، نگهداری یا صفحه محافظت | بدنه را بررسی کنید؛ با Retry-After صبر کنید |
504 | timeout دروازه | مقصد کند، نقطه خروج دور | دوباره تلاش کنید؛ در صورت نیاز موقعیت نزدیکتر |
| خطای اتصال | بیپاسخ | قطعی شبکه، آدرس یا پورت نادرست | اگر موقتی است دوباره تلاش کنید؛ اگر دائمی است پیکربندی را اصلاح کنید |
نمونه: تلاش دوبارهای که بر اساس کد وضعیت تصمیم میگیرد
نمونه زیر کلاینت ناهمگام HTTPX را به کار میبرد. در کدهای 408، 429 و 5xx به هدر Retry-After احترام میگذارد؛ اگر هدر نباشد backoff نمایی با جزء تصادفی اعمال میکند. در کدهایی که تلاش دوباره نتیجه را تغییر نمیدهد، مانند 403، 404 و 407، با برانگیختن استثنای جداگانه متوقف میشود. خطاهای احراز هویت پروکسی (که در درخواستهای HTTPS بهصورت ProxyError میرسند) هم در همین گروهاند.
import asyncio
import random
import httpx
RETRY_STATUS = {408, 429, 500, 502, 503, 504}
STOP_STATUS = {400, 401, 403, 404, 407, 410}
class StopScraping(Exception):
"""Cases where retrying won't change the result."""
def backoff(response, attempt, base=1.0, cap=60.0):
retry_after = response.headers.get("Retry-After") if response is not None else None
if retry_after and retry_after.isdigit():
return min(int(retry_after), cap)
return min(cap, base * 2**attempt) * random.uniform(0.5, 1.0)
async def fetch(client, url, attempts=5):
for attempt in range(attempts):
response = None
try:
response = await client.get(url)
except httpx.ProxyError as exc:
raise StopScraping(f"Proxy error (check your credentials): {exc}") from exc
except httpx.TransportError:
pass # dropped connection, timeout: can be retried
else:
status = response.status_code
if status < 400:
return response
if status in STOP_STATUS:
raise StopScraping(f"{url}: HTTP {status}, will not retry")
if status not in RETRY_STATUS:
return response
await asyncio.sleep(backoff(response, attempt))
raise RuntimeError(f"{url}: no result after {attempts} attempts")
async def main():
proxy = "http://user:pass@pr.proxynet.io:8000"
async with httpx.AsyncClient(proxy=proxy, timeout=20, follow_redirects=True) as client:
urls = ["https://example.com/product/1", "https://example.com/product/2"]
results = await asyncio.gather(*(fetch(client, u) for u in urls), return_exceptions=True)
for url, result in zip(urls, results):
print(url, result if isinstance(result, Exception) else result.status_code)
asyncio.run(main())نمونه را برای کار خودتان در دو جا گسترش دهید. نخست، asyncio.gather همه آدرسها را یکجا آغاز میکند؛ در کار واقعی تعداد درخواستهای همزمان به یک سایت را با asyncio.Semaphore محدود کنید. دوم، Retry-After ممکن است به شکل تاریخ HTTP هم بیاید؛ اگر تحلیل آن شکل را هم لازم دارید، تابعی را که بالاتر پیوندش آمده به کار ببرید.
ثبت و پایش خطاها
کدهای وضعیت را نه فقط برای تصمیم لحظهای، بلکه برای پایش سلامت کار هم به کار ببرید. ثبت این عددها در هر اجرا به تشخیص زودهنگام مشکل کمک میکند:
- پاسخها بر اساس کد: اگر نرخ
429بالا برود بیش از حد سریعاید؛ اگر نرخ403بالا برود چیزی در سمت IP یا هدر عوض شده است. - توزیع بر اساس سایت و endpoint: مشکل در یک سایت است یا در همه؟ بالا رفتن همزمان خطاها در همه سایتها معمولاً به پروکسی یا شبکه اشاره دارد.
- تعداد تلاش دوباره و کل زمان انتظار: اگر تلاشهای دوباره بخش بزرگی از زمان کار را بگیرند، تنظیم همروندی نادرست است.
- پاسخهای
200بدون عنصر مورد انتظار: تنها نشانه از دست رفتن بیصدای داده.
کاربردها
- پایش روزانه قیمت: محصولاتی که
404برمیگردانند از فهرست حذف میشوند و در اجرای بعد همروندی سایتهایی که429دادهاند کم میشود. ساختار کلی در صفحه راهحل استخراج داده آمده است. - جمعآوری کاتالوگ از سایتهای فراوان: نرخ رو به افزایش
403در یک سایت تشخیص ویژه همان سایت را لازم دارد؛ برای پخش بار پروکسی چرخشی به کار میرود. - پنل خودتان که نشست لازم دارد: هدایت
302به صفحه ورود میتواند نشان دهد IP در میانه نشست عوض شده است؛ برای حفظ همان IP در طول نشست پروکسی با نشست ثابت ترجیح داده میشود. - پیکربندی تازه پروکسی: اگر نخستین درخواستها
407یاProxyErrorبرگردانند، مشکل نه مقصد، بلکه اطلاعات ورود است.
اشتباهات رایج
- تلاش دوباره به تعداد یکسان در هر خطا. تکرار
403و407نتیجه را تغییر نمیدهد و فقط ترافیک بیفایده میسازد. - نخواندن هدر
Retry-After. زودتر برگشتن بر پایه حدس خودتان وقتی سرور گفته چقدر صبر کنید. - نیفزودن جزء تصادفی به backoff. صدها درخواستی که همزمان ناموفق شدهاند همزمان دوباره فرستاده میشوند و انباشت تازهای میسازند.
- پذیرفتن پاسخ
200بدون نگاه به محتوا. صفحههای بررسی بیصدا داده خالی میسازند. - دیدن
407همچون خطای سایت مقصد. باید پیکربندی پروکسی خود را بررسی کنید نه مقصد را. - سقف نگذاشتن برای تلاش دوباره. حلقهای که در برابر مشکل دائمی برای همیشه ادامه یابد هم منابع شما و هم سایت مقصد را فرسوده میکند.
راهنمای انتخاب
| آنچه میبینید | نخستین کار |
|---|---|
429 | به اندازه Retry-After صبر کنید، همروندی را کم کنید |
503 با صفحه خطای معمولی | صبر کنید و دوباره تلاش کنید |
503 با صفحه بررسی | متوقف شوید، سرعت و هویت کلاینت را بازبینی کنید |
403 | با همان IP در مرورگر بیازمایید و علت را تشخیص دهید |
407 یا ProxyError | نام کاربری، رمز و لیست سفید پروکسی را بررسی کنید |
502 یا 504 | پس از انتظاری کوتاه دوباره تلاش کنید؛ اگر ادامه یافت موقعیت خروج را عوض کنید |
404 یا 410 | آدرس را از فهرست حذف کنید |
200 اما بدون داده | صفحه بررسی یا بارگذاری پویا را بررسی کنید |
پرسشهای متداول
تفاوت 403 و 401 چیست؟
401 Unauthorized میگوید درخواست احراز هویت لازم دارد و شما اطلاعات ورود معتبر نفرستادهاید. 403 Forbidden میگوید سرور درخواست را فهمیده اما صرفنظر از اطلاعات ورود آن را رد میکند. در 401 افزودن اطلاعات ورود میتواند راهحل باشد؛ در 403 معمولاً نیست.
چرا خطای 407 از پروکسی میآید نه از سایت مقصد؟
چون درخواست هرگز به مقصد نرسیده است. پروکسی پیش از ارسال اتصال شما را احراز هویت میکند و اگر احراز هویت ناموفق باشد خودش پاسخ میدهد. به همین دلیل 407 ربطی به قواعد سایت مقصد ندارد.
آیا عوض کردن IP پس از 429 راهحل است؟
اگر محدودیت نرخ بر اساس IP شمرده شود ممکن است موقتاً کمک کند، اما سرعتی را که مشکل را ساخته تغییر نمیدهد و اگر محدودیت بر اساس نشست یا حساب باشد اصلاً کمکی نمیکند. واکنش درست صبر و کند شدن است؛ چرخش باید از ابتدا برای پخش بار برنامهریزی شود.
اگر هدر Retry-After نبود چقدر صبر کنم؟
backoff نمایی به کار ببرید: با چند ثانیه آغاز کنید، در هر تلاش دو برابر کنید، سقف بگذارید و به هر انتظار جزئی تصادفی بیفزایید. اگر پس از چند تلاش باز ناموفق بود، آدرس را به اجرای بعدی بسپارید.
آیا خطای 503 یعنی سایت از کار افتاده است؟
همیشه نه. ممکن است نگهداری، بار موقت یا مسدودسازی از سوی محافظت ربات باشد. به بدنه پاسخ نگاه کنید: پیام نگهداری است، صفحه خطای عمومی یا صفحه بررسی؟
با پروکسی خطای 502 میگیرم. مشکل کجاست؟
502 میگوید دروازه میانی از سرور پشتش پاسخ معتبری نگرفته است. در زمینه پروکسی ممکن است یعنی پروکسی به مقصد نرسیده یا مقصد اتصال را قطع کرده است. همان آدرس را بدون پروکسی بیازمایید: اگر بدون پروکسی کار کرد، مشکل در نقطه خروج پروکسی است؛ اگر بدون پروکسی هم کار نکرد، مشکل در سایت مقصد است.
خلاصه
کدهای وضعیت HTTP به اسکرپر شما میگویند چه کند: 429 و 503 صبر میخواهند، 403 تشخیص، 407 اصلاح پیکربندی پروکسی و 404 حذف آدرس. اگر هدر Retry-After هست از آن پیروی کنید؛ اگر نه، backoff نمایی با جزء تصادفی به کار ببرید و برای هر حلقه تلاش دوباره سقف بگذارید. بررسی محتوای پاسخهای 200 را هم فراموش نکنید. انواع پروکسی متناسب با کار جمعآوری داده را در خدمات پروکسی ما مییابید.




