مقدمه
این مقاله یک راهنمای عملی برای ساخت کلاینتهای HTTP سریع و مقاوم با Python hRequests است. هدف این است که بعد از خواندن این مطلب بتوانید درخواستهای GET و POST را بهدرستی ارسال کنید، User-Agent و پراکسی را مدیریت کنید، خطاها را با استراتژی retry هندل کنید و اسکریپرهای خود را با همزمانی مقیاسپذیر کنید. در پایان بخشهای عملی و مثالهای کد با توضیح ورودیها، خروجیها و نقش هر تابع خواهید دید.
ارسال درخواست GET — پایه و اصول
ایده کلی: با hrequests.get HTML یا JSON را از سرور دریافت میکنیم و با استفاده از ویژگیهای شیء Response بررسی و پردازش میکنیم.
import hrequests
response = hrequests.get('http://quotes.toscrape.com/')
print(response.text[:200]) # نمایش اولین 200 کاراکترتوضیحات:
- ورودی: یک URL (رشته) و اختیاری پارامترهایی مثل headers، params یا proxies.
- خروجی: یک شیء Response که ویژگیهایی مثل status_code، text، content و headers دارد.
- نکته خطبهخط: hrequests.get یک درخواست HTTP ارسال میکند، سرور پاسخ میدهد و متن پاسخ در response.text قرار میگیرد.
ویژگیهای مهم Response (خلاصه):
- status_code: کد HTTP
- text: محتوای یونیکد
- content: محتوای بایت
- headers: هدرهای پاسخ
- url, encoding, cookies, history, ok, elapsed
ارسال درخواست POST (JSON و فرم)
ایده کلی: برای ارسال دادهها از متد post استفاده میکنیم و برای JSON از پارامتر json و برای فرم از data.
import hrequests
url = 'https://example.com/api'
payload = {'key': 'value'}
# ارسال JSON
r = hrequests.post(url, json=payload)
print(r.status_code, r.json())توضیحات:
- پارامتر json باعث میشود هِدر Content-Type به "application/json" ست شود و داده به صورت JSON سریالایز شود.
- پارامتر data برای فرمهای سنتی کاربرد دارد (application/x-www-form-urlencoded یا multipart برای فایل).
- همیشه پاسخ سرور را با status_code و در صورت نیاز با r.json() یا بررسی r.text کنترل کنید.
مدیریت User-Agent و روتیشن
چرا مهم است: User-Agent یکی از سیگنالهای سادهای است که سرورها برای تشخیص باتها استفاده میکنند. مقادیر پیشفرض کلاینتها معمولاً قابل شناسایی هستند، بنابراین باید آن را مدیریت کنیم.
تنظیم یک User-Agent ساده:
import hrequests
headers = {"User-Agent": "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X)"}
r = hrequests.get('http://quotes.toscrape.com/', headers=headers)
print(r.status_code)توضیح: ورودی این تابع یک دیکشنری headers است و آن را به عنوان هدر درخواست ارسال میکند. خروجی همان Response است.
چرخش User-Agent (simple rotation):
import hrequests
import random
user_agent_list = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 ...) ...',
]
headers = {"User-Agent": random.choice(user_agent_list)}
r = hrequests.get('http://quotes.toscrape.com/', headers=headers)
print(r.status_code)نکات عملی و best practices:
- به جای نگهداری استاتیک یک لیست قدیمی، از سرویسهای بهروز کننده User-Agent یا یک دیتابیس معتبر استفاده کنید.
- همیشه User-Agent را همراه با تغییرات دیگر (پراکسی، تأخیر یکنواخت) ترکیب کنید تا الگوهای قابل تشخیص را کاهش دهید.
پراکسیها — چرا و چطور
هدف: پراکسیها به شما اجازه میدهند درخواستها را از IPهای مختلف بفرستید تا احتمال بلاک کاهش یابد. سه مدل متداول وجود دارد که در ادامه برای هرکدام مثال و مزایا/معایب میآوریم.
پراکسی نوع 1: لیست IP و چرخش محلی
ایده: سرویس دهنده یک لیست از IPها به شما میدهد و شما برای هر درخواست یک IP تصادفی انتخاب میکنید.
import hrequests
import random
proxy_list = [
'http://Username:Password@85.237.57.198:20000',
'http://Username:Password@85.237.57.198:21000',
]
proxy = random.choice(proxy_list)
proxies = {"http": proxy, "https": proxy}
r = hrequests.get('http://quotes.toscrape.com/', proxies=proxies)
print(r.status_code)مزایا/معایب:
- مزایا: کنترل کامل روی IPها و منطق روتیشن.
- معایب: نیاز به مانیتورینگ و حذف IPهای بد/کمکیفیت؛ کار زیاد برای نگهداری.
پراکسی نوع 2: پروکسی گیتوی (Gateway)
ایده: شما یک endpoint ثابت دارید و ارائهدهنده خودش روتیشن، تعویض IP و سلامتسنجی را انجام میدهد. در درخواست شما فقط یک proxy تعریف میشود و معمولاً نیاز به احراز هویت است.
import hrequests
proxies = {
'http': 'http://zproxy.lum-superproxy.io:22225',
'https': 'http://zproxy.lum-superproxy.io:22225',
}
# auth tuple اگر لازم باشد
r = hrequests.get('http://quotes.toscrape.com/', proxies=proxies, auth=('USERNAME','PASSWORD'))
print(r.status_code)مزایا/معایب:
- مزایا: ساده، مناسب برای استفاده از پراکسیهای residential یا mobile بدون مدیریت دستی.
- معایب: هزینه بالاتر و وابستگی به کیفیت سرویسدهنده.
پراکسی نوع 3: API پراکسی (Smart Proxy API)
ایده: به یک API میفرستید (پارامتر URL و API key) و خود سرویس HTML پردازششده یا proxied response را برمیگرداند. مناسب وقتی میخواهید سربار مدیریت پراکسی را حذف کنید.
import hrequests
params = {'api_key': 'APIKEY', 'url': 'http://quotes.toscrape.com/'}
r = hrequests.get('https://proxy.example.com/v1/', params=params)
print(r.text[:200])نکات امنیتی: کلیدهای API را در محیط (ENV) یا secret manager نگه دارید و آنها را در کد ثابت قرار ندهید.
Retry و مدیریت خطاها
ایده کلی: درخواستها ممکن است به خاطر شبکه یا بلاک شدن شکست بخورند. برای افزایش پایداری از مکانیسم retry با backoff استفاده کنید.
import time
import hrequests
import random
NUM_RETRIES = 4
BACKOFF_BASE = 1 # ثانیه
url = 'http://quotes.toscrape.com/'
response = None
for attempt in range(1, NUM_RETRIES + 1):
try:
response = hrequests.get(url, timeout=10)
if response.status_code == 200 and 'Robot or human?' not in response.text:
break
# اگر کد 404 هم قابل قبول است، میتوانید آن را قبول کنید
except Exception as e:
# لاگ کردن خطا و ادامه
pass
sleep_time = BACKOFF_BASE * (2 ** (attempt - 1)) + random.uniform(0, 0.5)
time.sleep(sleep_time)
if response and response.status_code == 200:
# پردازش موفق
pass
else:
# رسیدگی به شکست نهایی
passتوضیحات:
- این الگو از exponential backoff به اضافه یک جرقه تصادفی استفاده میکند تا فشار روی سرور کم شود.
- در بررسی پاسخ علاوه بر کد میتوانید محتوای HTML را برای نشانههای صفحه بن شود (مانند متن «Robot or human?») بررسی کنید.
همزمانی و مقیاسپذیری با ThreadPoolExecutor
ایده کلی: با استفاده از ThreadPoolExecutor میتوان درخواستها را بهصورت همزمان ارسال کرد تا زمان کل اجرا کاهش یابد. این روش برای IO-bound (مثل HTTP) بسیار مؤثر است.
import hrequests
from bs4 import BeautifulSoup
import concurrent.futures
NUM_THREADS = 5
list_of_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
output_data_list = []
def scrape_page(url):
try:
r = hrequests.get(url, timeout=10)
if r.status_code == 200:
soup = BeautifulSoup(r.text, 'html.parser')
title = soup.find('h1').text if soup.find('h1') else ''
return {'url': url, 'title': title}
except Exception as e:
return {'url': url, 'error': str(e)}
with concurrent.futures.ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
results = executor.map(scrape_page, list_of_urls)
for item in results:
output_data_list.append(item)
print(output_data_list)نکات عملی:
- برای IO-bound کارها، threading بهتر از پردازشهای سنگین CPU است.
- با افزایش تعداد نخها، بار روی سرور مقصد، محدودیتهای شبکه و مصرف حافظه را زیر نظر داشته باشید.
- ترکیب همزمانی با پراکسی و روتیشن UA برای اجتناب از بلاک مهم است.
صفحات JS-rendered و راهکارها
مشکل: hRequests فقط HTML اولیه را برمیگرداند و JavaScript را اجرا نمیکند؛ بنابراین اگر سایت با SPA ساخته شده باشد دادهها پس از لود اولیه در سمت کلاینت تولید میشوند.
گزینهها:
- استفاده از headless browser مانند Selenium یا Pyppeteer برای رندر جاوااسکریپت و دریافت HTML نهایی.
- استفاده از سرویس پراکسی/API که رندر JS را برای شما انجام میدهد (پارامتری مانند render_js=true به API میفرستید).
# نمونه ساده: درخواست به یک Proxy API که قابلیت رندر JS دارد
import hrequests
params = {'api_key': 'APIKEY', 'url': 'http://quotes.toscrape.com/', 'render_js': 'true'}
r = hrequests.get('https://proxy.example.com/v1/', params=params)
print(r.text[:300])نکات: رندر کردن JS هزینهبر است و نرخ درخواست مجاز را کاهش میدهد؛ بنابراین فقط برای صفحات ضروری از آن استفاده کنید.
جمعبندی و توصیههای نهایی
نکات کلیدی که باید بهکار ببرید:
- همیشه هدرها بهویژه User-Agent را مدیریت کنید و در مقیاس از روتیشن استفاده کنید.
- برای جلوگیری از بلاک از پراکسی استفاده کنید؛ اگر میخواهید نگهداری کم باشد از gateway یا proxy API استفاده کنید.
- برای افزایش پایداری از retry با backoff استفاده کنید و محتوای پاسخ را برای تشخیص صفحات بن چک کنید.
- برای سرعت از همزمانی (مثل ThreadPoolExecutor) استفاده کنید، اما منابع سیستم و رفتار سایت را رعایت کنید.
- برای صفحات JS-rendered از headless browser یا سرویسهایی که رندر را انجام میدهند بهره ببرید.
با ترکیب این الگوها میتوانید اسکریپرهایی بسازید که هم سریع، هم قابلاطمینان و هم کمتر در معرض بلاک شدن باشند. همیشه قوانین و سیاستهای هدف (robots.txt و Terms of Service) را بررسی کنید و اخلاق و قوانین را رعایت نمایید.





