إذا كنت تبني زاحف ويب في Python باستخدام Crawlee، فمن المحتمل أنك واجهت الحجب بعد عشرات أو مئات الطلبات. تدوير البروكسي في Crawlee لـ Python هو الحل الجوهري لتجاوز قيود المعدل وحجب عناوين IP. في هذا الدليل، نغطي كل ما تحتاجه: من معمارية Crawlee الداخلية، إلى إعداد ProxyConfiguration، وربط SessionPool بعناوين IP سكنية، وصولاً إلى أنماط الإنتاج مثل session.retire() وإدارة التزامن.
تنبيه قانوني: هذه المقالة مخصصة لجمع البيانات العامة فقط. احترم ملف robots.txt وشروط الخدمة لأي موقع. قد يخضع الوصول غير المصرح به لقوانين مثل CFAA في الولايات المتحدة أو GDPR في الاتحاد الأوروبي. فضّل واجهات برمجة التطبيقات الرسمية حيثما أمكن، واستشر فريقًا قانونيًا إذا كنت غير متأكد.
ما هو تدوير البروكسي في Crawlee لـ Python؟
تدوير البروكسي في Crawlee لـ Python يعني توزيع طلبات HTTP/S على عناوين IP متعددة عبر فئة ProxyConfiguration، بحيث لا يأتي كل طلب من نفس IP. يدعم Crawlee وضعين رئيسيين للتدوير: التدوير الدائري (round-robin) الذي يغيّر IP مع كل طلب جديد، والجلسات الثابتة (sticky sessions) التي تربط عنوان IP بمعرّف جلسة محدد عبر المعامل session_id في دالة proxy_configuration.new_url().
الوضعان ليسا متبادلين — بل متكاملان. تستخدم التدوير الدائري للمواقع التي لا تتطلب حالة (stateless)، وتستخدم الجلسات الثابتة للمواقع التي تربط ملفات تعريف الارتباط (cookies) وبصمات المتصفح بعنوان IP، مثل تلك التي تحميها Cloudflare أو DataDome. التزامن بين SessionPool وProxyConfiguration هو ما يجعل Crawlee قويًا: كل جلسة تحصل على IP ثابت، وعند حجب ذلك IP، تتقاعد الجلسة ويتم إنشاء جلسة جديدة بـ IP جديد.
معمارية Crawlee: نظرة داخلية على المكونات
Crawlee for Python هو إطار زحف (crawling framework) مبني على مكتبة Crawlee التي طورتها شركة Apify. يوفر طبقة تجريد موحدة فوق أدوات HTTP والمتصفح، مع إدارة تلقائية لقائمة الطلبات، والتحجيم، والجلسات. فهم هذه المكونات ضروري قبل إضافة طبقة البروكسي.
BeautifulSoupCrawler مقابل PlaywrightCrawler
يقدم Crawlee زاحفين رئيسيين:
- BeautifulSoupCrawler: يستخدم طلبات HTTP خام مع BeautifulSoup لتحليل HTML. سريع، خفيف الموارد، ومناسب للمواقع التي لا تتطلب تنفيذ JavaScript. يستهلك حوالي 50 MB من الذاكرة لكل عامل (worker).
- PlaywrightCrawler: يستخدم متصفحًا كاملًا (Chromium/Firefox) عبر Playwright. أبطأ وأثقل (حوالي 300-500 MB لكل عامل)، لكنه ضروري للمواقع التي تعتمد على JavaScript لعرض المحتوى أو التي تستخدم أنظمة مكافحة البوت المتقدمة.
كلا الزاحفين يشتركان في نفس البنية التحتية: قائمة الطلبات الموحدة، التحجيم التلقائي، وSessionPool. هذا يعني أن إعداد البروكسي يعمل بشكل متطابق مع كليهما — وهو تصميم أنيق يسهل التبديل بين الزاحفين دون إعادة كتابة منطق البروكسي.
قائمة الطلبات الموحدة والتحجيم التلقائي
يستخدم Crawlee RequestQueue لإدارة الطلبات بشكل موحد عبر جميع الزاحفين. كل طلب يُضاف إلى القائمة، ويتم توزيعه على العمال المتاحين. التحجيم التلقائي (AutoscaledPool) يضيف أو يزيل العمال بناءً على حمل النظام وسرعة المعالجة، حتى يصل إلى حد أقصى تحدده أنت (مثل 100 جلسة متزامنة).
هذا التصميم مهم لفهم تدوير البروكسي: كل عامل يستهلك جلسة من SessionPool، وكل جلسة مرتبطة بـ IP عبر ProxyConfiguration. عندما يتسع التجمع التلقائي، يزداد عدد الجلسات — وبالتالي يزداد عدد عناوين IP المستخدمة. إذا لم يكن لديك عناوين بروكسي كافية، فإن الجلسات الجديدة ستعيد استخدام IPs موجودة، مما يزيد خطر الحجب.
SessionPool وربط الجلسات بعناوين IP
SessionPool هو المكون الذي يربط كل شيء معًا. كل جلسة (Session) تحتوي على:
- معرّف فريد (
session.id) - ملفات تعريف ارتباط (cookies) مرتبطة بالجلسة
- بصمة متصفح (User-Agent, headers) إذا كنت تستخدم PlaywrightCrawler
- عنوان IP مرتبط عبر
ProxyConfiguration
عندما تحصل على جلسة من SessionPool، فإن Crawlee يستدعي proxy_configuration.new_url(session_id=session.id) تلقائيًا للحصول على عنوان بروكسي مرتبط بتلك الجلسة. هذا يعني أن ملفات تعريف الارتباط والبصمة وIP كلها متسقة — وهو أمر حاسم للمواقع التي تكتشف التغييرات المريبة في IP أثناء الجلسة.
إعداد ProxyConfiguration بشكل صحيح
فئة ProxyConfiguration في Crawlee هي النقطة المركزية لإدارة البروكسي. يمكنك تمريرها إلى أي زاحف، وستتولى توزيع عناوين البروكسي على الجلسات تلقائيًا. إليك الإعداد الأساسي مع ProxyHat:
from crawlee.proxy_configuration import ProxyConfiguration
proxy_configuration = ProxyConfiguration(
proxy_urls=[
'http://user-country-US-session-sess01:pass@gate.proxyhat.com:8080',
'http://user-country-US-session-sess02:pass@gate.proxyhat.com:8080',
'http://user-country-US-session-sess03:pass@gate.proxyhat.com:8080',
]
)
في هذا المثال، نمرر ثلاثة عناوين بروكسي سكنية من ProxyHat، كل منها بجلسة مختلفة. عندما يستدعي Crawlee new_url(session_id=...)، فإنه يختار عنوانًا بناءً على معرّف الجلسة — مما يضمن ثبات IP طوال عمر الجلسة.
التدوير الدائري مقابل الجلسات الثابتة
للتدوير الدائري البسيط (بدون جلسات ثابتة)، يمكنك استخدام new_url() بدون session_id:
# تدوير دائري: IP مختلف مع كل طلب
proxy_url = await proxy_configuration.new_url()
وللجلسات الثابتة (IP ثابت لكل جلسة):
# جلسة ثابتة: نفس IP طوال عمر الجلسة
proxy_url = await proxy_configuration.new_url(session_id='abc123')
القاعدة العامة: ابدأ بالجلسات الثابتة. التدوير الدائري البحت يكسر الجلسات على المواقع التي تتحقق من تطابق IP مع ملفات تعريف الارتباط، مما يؤدي إلى حجب أسرع. التدوير الدائري مناسب فقط للمواقع البسيطة التي لا تتبع حالة الجلسة.
البروكسي السكني مقابل بروكسي مراكز البيانات
عندما يتعلق الأمر بالمواقع المحمية بأنظمة مكافحة البوت مثل Cloudflare وDataDome، فإن نوع البروكسي يحدث فرقًا كبيرًا. بروكسي مراكز البيانات (datacenter proxies) يأتي من نطاقات IP مسجلة لمراكز بيانات معروفة — وهو ما تكتشفه أنظمة مكافحة البوت بسهولة عبر قواعد بيانات ASN. البروكسي السكني (residential proxies) يأتي من أجهزة منزلية حقيقية بمعرّفات ASN مزود خدمة إنترنت عادي، مما يجعله يبدو كحركة مرور شرعية.
إليك مقارنة عملية بين الأنواع الثلاثة:
| المعيار | البروكسي السكني | بروكسي مراكز البيانات | البروكسي المتنقل |
|---|---|---|---|
| مصدر IP | أجهزة منزلية حقيقية | خوادم في مراكز بيانات | شبكات الهاتف المحمول |
| معدل النجاح على Cloudflare | مرتفع | منخفض جدًا | مرتفع جدًا |
| زمن الاستجابة النموذجي | 200-500ms | <100ms | 300-800ms |
| التكلفة النسبية | متوسطة | منخفضة | مرتفعة |
| الاستخدام الأمثل | المواقع المحمية | المواقع البسيطة | الحجب الصارم |
استراتيجية البروكسي المتدرج (Tiered Proxies)
في الإنتاج، لا تعتمد على نوع واحد من البروكسي. استراتيجية البروكسي المتدرج تعمل كالتالي:
- ابدأ ببروكسي مراكز البيانات للمواقع التي لا تستخدم مكافحة البوت (أرخص وأسرع).
- عند مواجهة حجب (403 أو 429)، انتقل إلى البروكسي السكني.
- إذا استمر الحجب، انتقل إلى البروكسي المتنقل كحل أخير.
يمكنك تنفيذ ذلك في Crawlee عبر إنشاء ProxyConfiguration متعددة والتبديل بينها عند الحجب، أو ببساطة الاعتماد على البروكسي السكني من البداية إذا كنت تعلم أن الهدف محمي. اطّلع على مواقع ProxyHat لمعرفة الدول والمدن المتاحة للبروكسي السكني.
مثال عملي قابل للتشغيل
إليك مثالًا كاملًا لزاحف BeautifulSoupCrawler مع ProxyConfiguration يشير إلى ProxyHat بجلسات سكنية مثبتة على دولة الولايات المتحدة:
import asyncio
from crawlee.beautifulsoup_crawler import (
BeautifulSoupCrawler,
BeautifulSoupCrawlingContext,
)
from crawlee.proxy_configuration import ProxyConfiguration
def generate_proxy_urls(count: int = 5) -> list[str]:
\"\"\"يولّد قائمة عناوين بروكسي مع جلسات فريدة.\"\"\"
urls = []
for i in range(count):
session_id = f'sess-{i:04d}'
username = f'user-country-US-session-{session_id}'
urls.append(f'http://{username}:pass@gate.proxyhat.com:8080')
return urls
async def main() -> None:
proxy_configuration = ProxyConfiguration(
proxy_urls=generate_proxy_urls(count=10),
)
crawler = BeautifulSoupCrawler(
proxy_configuration=proxy_configuration,
max_request_retries=3,
max_requests_per_crawl=500,
request_handler_timeout=60,
)
@crawler.router.default_handler
async def handler(context: BeautifulSoupCrawlingContext) -> None:
context.log.info(
f'Processing {context.request.url} '
f'via {context.proxy_info}'
)
title = context.soup.find('title')
if title:
await context.push_data({
'url': context.request.url,
'title': title.text.strip(),
})
# استخراج الروابط الداخلية
for link in context.soup.find_all('a', href=True):
await context.enqueue_links(
links=[link['href']],
label='INTERNAL',
)
await crawler.run(['https://example.com'])
if __name__ == '__main__':
asyncio.run(main())
لاحظ كيف نولّد أسماء مستخدمين فريدة لكل جلسة باستخدام نمط user-country-US-session-{session_id}. هذا يضمن أن كل جلسة في SessionPool تحصل على IP سكني مختلف ومثبت على الولايات المتحدة. يمكنك تمرير city أيضًا لمزيد من الدقة الجغرافية: user-country-US-city-newyork-session-abc123.
للحصول على تفاصيل كاملة حول معاملات اسم المستخدم في ProxyHat، راجع وثائق ProxyHat. ولمعرفة خيارات التسعير المتاحة، زر صفحة الأسعار.
أنماط الإنتاج: التعامل مع الحجب وإعادة المحاولة
في الإنتاج، الحجب أمر لا مفر منه. الفرق بين زاحف جيد وزاحف رائع هو كيفية التعامل معه. إليك الأنماط الأساسية التي يجب تطبيقها:
تقاعد الجلسة عند الحجب
عندما يتلقى الزاحف استجابة 403 أو 429، يجب أن يتقاعد الجلسة الحالية ويعيد محاولة الطلب بجلسة جديدة (وبالتالي IP جديد). في Crawlee، هذا يتم عبر context.session.retire():
@crawler.router.default_handler
async def handler(context: BeautifulSoupCrawlingContext) -> None:
status = context.http_response.status_code
if status in (403, 429):
context.log.warning(
f'Blocked on {context.request.url} '
f'(status {status}), retiring session'
)
if context.session:
context.session.retire()
raise Exception(
'Blocked by anti-bot, will retry with new session'
)
if status == 200:
title = context.soup.find('title')
if title:
await context.push_data({
'url': context.request.url,
'title': title.text.strip(),
})
else:
context.log.warning(f'Unexpected status {status}')
هذا النمط يضمن أن IP المحجوب لا يُعاد استخدامه للجلسات الجديدة. SessionPool سيستبدل الجلسة المتقاعدة بجلسة جديدة تحصل على IP جديد من ProxyConfiguration. الطلب نفسه سيُعاد تلقائيًا حتى يصل إلى max_request_retries.
إعداد max_request_retries
حدد max_request_retries بحكمة. القيمة الافتراضية هي 3 إعادات محاولة، وهو رقم معقول لمعظم الحالات. إذا رفعتها إلى 5 أو أكثر، فإنك تخاطر بإهدار طلبات البروكسي على طلبات قد تفشل دائمًا. إذا خفضتها إلى 1، فإنك تستسلم بسرعة كبيرة. القاعدة: 3 إعادة محاولة كحد أقصى، مع تقاعد الجلسة في كل محاولة فاشلة.
التحجيم التلقائي والتزامن
يتحكم AutoscaledPool في عدد العمال المتزامنين. بشكل افتراضي، يبدأ بعدد صغير ويتسع بناءً على الأداء. يمكنك التحكم في الحدود عبر معاملات الزاحف:
crawler = BeautifulSoupCrawler(
proxy_configuration=proxy_configuration,
max_request_retries=3,
max_requests_per_crawl=500,
autoscaled_pool_options={
'min_concurrency': 5,
'max_concurrency': 50,
},
)
كل عامل متزامن يستهلك جلسة — وبالتالي IP — من SessionPool. إذا حددت max_concurrency=50، فأنت بحاجة إلى 50 جلسة على الأقل في SessionPool، ويفضل 50 عنوان بروكسي فريد في ProxyConfiguration. إذا كان لديك عناوين بروكسي أقل من العمال المتزامنين، فإن بعض الجلسات ستشارك نفس IP، مما يزيد خطر الحجب.
التشغيل في حاويات (Containerization)
للتوسع أفقيًا، شغّل عدة حاويات من الزاحف باستخدام Docker أو Kubernetes. كل حاوية يجب أن تستخدم مجموعة جلسات فريدة لتجنب تداخل عناوين IP. يمكنك تمرير معرّف الحاوية كمتغير بيئة واستخدامه في توليد أسماء الجلسات:
import os
container_id = os.environ.get('CONTAINER_ID', 'local')
session_id = f'{container_id}-sess-{i:04d}'
username = f'user-country-US-session-{session_id}'
هذا يضمن أن كل حاوية تستخدم جلسات (وبالتالي IPs) فريدة، حتى لو كانت جميعها تشير إلى نفس بوابة ProxyHat على gate.proxyhat.com:8080. عند تشغيل 10 حاويات بـ max_concurrency=20 لكل منها، فإنك تستهلك حتى 200 جلسة متزامنة — تأكد من أن خطة البروكسي لديك تدعم هذا الحجم.
متى تتجنب تصفح المتصفح
PlaywrightCrawler أقوى بكثير من BeautifulSoupCrawler، لكنه أيضًا أبطأ بـ 10-20 ضعفًا وأكثر استهلاكًا للموارد. لا تلجأ إلى تصفح المتصفح إلا عندما:
- الموقع يعتمد على JavaScript لعرض المحتوى الأساسي (SPA).
- الموقع يستخدم تحديات مكافحة البوت التي تتطلب تنفيذ JavaScript (مثل Cloudflare's JS challenge).
- تحتاج إلى التفاعل مع الصفحة (النقر، التمرير، ملء النماذج).
في جميع الحالات الأخرى — بما في ذلك معظم مواقع التجارة الإلكترونية والمدونات والمواقع الإخبارية — فإن BeautifulSoupCrawler مع بروكسي سكني كافٍ وأسرع بكثير. إذا كنت تتبع نتائج محركات البحث، راجع دليل تتبع SERP الخاص بنا.
الاعتبارات الأخلاقية والقانونية
قبل بناء أي زاحف، ضع هذه القواعد في الاعتبار:
- البيانات العامة فقط: لا تتجاوز مصادقة أو جدران حماية. إذا كانت البيانات تتطلب تسجيل دخول، فاستخدم واجهة برمجة التطبيقات الرسمية.
- احترم robots.txt: تحقق من ملف
robots.txtللموقع واتبع قواعده. Crawlee لا يفعل ذلك تلقائيًا بشكل افتراضي، لذا أضف منطق التحقق بنفسك. - احترم معدلات الطلبات: حتى مع البروكسي، لا ترسل آلاف الطلبات في الثانية. اضبط
max_concurrencyوrequest_handler_timeoutلتكون معقولًا. - فضل واجهات برمجة التطبيقات الرسمية: إذا كان الموقع يوفر API، استخدمه بدلاً من الزحف. أرخص، أسرع، وأكثر موثوقية.
- التوافق القانوني: قد يخضع الزحف غير المصرح به لقوانين مثل CFAA في الولايات المتحدة وGDPR في الاتحاد الأوروبي. استشر فريقًا قانونيًا للمشاريع الكبيرة.
لمزيد من المعلومات حول حالات استخدام الزحف الأخلاقي، راجع دليل الزحف على الويب.
النقاط الرئيسية
تدوير البروكسي في Crawlee لـ Python ليس مجرد تمرير قائمة IPs — بل تكامل عميق بين
ProxyConfigurationوSessionPoolوAutoscaledPool. إليك الخلاصة:
- استخدم الجلسات الثابتة افتراضيًا: ربط IP بجلسة عبر
session_idيحافظ على اتساق ملفات تعريف الارتباط والبصمة وIP. - البروكسي السكني للمواقع المحمية: بروكسي مراكز البيانات يُحجب بسرعة على Cloudflare وDataDome. استخدم البروكسي السكني من ProxyHat عبر
gate.proxyhat.com:8080. - تقاعد الجلسات عند الحجب: استدعِ
session.retire()عند استجابة 403/429، ثم أعد المحاولة بجلسة جديدة. - طابق التزامن مع عدد IPs: إذا كان
max_concurrency=50، فأنت بحاجة إلى 50 عنوان بروكسي فريد على الأقل. - ابدأ بـ BeautifulSoupCrawler: لا تلجأ إلى PlaywrightCrawler إلا عند الحاجة الفعلية لتنفيذ JavaScript.
- احترم القوانين والأخلاق: بيانات عامة فقط، احترم
robots.txt، وفضل واجهات برمجة التطبيقات الرسمية.






