يعد حد المعدل أحد الأسباب الأكثر شيوعًا التي تؤدي إلى تعطل أدوات التجريف، وفشل تكاملات API، والحصول على حالة 429 Too Many Requests. يرى الخادم عددًا كبيرًا جدًا من الطلبات من عنوان IP واحد - ويتوقف ببساطة عن الاستجابة. في هذه المقالة، سنستعرض كيفية بناء بنية تحتية صحيحة باستخدام البروكسي لتجاوز حدود الطلبات دون حظر أو فشل - مع أمثلة حقيقية للشفرة بلغة بايثون وNode.js.
ما هو حد المعدل ولماذا لا تساعد التأخيرات العادية
حد المعدل (تحديد تردد الطلبات) هو آلية لحماية الخادم، تحدد عدد الطلبات من مصدر واحد خلال فترة زمنية معينة. غالبًا ما يكون المصدر هو عنوان IP، ولكن الأنظمة المتقدمة تأخذ في الاعتبار أيضًا رموز التفويض، User-Agent، الكوكيز، وحتى أنماط السلوك.
عندما يتجاوز سكربتك الحد، يعيد الخادم أحد الردود التالية:
429 Too Many Requests- الحالة القياسية لـ HTTP لحد المعدل503 Service Unavailable- يستخدم أحيانًا بدلاً من 429403 Forbidden- إذا كان IP قد تم إدراجه بالفعل في قائمة الحظر- استجابة فارغة أو مهلة - عند الحظر العدواني
الفكرة الأولى لمعظم المطورين هي إضافة time.sleep(1) بين الطلبات. هذا يعمل فقط عند وجود حدود خفيفة جدًا (مثل 60 طلبًا في الدقيقة). لكن السيناريوهات الحقيقية أكثر تعقيدًا:
الحدود الحقيقية للمنصات الشائعة:
- Twitter/X API (مجاني): 500,000 تغريدة في الشهر، ولكن لا أكثر من 15 طلبًا كل 15 دقيقة
- بحث Google: ~100 طلب في اليوم من عنوان IP واحد بدون تفويض
- Wildberries، Ozon: حد معدل عدواني - حظر بعد 30-50 طلبًا في الدقيقة
- GitHub API: 60 طلبًا/ساعة بدون رمز، 5000/ساعة مع رمز
- المواقع المحمية بواسطة Cloudflare: يمكن أن تحظر بعد 10-20 طلبًا في الدقيقة
إذا كنت بحاجة إلى جمع 100,000 بطاقة منتج من سوق أو مراقبة الأسعار في الوقت الحقيقي - فإن التأخيرات ببساطة لن تساعد. تحتاج إلى بنية أخرى. وهنا تصبح البروكسي ضرورة وليس خيارًا.
من المهم أن نفهم: حد المعدل مرتبط بعنوان IP. إذا كان لديك 100 IP مختلف - لديك فعليًا 100 "حصة" مستقلة. هذه هي الفكرة الرئيسية لتجاوز القيود عبر البروكسي.
كيف تحل البروكسي مشكلة قيود الطلبات
الآلية بسيطة: كل طلب إلى الخادم المستهدف يأتي من عنوان IP مختلف. من وجهة نظر الخادم - هؤلاء مستخدمون مختلفون. حصة كل منهم لا تُستهلك تقريبًا، لذلك لا يحدث الحظر.
دعونا نلقي نظرة على الفرق بين العمل بدون بروكسي ومع مجموعة بروكسي في مثال محدد. لنفترض أن الخادم يسمح بـ 10 طلبات في الدقيقة من عنوان IP واحد:
| السيناريو | الطلبات في الدقيقة | الحظر | الوقت لـ 10,000 طلب |
|---|---|---|---|
| عنوان IP واحد، بدون بروكسي | 10 | نعم، بعد 10 طلبات | ~16 ساعة |
| 10 بروكسي، تدوير | 100 | لا | ~1.7 ساعة |
| 100 بروكسي، تدوير | 1000 | لا | ~10 دقائق |
بالإضافة إلى توسيع عرض النطاق الترددي، توفر البروكسي أيضًا عدة مزايا عند العمل مع حد المعدل:
- عزل الجلسات - إذا تم حظر عنوان IP واحد، تواصل البقية العمل
- التوزيع الجغرافي - الطلبات تأتي من مناطق مختلفة، مما يقلل من الشكوك
- جلسات ثابتة - إمكانية "الالتصاق" بعنوان IP واحد لسيناريوهات متعددة الخطوات (تفويض + إجراء)
- التحكم في الحمل - يمكن ضبط الطلبات بدقة لكل IP، دون تجاوز الحد
أي نوع من البروكسي يجب أن تختار لمهمتك
ليست جميع البروكسي فعالة بنفس القدر ضد حد المعدل. يعتمد اختيار النوع على الموقع المستهدف، وحجم الطلبات، والميزانية. دعونا نستعرض ثلاثة أنواع رئيسية:
بروكسي سكنية
هذه هي عناوين IP لمستخدمين حقيقيين في المنازل. تبدو كحركة مرور الإنترنت العادية ونادرًا ما تتعرض للحظر. البروكسي السكنية هي الخيار الأمثل للمواقع ذات الحماية العدوانية: الأسواق (Wildberries، Ozon)، الشبكات الاجتماعية، الموارد المحمية بواسطة Cloudflare. العيب الرئيسي هو السعر المرتفع مقارنة بالبروكسي من مراكز البيانات.
بروكسي موبايل
عناوين IP لمشغلي الهواتف المحمولة (3G/4G/5G). ميزتها هي أن عنوان IP واحد يمكن أن يستخدمه آلاف المشتركين الحقيقيين في نفس الوقت، لذلك لا ترغب المواقع في حظر هذا العنوان. البروكسي الموبايل تظهر أفضل النتائج حيث تبدأ البروكسي السكنية في الحظر - على سبيل المثال، عند التجريف عالي التردد لـ Instagram أو العمل مع API للمنصات التي تحلل نوع الاتصال.
بروكسي من مراكز البيانات
عناوين IP سريعة ورخيصة من مراكز البيانات. مثالية لتجريف المواقع بدون حماية جدية: APIs مفتوحة، مجمعات الأخبار، قواعد بيانات عامة. لمهام حد المعدل، تحتاج إلى المزيد منها (لأنها غالبًا ما تدخل قوائم الحظر)، ولكن مع تدوير صحيح، يمكنها التعامل بشكل جيد مع أحجام كبيرة من الطلبات. المزيد من التفاصيل - على صفحة بروكسي مراكز البيانات.
| نوع البروكسي | الخصوصية | السرعة | السعر | أفضل سيناريو |
|---|---|---|---|---|
| سكنية | عالية جدًا | متوسطة | $$ | أسواق، شبكات اجتماعية، Cloudflare |
| موبايل | قصوى | متوسطة | $$$ | Instagram API، تجريف عالي التردد |
| مراكز البيانات | متوسطة | عالية | $ | APIs مفتوحة، بيانات عامة |
استراتيجيات تدوير IP: لكل طلب، جلسات ثابتة، جولة
مجرد وجود بروكسي لا يحل المشكلة - من المهم إدارتها بشكل صحيح. هناك عدة استراتيجيات للتدوير، كل منها تناسب سيناريوهاتها الخاصة.
تدوير لكل طلب (IP جديد لكل طلب)
كل طلب HTTP يذهب عبر عنوان IP جديد. هذه هي الاستراتيجية الأكثر عدوانية لتجاوز حد المعدل - لا يتمكن الخادم من تراكم العد للـ IP الواحد. تناسب:
- تجريف بطاقات المنتجات (كل بطاقة هي طلب منفصل)
- جمع البيانات من محركات البحث
- أي طلبات بدون حالة، لا تتطلب جلسة
جلسات ثابتة (IP ثابت للجلسة)
يتم استخدام IP واحد طوال الجلسة (عادةً 1-30 دقيقة). هذا أمر حاسم للسيناريوهات التي تتطلب تفويضًا: تسجيل الدخول إلى الحساب، تنفيذ الإجراء، تسجيل الخروج. إذا تغير IP بين الخطوات - قد يقوم الخادم بحظر الجلسة كشيء مشبوه.
جولة مع حد الطلبات على IP
الاستراتيجية الأكثر دقة. تعرف حد الخادم (على سبيل المثال، 10 طلبات في الدقيقة) وتوزع الطلبات عبر مجموعة البروكسي بحيث لا يتجاوز كل IP هذا العتبة أبدًا. يتطلب تنفيذ قائمة انتظار مع مراعاة وقت الطلب الأخير لكل IP.
صيغة حساب عدد البروكسي المطلوب:
N بروكسي = (السرعة المستهدفة للطلبات/الدقيقة) ÷ (حد الخادم/الدقيقة على IP)
مثال: تحتاج 500 طلب/دقيقة، حد الخادم - 10/دقيقة → تحتاج على الأقل 50 بروكسي.
أضف 20% احتياطي في حالة الحظر: الإجمالي 60 بروكسي.
أمثلة للشفرة بلغة بايثون: requests، aiohttp، Scrapy
دعونا ننتقل إلى الممارسة. أدناه - قوالب جاهزة لأكثر ثلاثة أدوات بايثون شعبية.
1. requests + تدوير البروكسي يدويًا
أبسط خيار - قائمة البروكسي واختيار عشوائي لكل طلب:
import requests
import random
import time
PROXIES = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
# ... أضف العدد المطلوب
]
def get_random_proxy():
proxy = random.choice(PROXIES)
return {"http": proxy, "https": proxy}
def fetch_with_retry(url, max_retries=3):
for attempt in range(max_retries):
proxy = get_random_proxy()
try:
response = requests.get(
url,
proxies=proxy,
timeout=10,
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
)
if response.status_code == 429:
print(f"تم تحديد حد المعدل على {proxy}، يتم التبديل...")
time.sleep(1)
continue
return response
except requests.RequestException as e:
print(f"محاولة {attempt+1} فشلت: {e}")
time.sleep(2)
return None
# الاستخدام
urls = ["https://example.com/item/1", "https://example.com/item/2"]
for url in urls:
result = fetch_with_retry(url)
if result:
print(f"نجاح: {url} — {len(result.text)} بايت")
2. مجموعة بروكسي ذكية مع مراعاة حد المعدل
خيار أكثر تقدمًا - فئة ProxyPool، التي تتتبع وقت آخر استخدام لكل IP ولا تتجاوز الحد المحدد:
import requests
import time
from collections import defaultdict
from threading import Lock
class ProxyPool:
def __init__(self, proxies, rate_limit=10, window=60):
"""
proxies: قائمة من السلاسل على شكل 'http://user:pass@host:port'
rate_limit: الحد الأقصى للطلبات من IP واحد خلال window ثواني
window: نافذة زمنية بالثواني
"""
self.proxies = proxies
self.rate_limit = rate_limit
self.window = window
self.usage = defaultdict(list) # proxy -> [timestamps]
self.lock = Lock()
def get_available_proxy(self):
now = time.time()
with self.lock:
for proxy in self.proxies:
# تنظيف العلامات القديمة
self.usage[proxy] = [
t for t in self.usage[proxy]
if now - t < self.window
]
if len(self.usage[proxy]) < self.rate_limit:
self.usage[proxy].append(now)
return {"http": proxy, "https": proxy}
return None # جميع البروكسي استنفدت الحد
def fetch(self, url, **kwargs):
proxy = self.get_available_proxy()
if proxy is None:
print("جميع البروكسي محجوزة، في الانتظار...")
time.sleep(5)
return self.fetch(url, **kwargs)
try:
response = requests.get(url, proxies=proxy, timeout=10, **kwargs)
return response
except requests.RequestException as e:
print(f"فشل الطلب: {e}")
return None
# الاستخدام
pool = ProxyPool(
proxies=[
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
],
rate_limit=10, # 10 طلبات في الدقيقة على IP
window=60
)
for i in range(100):
r = pool.fetch(f"https://example.com/page/{i}")
if r:
print(f"الصفحة {i}: {r.status_code}")
3. aiohttp للتجريف غير المتزامن
يسمح النهج غير المتزامن باستخدام عشرات البروكسي في وقت واحد دون حظر الخيوط:
import asyncio
import aiohttp
import itertools
PROXIES = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
proxy_cycle = itertools.cycle(PROXIES)
async def fetch(session, url, proxy):
try:
async with session.get(
url,
proxy=proxy,
timeout=aiohttp.ClientTimeout(total=10)
) as response:
if response.status == 429:
await asyncio.sleep(2)
return None
return await response.text()
except Exception as e:
print(f"خطأ: {e}")
return None
async def main(urls):
connector = aiohttp.TCPConnector(limit=50)
async with aiohttp.ClientSession(connector=connector) as session:
tasks = [
fetch(session, url, next(proxy_cycle))
for url in urls
]
results = await asyncio.gather(*tasks)
return results
urls = [f"https://example.com/item/{i}" for i in range(200)]
results = asyncio.run(main(urls))
print(f"تم جمع: {sum(1 for r in results if r is not None)} صفحات")
4. Scrapy مع تدوير عبر middleware
يوجد حل جاهز لـ Scrapy - scrapy-rotating-proxies. ولكن يمكنك كتابة middleware خاص بك:
# middlewares.py
import random
class RotatingProxyMiddleware:
def __init__(self, proxies):
self.proxies = proxies
@classmethod
def from_crawler(cls, crawler):
return cls(proxies=crawler.settings.getlist("PROXY_LIST"))
def process_request(self, request, spider):
proxy = random.choice(self.proxies)
request.meta["proxy"] = proxy
def process_response(self, request, response, spider):
if response.status == 429:
spider.logger.warning(f"تم تحديد حد المعدل، البروكسي: {request.meta.get('proxy')}")
# يمكنك إضافة منطق لاستبعاد البروكسي المسبب للمشاكل
return response
# settings.py
PROXY_LIST = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.RotatingProxyMiddleware": 350,
}
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 10
أمثلة للشفرة بلغة Node.js: axios، got، Puppeteer
Node.js هو خيار شائع لأتمتة المتصفحات والعمل مع API. إليك أنماط جاهزة للعمل مع البروكسي.
1. axios مع تدوير البروكسي
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxies = [
'http://user:[email protected]:8080',
'http://user:[email protected]:8080',
'http://user:[email protected]:8080',
];
let proxyIndex = 0;
function getNextProxy() {
const proxy = proxies[proxyIndex % proxies.length];
proxyIndex++;
return proxy;
}
async function fetchWithProxy(url, retries = 3) {
for (let i = 0; i < retries; i++) {
const proxyUrl = getNextProxy();
const agent = new HttpsProxyAgent(proxyUrl);
try {
const response = await axios.get(url, {
httpsAgent: agent,
httpAgent: agent,
timeout: 10000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
},
});
return response.data;
} catch (error) {
if (error.response?.status === 429) {
console.log(`تم تحديد حد المعدل، يتم التبديل للبروكسي...`);
await new Promise(r => setTimeout(r, 1000));
continue;
}
console.error(`محاولة ${i + 1} فشلت:`, error.message);
}
}
return null;
}
// الاستخدام
(async () => {
const urls = Array.from({length: 50}, (_, i) => `https://example.com/item/${i}`);
const results = await Promise.allSettled(
urls.map(url => fetchWithProxy(url))
);
const successful = results.filter(r => r.status === 'fulfilled' && r.value).length;
console.log(`نجاح: ${successful}/${urls.length}`);
})();
2. Puppeteer مع البروكسي وتجاوز حد المعدل
للمواقع التي تستخدم الريندرينغ عبر JavaScript وحماية Cloudflare، تحتاج إلى متصفح بدون رأس:
const puppeteer = require('puppeteer');
const proxies = [
'proxy1.example.com:8080',
'proxy2.example.com:8080',
];
async function scrapeWithProxy(url, proxyHost) {
const browser = await puppeteer.launch({
args: [
`--proxy-server=${proxyHost}`,
'--no-sandbox',
'--disable-setuid-sandbox',
],
headless: true,
});
const page = await browser.newPage();
// تفويض البروكسي
await page.authenticate({
username: 'user',
password: 'pass',
});
// تعيين User-Agent واقعي
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
);
try {
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
// التحقق من حد المعدل
const status = await page.evaluate(() => document.title);
if (status.includes('429') || status.includes('Too Many')) {
console.log('تم تحديد حد المعدل، تحتاج إلى تبديل البروكسي');
return null;
}
const data = await page.evaluate(() => {
return document.querySelector('.price')?.textContent || null;
});
return data;
} finally {
await browser.close();
}
}
// تدوير حسب المهام
(async () => {
const urls = ['https://example.com/product/1', 'https://example.com/product/2'];
for (let i = 0; i < urls.length; i++) {
const proxy = proxies[i % proxies.length];
const result = await scrapeWithProxy(urls[i], proxy);
console.log(`${urls[i]}: ${result}`);
await new Promise(r => setTimeout(r, 500)); // تأخير بسيط
}
})();
تقنيات متقدمة: رؤوس، بصمة، تجاوز Cloudflare
تغيير IP هو شرط ضروري، ولكنه ليس كافيًا دائمًا. تقوم الأنظمة الحديثة بتحليل عشرات المعلمات للطلب. دعونا نحلل ما يجب أخذه في الاعتبار أيضًا.
رؤوس HTTP: الحد الأدنى المطلوب
يبدو الطلب بدون رؤوس طبيعية كأنه روبوت حتى مع تغيير IP. دائمًا أضف:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Cache-Control": "max-age=0",
}
معالجة رأس Retry-After
عند الرد 429، غالبًا ما يحدد الخادم مقدار الوقت الذي يجب الانتظار فيه. تتيح المعالجة الصحيحة لهذا الرأس عدم إهدار الطلبات:
def handle_rate_limit(response):
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
if retry_after:
wait_time = int(retry_after)
print(f"تم تحديد حد المعدل. الانتظار {wait_time} ثواني...")
time.sleep(wait_time + 1) # +1 ثانية احتياطي
else:
# تأخير أسي إذا لم يكن هناك رأس
time.sleep(min(2 ** attempt, 60))
return True
return False
بصمة TLS وكيفية تجاوزها
تقوم الأنظمة المتقدمة (Cloudflare، Akamai، PerimeterX) بتحليل بصمة TLS - "بصمة" فريدة من نوعها لاتصال TLS الخاص بك. تحتوي مكتبة requests على بصمة سهلة التعرف عليها. الحلول:
- curl_cffi (بايثون) - تحاكي بصمة Chrome/Firefox على مستوى TLS
- tls-client (Go/بايثون) - أداة مماثلة تدعم ملفات تعريف المتصفح المختلفة
- Playwright/Puppeteer - متصفح حقيقي، بصمة مثالية بشكل افتراضي
# pip install curl-cffi
from curl_cffi import requests as cffi_requests
response = cffi_requests.get(
"https://cloudflare-protected-site.com/api/data",
impersonate="chrome120", # تحاكي Chrome 120
proxies={"https": "http://user:[email protected]:8080"}
)
print(response.json())
إدارة الكوكيز والجلسات
إذا كان الموقع يستخدم الكوكيز لتتبع الجلسات، فإن تغيير IP دون تغيير الكوكيز يكون بلا جدوى. عند تبديل البروكسي، أنشئ دائمًا جلسة جديدة:
import requests
def create_fresh_session(proxy_url):
"""إنشاء جلسة جديدة مع كوكيز نظيفة لكل بروكسي"""
session = requests.Session()
session.proxies = {"http": proxy_url, "https": proxy_url}
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
})
# لا يتم نقل الكوكيز من الجلسة السابقة
return session
# لكل IP جديد - جلسة جديدة
for proxy in proxies:
session = create_fresh_session(proxy)
response = session.get("https://example.com/protected-page")
# معالجة الاستجابة...
الأخطاء الشائعة عند العمل مع البروكسي وحدود المعدل
حتى مع إعداد البروكسي بشكل صحيح، يواجه المطورون نفس الأخطاء بشكل متكرر. إليك أكثر الأخطاء شيوعًا وطرق تجنبها.
قائمة التحقق: ماذا تحقق قبل بدء أداة التجريف
- ☐ تمت إضافة رؤوس HTTP واقعية (User-Agent، Accept، Accept-Language)
- ☐ عند تغيير البروكسي، يتم إنشاء جلسة جديدة (كوكيز جديدة)
- ☐ يتم معالجة الحالات 429، 503، 403 مع منطق إعادة المحاولة
- ☐ تم تنفيذ تأخير بين الطلبات (على الأقل 100-500 مللي ثانية)
- ☐ عدد البروكسي يتناسب مع السرعة المستهدفة للطلبات
- ☐ تم اختبار عمل البروكسي قبل البدء (فحص الصحة)
- ☐ يتم تسجيل الأخطاء والإحصائيات لكل بروكسي
- ☐ تم إعداد مهلة للطلبات (لا تزيد عن 15-30 ثانية)
الخطأ 1: استخدام بروكسي "ميت"
تحقق دائمًا من البروكسي قبل إضافته إلى المجموعة وراجعها بشكل دوري أثناء العمل. بروكسي واحد غير عامل في الحلقة - يعني طلبات مفقودة ومهلات:
def check_proxy(proxy_url, test_url="https://httpbin.org/ip", timeout=5):
try:
r = requests.get(
test_url,
proxies={"http": proxy_url, "https": proxy_url},
timeout=timeout
)
return r.status_code == 200
except:
return False
# تصفية البروكسي العاملة قبل البدء
working_proxies = [p for p in PROXIES if check_proxy(p)]
print(f"البروكسي العاملة: {len(working_proxies)}/{len(PROXIES)}")
الخطأ 2: تجاهل نوع البروتوكول
لا يمكن للبروكسي HTTP توجيه حركة مرور HTTPS مباشرة (فقط عبر CONNECT). تعمل بروكسي SOCKS5 على مستوى النقل وتدعم أي بروتوكولات. استخدم بروكسي SOCKS5 أو HTTPS لمعظم المواقع الحديثة:
# بروكسي SOCKS5 في requests (يتطلب pip install requests[socks])
proxies = {
"http": "socks5://user:[email protected]:1080",
"https": "socks5://user:[email protected]:1080",
}
# بروكسي HTTPS
proxies = {
"http": "https://user:[email protected]:8080",
"https": "https://user:[email protected]:8080",
}
الخطأ 3: عدم وجود تأخير أسي
إذا قمت بإعادة الطلب مباشرة بعد 429 - فإنك تزيد من تفاقم الوضع. الاستراتيجية الصحيحة هي تأخير أسي مع تذبذب (انحراف عشوائي):
import random
def exponential_backoff(attempt, base=1, max_wait=60):
"""
attempt: رقم المحاولة (بدءًا من 0)
base: تأخير أساسي بالثواني
max_wait: الحد الأقصى للتأخير
"""
wait = min(base * (2 ** attempt), max_wait)
# تذبذب ±25% لمنع herd thunder
jitter = wait * 0.25 * random.uniform(-1, 1)
return wait + jitter
# الاستخدام في منطق إعادة المحاولة
for attempt in range(5):
response = requests.get(url, proxies=proxy)
if response.status_code == 429:
wait = exponential_backoff(attempt)
print(f"تم تحديد حد المعدل. الانتظار {wait:.1f} ثواني (المحاولة {attempt+1})")
time.sleep(wait)
else:
break
الخطأ 4: خيط واحد لجميع البروكسي
إذا كان لديك 50 بروكسي، ولكن خيط تنفيذ واحد - فأنت تستخدم بروكسي واحد فقط في نفس الوقت. استخدم ThreadPoolExecutor أو نهج غير متزامن لاستخدام جميع المجموعة بشكل متوازي:
from concurrent.futures import ThreadPoolExecutor, as_completed
def fetch_url(args):
url, proxy = args
try:
r = requests.get(url, proxies={"https": proxy}, timeout=10)
return url, r.status_code, len(r.text)
except Exception as e:
return url, None, str(e)
# استخدام جميع البروكسي بشكل متوازي
tasks = [(url, proxies[i % len(proxies)]) for i, url in enumerate(urls)]
with ThreadPoolExecutor(max_workers=len(proxies)) as executor:
futures = {executor.submit(fetch_url, task): task for task in tasks}
for future in as_completed(futures):
url, status, size = future.result()
print(f"{url}: {status} ({size})")
الخاتمة والتوصيات
يعد حد المعدل مشكلة يمكن حلها إذا تم التعامل معها بشكل منهجي. الاستنتاجات الرئيسية من هذا الدليل:
- مجموعة البروكسي، وليس بروكسي واحد - الحد الأدنى للعمل الجاد. يتم تحديد عدد البروكسي وفقًا للصيغة: السرعة المستهدفة ÷ حد الخادم على IP.
- استراتيجية التدوير مهمة - لكل طلب للطلبات بدون حالة، جلسات ثابتة للسيناريوهات المفوضة.
- IP ليس المعلمة الوحيدة - يتم تحليل الرؤوس، الكوكيز، بصمة TLS وأنماط السلوك أيضًا بواسطة أنظمة الحماية.
- قم بمعالجة 429 بشكل صحيح - تأخير أسي، رأس Retry-After، تغيير البروكسي عند الحظر.
- نوع البروكسي يعتمد على الهدف - مراكز البيانات لـ APIs المفتوحة، سكنية للأسواق، موبايل لأقصى حماية.
إذا كنت تعمل على تجريف الأسواق (Wildberries، Ozon)، جمع البيانات من APIs المحمية أو الأتمتة بسرعات عالية - نوصي بالبدء بـ البروكسي السكنية: فهي توفر توازنًا مثاليًا بين الخصوصية والسرعة، وعناوين IP الخاصة بها نادرًا ما تدخل قوائم الحظر. لمهام تتطلب أقصى مقاومة للحظر مع تكرار الطلبات العالية، يجب النظر في البروكسي الموبايل - حيث تشارك عناوين IP الخاصة بها آلاف المستخدمين الحقيقيين، مما يجعل الحظر غير مرغوب فيه للغاية لأي موقع.