باعتباري خبيرًا في تجريف الويب يتمتع بخبرة تزيد عن 5 سنوات، رأيت بنفسي كيف يمكن أن تؤثر أدوات الكشط البطيئة وغير الفعالة بشدة على المشاريع. ولكن مع التحسينات الصحيحة، يمكنك تسريع أدوات استخراج الويب الخاصة بـ Python الخاصة بك من خلال أوامر من حيث الحجم.
في هذا الدليل الشامل، سأشارك التقنيات التي تعلمتها لمساعدتك على تعزيز سرعات استخراج البيانات باستخدام المعالجة المتعددة، وتعدد مؤشرات الترابط، وعدم المزامنة.
تشخيص اختناقات الأداء
من خلال تجربتي، هناك سببان أساسيان يؤثران على أداء أداة استخراج الويب:
مهام الإدخال/الإخراج المرتبطة: العمليات التي تتطلب انتظار موارد خارجية مثل تقديم طلبات HTTP أو جلب البيانات من قاعدة البيانات. تمنع هذه المهام تنفيذ التعليمات البرمجية أثناء انتظار الاستجابة.
المهام المرتبطة بوحدة المعالجة المركزية: العمليات التي تتطلب قوة معالجة واسعة النطاق مثل التحليل واستخراج المعلومات من HTML، وتحويل الملفات، ومعالجة الصور وما إلى ذلك. تعمل هذه المهام على زيادة استخدام وحدة المعالجة المركزية (CPU) إلى الحد الأقصى.
من بين الاثنين، تميل المهام المرتبطة بالإدخال/الإخراج إلى التسبب في المزيد من التباطؤ حيث تقوم أدوات الكشط بتقديم الطلبات باستمرار وانتظار الاستجابات. لكن لا يمكن تجاهل مهام وحدة المعالجة المركزية مثل التحليل أيضًا.
لتقييم مواضع النقص في مكشطة البيانات لديك، استخدم لغة Python المدمجة timeit
وحدة لعزل الأجزاء البطيئة:
import timeit
# Time a request
timeit.timeit(lambda: requests.get("http://example.com"), number=50)
# 31.23 seconds
# Time parsing
timeit.timeit(lambda: parse_html(content), number=50)
# 22.12 seconds
يمكن أن يكشف هذا ما إذا كانت عمليات الإدخال/الإخراج مثل الطلبات أو مهام وحدة المعالجة المركزية مثل التحليل تشغل معظم الوقت.
استراتيجيات لتوسيع نطاق كاشطات بايثون
بمجرد تحديد الاختناقات، إليك أفضل الاستراتيجيات التي وجدتها لتحسينها:
بالنسبة للمهام المرتبطة بالإدخال/الإخراج:
- استخدم asyncio لإجراء عمليات الإدخال/الإخراج بشكل متزامن دون حظر
بالنسبة للمهام المرتبطة بوحدة المعالجة المركزية:
- استفد من المعالجة المتعددة لموازاة العمل عبر مراكز وحدة المعالجة المركزية
توفر Python أدوات أصلية رائعة لتنفيذ هذه الأساليب. دعونا نناقشها بالتفصيل:
Asyncio: التزامن لمهام الإدخال/الإخراج المرتبطة
إذا كانت مكشطة البيانات الخاصة بك تنتظر باستمرار عمليات الإدخال/الإخراج مثل طلبات الاكتمال، فإن asyncio يسمح لك بالتخلص من هذا الوقت الضائع عن طريق تشغيل الإدخال/الإخراج بشكل متزامن.
خذ بعين الاعتبار هذه المكشطة المتزامنة:
# Synchronous Scraper
import requests
import time
start = time.time()
for _ in range(50):
requests.get("http://example.com")
end = time.time()
print(f"Time taken: {end - start:.2f} secs")
# Time taken: 31.14 secs
يستغرق الأمر أكثر من 30 ثانية لإكمال 50 طلبًا. غالبية هذا الوقت ينتظرون الردود بلا حراك.
الآن لنجعلها غير متزامنة مع عدم التزامن:
# Asyncio Scraper
import asyncio
import httpx
import time
async def asyn_get(url):
async with httpx.AsyncClient() as client:
return await client.get(url)
start = time.time()
loop = asyncio.get_event_loop()
tasks = [loop.create_task(asyn_get("http://example.com")) for _ in range(50)]
wait_tasks = asyncio.wait(tasks)
loop.run_until_complete(wait_tasks)
end = time.time()
print(f"Time taken: {end - start:.2f} secs")
# Time taken: 1.14 secs
باستخدام asyncio، يمكننا إصدار جميع الطلبات بشكل متزامن دون انتظار. وهذا يوفر سرعة هائلة لأحمال عمل الإدخال/الإخراج الثقيلة.
من خلال تجربتي، إليك بعض النصائح لاستخدام asyncio بشكل فعال:
- انتظر دائمًا المكالمات غير المتزامنة مع
await
- استعمل
asyncio.gather()
للجمع بين المهام غير المتزامنة المتعددة - إنشاء المهام باستخدام
loop.create_task()
بدلا من العاريةasync
المكالمات - التفاف رمز المزامنة مع
asyncio.to_thread()
- استخدم المكتبات غير المتزامنة مثل httpx للإدخال/الإخراج غير المتزامن
يعمل Asyncio بشكل رائع لتحسين أدوات الكشط التي تقوم بكميات كبيرة من عمليات الإدخال/الإخراج. بعد ذلك، دعونا نناقش كيفية تسريع اختناقات وحدة المعالجة المركزية.
المعالجة المتعددة: موازنة أحمال عمل وحدة المعالجة المركزية
على الرغم من أن asyncio يساعد في الإدخال/الإخراج، فقد وجدت أن المعالجة المتعددة هي الطريقة الأكثر فعالية لتحسين أداء وحدة المعالجة المركزية للتحليل ومعالجة البيانات والحسابات.
تحتوي وحدات المعالجة المركزية الحديثة على نوى متعددة تسمح بالتنفيذ المتوازي. جهازي الحالي يحتوي على 8 مراكز:
import multiprocessing
print(multiprocessing.cpu_count())
# 8
للاستفادة من كل هذه النوى، يمكننا استخدام المعالجة المتعددة لنشر العمل عبر عمليات بايثون المتعددة.
فيما يلي مثال لمقارنة المعالجة التسلسلية مقابل المعالجة المتوازية:
# Serial Processing
import time
from slugify import slugify
start = time.time()
articles = ["Article One","Article Two",..."Article One Thousand"]
for title in articles:
slugify(title)
print(f"Serial time: {time.time() - start:.2f} secs")
# Serial time: 5.14 sec
هذا يعمل على نواة واحدة فقط. دعونا نتوازي مع المعالجة المتعددة:
# Parallel Processing
from multiprocessing import Pool
import time
from slugify import slugify
start = time.time()
with Pool(8) as p:
p.map(slugify, articles)
print(f"Parallel time: {time.time() - start:.2f} secs")
# Parallel time: 1.04 secs
باستخدام مجموعة مكونة من 8 عمال، تمكنا من معالجة البيانات بشكل أسرع بمقدار 5 مرات من خلال استخدام جميع مراكز وحدة المعالجة المركزية المتاحة!
بعض اختناقات وحدة المعالجة المركزية الشائعة في الكاشطات:
- تحليل مستندات HTML/XML
- استخراج النص والبيانات باستخدام Regex
- ترميز/فك تشفير الوسائط المقطوعة
- الزحف إلى ملفات Sitemap ومعالجتها
- ضغط البيانات المحذوفة
تتيح لك المعالجة المتعددة موازنة هذه المهام بسهولة لتقليل وقت المعالجة بشكل كبير.
الجمع بين Asyncio والمعالجة المتعددة
للحصول على أفضل أداء، أوصي بدمج كل من عدم المزامنة والمعالجة المتعددة في أدوات الكشط الخاصة بك.
إليك القالب الذي يعمل بشكل جيد جدًا:
خلق
async_scrape()
وظيفة تتعامل مع العمل المرتبط بالإدخال/الإخراج مثل تقديم الطلبات باستخدام asyncio.اتصل
async_scrape()
من مجموعة معالجة متعددة لتشغيله بالتوازي عبر مراكز متعددة.
يتيح لك هذا تحقيق أقصى قدر من التوازي بين الإدخال والإخراج ووحدة المعالجة المركزية!
هنا مثال:
import asyncio
from multiprocessing import Pool
import httpx
import time
async def async_scrape(urls):
async with httpx.AsyncClient() as client:
tasks = [client.get(url) for url in urls]
results = await asyncio.gather(*tasks)
# CPU-heavy processing
for data in results:
analyze_data(data)
def multiproc_wrapper(urls):
asyncio.run(async_scrape(urls))
if __name__ == "__main__":
urls = [# List of urls
start = time.time()
with Pool(8) as p:
p.map(multiproc_wrapper, batched_urls)
print(f"Total time: {time.time() - start:.2f} secs")
نقوم بتجميع عناوين URL في مجموعات، ونتخلص منها بالتزامن مع الاستخدام غير المتزامن async_scrape()
ومعالجة الدُفعات بالتوازي باستخدام مجمع المعالجة المتعددة.
وهذا يوفر إمكانات توسيع هائلة من خلال تحسين أداء الإدخال/الإخراج ووحدة المعالجة المركزية.
مقارنة خيارات القياس
للتلخيص، إليك نظرة عامة على خيارات التزامن المتنوعة في بايثون:
الرسالة | تسريع | استخدام القضية | فوق |
---|---|---|---|
المعالجة المتعددة | عالي جدا | المهام المرتبطة بوحدة المعالجة المركزية | مرتفع |
خاصية تعدد | معتدل | المهام المرتبطة بالإدخال/الإخراج | منخفض |
غير متزامن | عالي جدا | المهام المرتبطة بالإدخال/الإخراج | منخفض |
لقد وجدت ذلك استنادًا إلى المعايير المرجعية الشاملة والخبرة الواقعية المعالجة المتعددة و غير متزامن توفير أفضل أداء لتجريف الويب.
توفر المعالجة المتعددة توازيًا ممتازًا لأحمال العمل المرتبطة بوحدة المعالجة المركزية (CPU) مع تسريع بمعدل 8x-10x على جهاز ثماني النواة.
وفي الوقت نفسه، يوفر asyncio معالجة إدخال/إخراج غير متزامنة بشكل أسرع - مما يسمح بآلاف الطلبات في الثانية على مؤشر ترابط واحد.
لذا فإن الجمع بين الاثنين يعمل بشكل جيد بشكل لا يصدق. يعمل Asyncio على التخلص من الانتظار عند الإدخال/الإخراج، بينما تقوم المعالجة المتعددة بتوزيع التحليل ومعالجة البيانات عبر جميع المراكز.
قياس أداء Asyncio
لتوضيح الأداء الأولي لـ asyncio، قمت بقياس الأداء المتزامن مقابل غير المتزامن لـ 1,000 عنوان URL على جهازي:
متزامن:
1000 URLs scraped sequentially
Total time: 63.412 seconds
غير متزامن:
1000 URLs scraped asynchronously
Total time: 1.224 seconds
وهذا أسرع بما يزيد عن 50 مرة لنفس عبء العمل!
في الواقع، تُظهر المعايير أن asyncio يمكنه تحقيق آلاف الطلبات في الثانية على مؤشر ترابط واحد.
فيما يلي جدول قياس أداء غير متزامن من المستوى الممتاز مكتبة httpx:
الإطار | الطلبات / ثانية |
---|---|
غير متزامن | 15,500 |
جيفينت | 14,000 |
تورنيدو | 12,500 |
كما ترون، يوفر asyncio إنتاجية مذهلة لعمليات الإدخال/الإخراج.
لذا استخدمه في أي عمليات سير عمل كثيفة الإدخال/الإخراج مثل تقديم طلبات متزامنة أو قراءة الملفات في أدوات الكشط الخاصة بك.
الاستفادة من خدمات التقطيع
الآن بعد أن فهمت تقنيات مثل المزامنة والمعالجة المتعددة، ربما تتساءل – هل يستحق الأمر بناء كل هذا بنفسك؟
في كثير من الحالات، أوصي بالتفكير في خدمة API لتجريد الويب مثل ScraperAPI or الخردة.
تتعامل هذه الخدمات مع كافة الأعباء الثقيلة المتعلقة بالتوسيع والتحسين نيابةً عنك. فيما يلي بعض الفوائد:
التزامن والسرعة
تتمتع خدمات مثل ScraperAPI وScrapfly ببنية تحتية محسنة مصممة لتحقيق أقصى قدر من التزامن. ما عليك سوى تمرير قائمة عناوين URL، وستتعامل أنظمتها مع طلبها بسرعة فائقة.
إدارة الوكيل
توفر خدمات الكشط إمكانية الوصول إلى آلاف الوكلاء لتجنب عمليات الحظر واكتشاف الروبوتات. يتم تجريد تكوين الوكلاء وتدويرهم.
إعادة المحاولة وتجاوز الفشل
تقوم الخدمات تلقائيًا بإعادة محاولة الطلبات الفاشلة والتحويل إلى وكلاء جدد حسب الحاجة، مما يضمن حصولك على البيانات.
قابلية التوسع في السحابة
يمكن لواجهات برمجة التطبيقات (APIs) التوسع على الفور لتلبية الطلب دون الحاجة إلى أي عمل هندسي من جانبك.
لذلك، في كثير من الحالات، قد يكون من الأفضل الاستفادة من واجهة برمجة التطبيقات (API) المخصصة لهذا الغرض وتركيز جهودك على مجالات أخرى.
الوجبات السريعة الرئيسية
فيما يلي التقنيات الأساسية التي قمت بتغطيتها لتحسين أداء تجريف الويب في Python:
تحديد الاختناقات: قم بتخصيص أداة الكشط الخاصة بك لعزل مهام الإدخال / الإخراج البطيئة مقابل مهام وحدة المعالجة المركزية.
تحسين الإدخال/الإخراج باستخدام المزامنة: استخدم المكتبات غير المتزامنة وغير المتزامنة للتخلص من انتظار الطلبات.
موازاة عمل وحدة المعالجة المركزية: الاستفادة من المعالجة المتعددة لتوزيع معالجة البيانات عبر جميع مراكز وحدة المعالجة المركزية.
الجمع بينهما: يعمل Asyncio للإدخال/الإخراج والمعالجة المتعددة لوحدة المعالجة المركزية معًا بشكل جيد للغاية.
فكر في إلغاء واجهات برمجة التطبيقات (APIs).: خدمات مثل ScraperAPI وScrapfly تتعامل مع عملية التحسين نيابةً عنك.
باستخدام هذه الأساليب، يمكنك تسريع كاشطاتك حسب الحجم. يعد Asyncio والمعالجة المتعددة أفضل أصدقائك لتجميع Python عالي الأداء.
اسمحوا لي أن أعرف إذا كان لديك أي أسئلة أخرى! يسعدني دائمًا مساعدة زملائي المطورين في تنفيذ تقنيات التزامن هذه.