انتقل إلى المحتوى

سرعة تجريف الويب: العمليات والخيوط وعدم المزامنة

باعتباري خبيرًا في تجريف الويب يتمتع بخبرة تزيد عن 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 والمعالجة المتعددة

للحصول على أفضل أداء، أوصي بدمج كل من عدم المزامنة والمعالجة المتعددة في أدوات الكشط الخاصة بك.

إليك القالب الذي يعمل بشكل جيد جدًا:

  1. خلق async_scrape() وظيفة تتعامل مع العمل المرتبط بالإدخال/الإخراج مثل تقديم الطلبات باستخدام asyncio.

  2. اتصل 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 عالي الأداء.

اسمحوا لي أن أعرف إذا كان لديك أي أسئلة أخرى! يسعدني دائمًا مساعدة زملائي المطورين في تنفيذ تقنيات التزامن هذه.

الوسوم (تاج):

الانضمام إلى محادثة

لن يتم نشر عنوان بريدك الإلكتروني. الحقول المشار إليها إلزامية *