مقدمه
در این مقاله یاد میگیریم چگونه در وب اسکریپینگ با aiohttp هدر User-Agent را بهصورت امن و قابل مقیاس مدیریت کنیم. ابتدا مفهوم User-Agent و دلیلِ استفاده از مقادیر جعلی را توضیح میدهیم، سپس چند روش عملی (تنظیم در هر درخواست، تنظیم سِشن، گردش ساده، و استفاده از API برای دانلود لیست بهروز) همراه با نمونهکد پایتون و نکات production ارائه میکنیم. هدف این است که بعد از خواندن مطلب بتوانید یک اسکرپر همزمان، مقاوم در برابر بلاک و با مدیریت هدرهای مرورگر پیادهسازی کنید.
چیست User-Agent و چرا جعلی میسازیم
User-Agent یک رشته است که اطلاعاتی در مورد مرورگر، سیستمعامل و نسخهٔ کلاینت را به سرور میدهد. بسیاری از سرورها رفتار غیرمعمول را براساس این رشته تشخیص میدهند؛ مثال ساده این است که کتابخانههای HTTP مثل aiohttp بهصورت پیشفرض رشتهای شامل نام پایتون و نسخهٔ aiohttp میفرستند و این نشانهٔ یک بات است.
دلیل استفاده از User-Agent جعلی:
- کاهش احتمال بلاک شدن در لایههای سادهٔ دفاع ضدربات
- همسانسازی هدرها با رفتار مرورگر واقعی برای عبور از بررسیهای سطحی
تنظیم سادهٔ User-Agent در هر درخواست
ایدهٔ کلی: در هر فراخوانی session.get یک دیکشنری headers میفرستیم که شامل کلید User-Agent است. مناسب برای اسکریپتهای کوچک یا تست سریع.
نمونهٔ کد (عملکرد: یک درخواست میزند و هدرهای برگشتی را چاپ میکند):
import aiohttp
import asyncio
async def make_request():
headers = {"User-Agent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15"}
async with aiohttp.ClientSession() as session:
async with session.get("", headers=headers) as resp:
text = await resp.text()
print(text[:500]) # نمایش نیمهٔ اولیه پاسخ
asyncio.run(make_request())
ورودی: URL هدف و دیکشنری headers، خروجی: متن پاسخ. دقت کنید از مقدار واقعی و معتبر برای User-Agent استفاده کنید تا رفتار طبیعی به نظر برسد.
تعریف User-Agent در سطح Session
اگر میخواهید همهٔ درخواستهای یک session از یک هدر پیشفرض استفاده کنند، مقدار headers را هنگام ساخت ClientSession تعیین کنید. این روش برای گروهی از درخواستها مناسب است و از تکرار جلوگیری میکند.
import aiohttp
import asyncio
async def make_request():
session_headers = {"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4) AppleWebKit/605.1.15"}
async with aiohttp.ClientSession(headers=session_headers) as session:
async with session.get("") as resp:
print(resp.status)
print(await resp.text())
asyncio.run(make_request())
نکته: اگر برای یک درخواست بخواهید هدر متفاوت بفرستید، میتوانید پارامتر headers را در متد درخواست مشخص کنید که بر هدر سِشن اولویت دارد.
گردش سادهٔ User-Agent در هر درخواست
برای کاهش احتمال شناسایی، لیستی از User-Agentها نگهدارید و برای هر درخواست بهصورت تصادفی یکی انتخاب کنید. این روش ساده ولی نیازمند نگهداری فهرست معتبر است.
import aiohttp
import asyncio
import random
user_agent_list = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/93.0',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4) AppleWebKit/605.1.15',
'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)'
]
async def make_request():
headers = {"User-Agent": random.choice(user_agent_list)}
async with aiohttp.ClientSession() as session:
async with session.get("", headers=headers) as resp:
return await resp.text()
asyncio.run(make_request())
نکات عملی: لیست User-Agentها باید متنوع و بهروز باشد؛ تکرار بیشازحد یک رشته هم میتواند نشانهٔ بات باشد.
دریافت لیست بزرگی از User-Agentها از API خارجی
ساخت و بروزرسانی دستی هزاران User-Agent سخت و وقتگیر است. راه بهتر ذخیرهسازی محلی یا فراخوانی یک API سرویسدهنده برای گرفتن لیست بهروز است. در این حالت مراحل کلی:
- در ابتدای اجرای اسکرپر یکبار لیست را از API دریافت و ذخیره کنید.
- در حین اجرای اسکرپر از این لیست بهصورت تصادفی انتخاب کنید.
- در فواصل زمانی مشخص لیست را تازهسازی کنید (مثلاً هر 24 ساعت).
نمونهٔ تابع برای گرفتن لیست (توجه: مقدار endpoint بهصورت متغیر تعریف شده):
import aiohttp
import asyncio
SCRAPEOPS_API_KEY = "YOUR_API_KEY"
SCRAPEOPS_USER_AGENTS_ENDPOINT = "" # تنظیم کنید
async def get_user_agent_list():
# ورودی: کلید API و endpoint؛ خروجی: لیستی از رشتههای User-Agent
async with aiohttp.ClientSession() as session:
async with session.get(SCRAPEOPS_USER_AGENTS_ENDPOINT, params={"api_key": SCRAPEOPS_API_KEY}) as resp:
json_resp = await resp.json()
return json_resp.get("result", [])
# استفادهٔ نمونه
# user_agents = asyncio.run(get_user_agent_list())
نکات امنیتی: کلیدهای API را در متغیرهای محیطی قرار دهید (مثلاً با os.getenv) و هرگز در کد منبع عمومی نگهداری نکنید.
استفاده از Fake Browser Headers (بیش از فقط User-Agent)
بسیاری از سامانههای ضدربات تنها به User-Agent نگاه نمیکنند و هدرهای تکمیلی مثل Accept، Accept-Language، sec-ch-ua و Sec-Fetch-* را نیز بررسی میکنند. برای رفتاری طبیعیتر میتوانید مجموعهٔ کاملتری از هدرهای مرورگر را ارسال کنید.
روال پیشنهادی:
- یک منبع معتبر برای هدرها داشته باشید (ممکن است API دانلود هدرها یا مجموعهٔ محلی).
- برای هر درخواست یک مجموعهٔ کامل از هدرها انتخاب کنید تا سازگاری بین فیلدها حفظ شود (مثلاً platform و sec-ch-ua-platform).
نمونهٔ استفادهٔ هدر کامل در درخواست:
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/99.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Sec-Fetch-Site": "none",
"Sec-Fetch-Mode": "navigate",
}
# سپس این headers را همانطور که پیشتر نشان داده شد ارسال کنید.
نکات عملی، بهترین روشها و محدودیتها
چند نکتهٔ حیاتی که همیشه در نظر داشته باشید:
- بازاستفادهٔ سِشن: ساخت مکرر سِشن هزینهبر است؛ برای تعداد زیاد درخواستها از یک یا چند سِشن باز استفاده کنید.
- محدودسازی همزمانی: از Semaphore برای جلوگیری از ارسال همزمان بیشازحد درخواستها استفاده کنید تا هم ریسک بلاک کاهش یابد و هم منابع محلی کنترل شوند.
- Timeout و Retry: از aiohttp.ClientTimeout و مکانیزم retry (مثلاً backoff) برای مدیریت خطاهای شبکه استفاده کنید.
- استفاده از پراکسی: در پروژههای بزرگ و وقتی سایتها محدودکنندهاند، پراکسیها معمولاً لازماند. پراکسیها را ترکیب با گردش هدر استفاده کنید.
- اعتبار و سازگاری هدرها: مطمئن شوید هدرها با هم سازگارند؛ مقادیر تصادفی بیربط میتوانند موجب شناسایی شوند.
- حریم خصوصی و قوانین: همیشه قوانین سایت (robots.txt و شرایط خدمات) و قوانین محلی را رعایت کنید. جمعآوری دادهٔ غیرمجاز میتواند تبعات قانونی داشته باشد.
نمونهٔ کاملتر: ترکیب دریافت هدرها و اجرای همزمان با محدودیت
در این مثال کوتاه از semaphore برای محدود کردن تعداد همزمانی، گرفتن لیست هدرها از API و ارسال درخواست با هدرهای تصادفی استفاده میکنیم.
import asyncio
import aiohttp
import random
import os
SCRAPEOPS_API_KEY = os.getenv("SCRAPEOPS_API_KEY", "YOUR_API_KEY")
SCRAPEOPS_HEADERS_ENDPOINT = ""
CONCURRENCY = 5
async def get_headers_list():
async with aiohttp.ClientSession() as session:
async with session.get(SCRAPEOPS_HEADERS_ENDPOINT, params={"api_key": SCRAPEOPS_API_KEY}) as r:
data = await r.json()
return data.get("result", [])
async def fetch(url, header_list, sem):
headers = random.choice(header_list)
async with sem:
timeout = aiohttp.ClientTimeout(total=15)
async with aiohttp.ClientSession(timeout=timeout) as session:
try:
async with session.get(url, headers=headers) as resp:
return await resp.text()
except Exception as e:
return f"ERROR: {e}"
async def main(urls):
header_list = await get_headers_list()
sem = asyncio.Semaphore(CONCURRENCY)
tasks = [asyncio.create_task(fetch(u, header_list, sem)) for u in urls]
results = await asyncio.gather(*tasks)
for r in results:
print(len(r))
# asyncio.run(main(["", ""]))
توضیح: ورودیها: لیست URLها و endpoint هدرها؛ خروجی: متن پاسخها یا پیام خطا. از semaphore برای کنترل همزمانی استفاده شده و timeout برای جلوگیری از بستهشدن منابع بهکار رفته است.
جمعبندی
تنظیم User-Agent تنها شروع کار است؛ برای اسکریپینگ مقاوم و قابلاعتماد باید به نکات زیر توجه کنید:
- از سشنهای باز و مدیریت صحیح زمان/ریسورس استفاده کنید.
- برای دقت بیشتر هدرهای مرورگر (نه فقط User-Agent) را شبیهسازی کنید.
- گردش (rotation) و بهروزرسانی لیست هدرها با منابع معتبر دردسترس را خودکار کنید.
- کلیدهای API را ایمن نگه دارید، از retry و backoff استفاده کنید و قوانین سایتها را رعایت کنید.
با رعایت این روشها میتوانید اسکرپرهای aiohttp بسازید که هم مقیاسپذیر باشند و هم ریسک بلاک شدن را کاهش دهند.





