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

كيف تنتظر تحميل الصفحة في السيلينيوم؟ دليل الخبراء

دعني أخمن - لقد بدأت في تجريف موقع يحتوي على السيلينيوم وفجأة تواجه أخطاء مهلة مخيفة، واستثناءات عناصر قديمة، ومحددات مواقع غير مستقرة. تبدو مألوفة؟

كثير منا كانوا هناك! في شبكة الويب الديناميكية اليوم، يعد الانتظار بشكل صحيح حتى يتم تحميل الصفحات بالكامل قبل التفاعل أمرًا بالغ الأهمية لتحقيق أتمتة موثوقة.

في هذا الدليل الشامل الذي يزيد عن 3200 كلمة، سأستفيد من خبرتي التي تزيد عن 5 سنوات كخبير محترف في استخراج الويب لاستكشاف الأساليب المختلفة وأفضل الممارسات للانتظار الجميل في السيلينيوم.

سواء كنت مبتدئًا أو محترفًا متمرسًا، فإن منطق الانتظار القوي هو أداة لا بد منها لتحقيق الاستقرار. دعونا الغوص في!

لماذا لا يمكنك التسرع في ذلك

في الأيام الأولى للويب، كانت الصفحات في الغالب عبارة عن HTML بسيط يتم عرضه بشكل تسلسلي. يمكن أن تبدأ أدوات الكشط في الاستخراج فورًا عند تحميل الصفحة.

لكن شبكة الإنترنت اليوم ديناميكية للغاية. وفق بحث جوجل، متوسط ​​الوقت للرسم الأول هو 1.7 ثانية، ولكن متوسط ​​الوقت للتفاعل الكامل هو هائل 15 ثانية. هذا وقت طويل لتحميل المحتوى.

كمكشطة، إذا اندفعت بسرعة كبيرة، فإليك بعض المشكلات الشائعة التي ستواجهها:

  • أخطاء النقر على الزر لأن العنصر لم يتم عرضه بعد
  • محاولة قراءة البيانات من جدول لم يقم بتحميل محتوى الخادم
  • إرسال نص إلى مدخلات غير مرئية على الشاشة
  • كشط العناصر الفارغة التي سيتم ملؤها بعد تحميل الصفحة

هذه الأنواع من الاستثناءات هي أعراض تحتاج إلى الانتظار لفترة أطول حتى تصبح الصفحة جاهزة قبل التفاعل.

بالأرقام: أوقات تحميل الصفحة

لفهم المدة التي قد نحتاجها للانتظار، دعنا نلقي نظرة على بعض المقاييس الواقعية حول أداء تحميل الصفحة من تقرير حالة الويب لعام 2020 بواسطة أكامي:

  • متوسط ​​الوقت للتفاعل: 15s
  • متوسط ​​وزن الصفحة: 2744KB
  • متوسط ​​عدد الطلبات: 105
  • متوسط ​​الصور لكل صفحة: 53
  • بايتات جافا سكريبت لكل صفحة: 453KB

أصبحت الصفحات أكبر وأكثر تعقيدًا اليوم، مع حدوث المزيد من العمل بعد الاستجابة الأولية. من الضروري أن تنتظر الكاشطات التفاعل، وليس فقط الطلاء الأول.

الاستثناءات الشائعة الناجمة عن عدم الانتظار

فيما يلي بعض الاستثناءات المحددة التي يمكن أن تحدث عندما لا تكون العناصر جاهزة بعد:

  • StaleElementReferenceException - تمت إزالة العنصر من DOM بعد الجلب
  • ElementNotInteractableException - محاولة النقر فوق العنصر غير المرئي
  • NoSuchElementException - انتهت مهلة البحث لأن العنصر غير موجود بعد

يشير كل واحد من هذه العناصر إلى أن المكشطة تتطلب المزيد من الانتظار.

الانتظار الصريح هو صديقك

لتجنب هذه الأخطاء، نحتاج إلى الانتظار حتى يتم عرض الصفحة بالكامل قبل التفاعل. هناك طريقتان رئيسيتان في السيلينيوم:

الانتظار الضمني – ضبط وقت الانتظار العالمي على السائق

الانتظار الصريح - انتظر حتى تحدث ظروف معينة

يُفضل الانتظار الصريح كثيرًا على الانتظار الضمني في معظم الحالات. دعونا نفهم لماذا.

الانتظار الضمني: نهج المطرقة

يضبط الانتظار الضمني مهلة على برنامج التشغيل لاستقصاء DOM عند البحث عن العناصر. وهذا يعني في أي وقت تتصل فيه:

driver.find_element_by_id(‘some-id‘)

سيعيد برنامج التشغيل المحاولة حتى مدة الانتظار الضمنية لتحديد موقع هذا العنصر قبل طرح NoSuchElementException.

يمكنك استخدامه مثل:

driver = webdriver.Chrome()
driver.implicitly_wait(10) 

الآن ستتم إعادة محاولة جميع عمليات البحث لمدة تصل إلى 10 ثوانٍ للعثور على العناصر إذا لم تكن موجودة على الفور.

الجانب السلبي هو أنه ينتظر كل محدد مواقع، حتى تلك التي لا حاجة إليها لتحديد مدى جاهزية الصفحة. هذا يمكن أن يبطئ مكشطة الخاص بك حقا.

فكر في فترات الانتظار الضمنية مثل إضافة فترة نوم مدتها 5 ثوانٍ لكل عنصر يتم جلبه. إنه يضيف!

دقة الانتظار الصريح

يتيح لنا الانتظار الصريح الانتظار بدقة لشروط محددة تشير إلى الاستعداد قبل المتابعة.

الأفكار الرئيسية هي:

  • انتظر فقط عند الحاجة – تجنب الانتظار غير الضروري الذي لا علاقة له بجاهزية الصفحة
  • شروط دقيقة – انتظر العناصر أو الحالات المحددة، وليس فقط الوقت الشامل
  • مرونة - تخصيص منطق الانتظار لكل صفحة بشروط مختلفة
  • مقروء - من السهل فهم النية عند إعادة النظر في الكود القديم

فيما يلي مثال نموذجي لانتظار ظهور عنصر:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait 

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "myDynamicElement"))
)

يؤدي هذا إلى إيقاف التنفيذ مؤقتًا حتى يتم تحميل العنصر ذو المعرف "myDynamicElement"، أو مرور 10 ثوانٍ.

الشروط المتوقعة المفيدة الأخرى المقدمة من السيلينيوم تتضمن:

  • title_contains() - انتظر حتى يتم تحديث عنوان الصفحة
  • staleness_of() – انتظر حتى لا يتم ربط العنصر بـ DOM
  • element_to_be_clickable() - انتظر حتى يصبح العنصر مرئيًا وممكّنًا

دقات صريحة ضمنية: مثال على العالم الحقيقي

دعونا نقارن بين الانتظارين بمثال حقيقي.

لنفترض أنني أقوم بتجميع موقع يحتوي على شريط تنقل ولوحة على الجانب الأيسر ومحتوى رئيسي.

العنصر الأساسي الذي أحتاج إلى انتظاره هو المعرف "#main-content" حيث يتم عرض بياناتي.

مع الانتظار الضمني:

  • تتم إضافة 10 ثوانٍ إلى كل عنصر بحث، حتى لو لم تكن هناك حاجة إليه
  • لا تزال عرضة لأخطاء العناصر التي لا معنى لها إذا كانت سريعة جدًا

مع الانتظار الصريح:

  • انتظر فقط عند الحاجة لمحدد المحتوى الرئيسي #
  • تجنب الانتظار غير الضروري للتنقل واللوحة الجانبية
  • انتظر على وجه التحديد حتى يتم تحميل البيانات قبل المتابعة

من خلال الانتظار بشكل انتقائي لشرط جاهزية واحد مثل العنصر، أتجنب التأخير غير الضروري.

أنماط الانتظار الصريح الفعال

الآن بعد أن أصبحت مقتنعًا بأن الانتظار الصريح هو الحل الأمثل، دعنا نستكشف بعض أفضل الممارسات لاستخدامها بفعالية.

انتظار تحميل الصفحة

يعد انتظار حالة جاهزية المستند أسلوبًا شائعًا لتحديد وقت اكتمال التحميل:

WebDriverWait(driver, 10).until(
   lambda d: d.execute_script(‘return document.readyState‘) == ‘complete‘
)

يقوم هذا باستقصاء المتصفح حتى تصبح حالة الاستعداد "مكتملة"، مما يشير إلى تحميل جميع الأصول.

هناك نمط خفيف الوزن يراقب عناصر محددة عالية المستوى:

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "main-content"))
) 

وينجح هذا عند تحميل قسم المحتوى الرئيسي، دون انتظار أي شيء آخر.

انتظار كل إجراء

يمكنك أيضًا الانتظار مباشرةً قبل اتخاذ إجراء، مثل النقر فوق عنصر:

menu = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "top-menu"))
)

submenu = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "submenu"))
)

submenu.click()

وهذا يضمن أن القائمة العلوية والقائمة الفرعية جاهزة قبل النقر.

الإنتظار الموازي

انتظار عدة شروط يمكن أن يؤكد أن الصفحة جاهزة:

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "header")),
    EC.presence_of_element_located((By.ID, "footer")), 
    EC.presence_of_element_located((By.ID, "main"))
)

إن طلب تحميل الرأس والتذييل والمحتوى الرئيسي يقلل من النتائج الإيجابية الخاطئة.

انتظارات متسلسلة ومتداخلة

بالنسبة للسيناريوهات المتقدمة، يمكنك أيضًا دمج فترات الانتظار:

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "dropdown"))
)

menu = WebDriverWait(element, 10).until(
    EC.presence_of_element_located((By.ID, "menu"))  
)

هذا ينتظر أولاً العنصر الأصلي، ثم العنصر الفرعي داخل ذلك العنصر.

AJAX ينتظر الاقتراع

يتم تحميل بعض المواقع عبر طلبات AJAX المستمرة. يمكنك تكرار انتظار التغييرات:

while True:

    current_count = driver.find_element_by_id(‘result-count‘).text

    # If count changed since last check, page is still loading
    if current_count != previous_count:
        previous_count = current_count
        continue 

    break # Page loaded!

يقوم هذا باستقصاء عنصر يبحث عن التغييرات لاكتشاف التحميل.

الانتظار غير المتزامن

في الأطر غير المتزامنة مثل asyncio، يمكنك انتظار الوعود:

await page.waitForSelector(‘#content‘)

بناء الجملة مختلف بعض الشيء ولكنه يوفر انتظارًا غير متزامن.

تركيبة ضمنية + صريحة

يمكنك أيضًا الجمع بين فترات الانتظار الضمنية والصريحة:

driver.implicitly_wait(10) 

my_element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "my-element"))
)

بهذه الطريقة يكون لديك انتظار عالمي بالإضافة إلى انتظار محدد. فقط تأكد من أنهم يستخدمون فترات معقولة.

اختيار المواقع التي تشير إلى الاستعداد

عند تحديد محددات المواقع التي سيتم انتظارها، فأنت تريد العناصر التي تطابق هذه المعايير:

  • تظهر متأخرة في عملية التحميل
  • لديك معرفات أو فئات فريدة لن تتغير
  • تقع فوق الطية لإجراء فحوصات سريعة
  • من غير المرجح أن يتم نقلها عن طريق تغييرات الموقع
  • لا تتم إزالتك من DOM وتصبح قديمة

بعض الأمثلة الشائعة هي:

  • تم تحميل الرأس الرئيسي أو التنقل بعد الأصول
  • حاويات المحتوى الأساسي أو عناصر واجهة المستخدم
  • تذييل الصفحة
  • عناصر واجهة المستخدم الديناميكية الصغيرة مثل الأزرار

تعد مؤشرات التحميل مثل المغازل أيضًا بمثابة محفزات انتظار رائعة عند اختفائها.

ضبط المهلات للانتظار الأمثل

يمكن أن يؤدي تحديد المهلات لفترة طويلة جدًا إلى إبطاء أداة الكشط الخاصة بك - ولكن قد يؤدي تعيين المهلات القصيرة جدًا إلى حدوث أعطال غير متوقعة.

فيما يلي بعض أفضل الممارسات بشأن فترات الضبط:

  • اضبط مهلة تحميل الصفحة لفترة أطول، حوالي 10 إلى 20 ثانية.
  • استخدم مهلات أقصر مثل 3-5 ثوانٍ للعناصر الفردية.
  • ضع في اعتبارك أداء المتصفح، بين الأجهزة المحمولة وسطح المكتب.
  • عامل في زمن وصول الشبكة، النطاق العريض مقابل 3G.
  • مراقبة أخطاء المهلة وضبط أعلى إذا لزم الأمر.
  • تحليل شلال تحميل الصفحة لأوقات التحميل النموذجية.
  • قم بتخصيص 1-2 ثانية إضافية كمخزن مؤقت.
  • توحيد فترات الانتظار المماثلة عبر قاعدة التعليمات البرمجية الخاصة بك.

كلما قمت باستخلاص المزيد من الصفحات، ستحصل على حدس أفضل بشأن فترات الانتظار المثالية للحصول على الموثوقية.

التعامل مع فشل الانتظار والمهلة

حتى مع فترات الانتظار القوية، قد لا تزال تواجه مهلات عرضية. فيما يلي بعض الطرق للتعامل معها:

  • تسجيل تفاصيل التصحيح - تساعد إضافة المطبوعات في تشخيص أماكن فشل الانتظار.
  • أعد المحاولة عند انتهاء المهلة - إعادة المحاولة، والانتظار الصريح لمدة تصل إلى 3 مرات عند الفشل.
  • زيادة المهلة - في حالة حدوث العديد من المهلات، قم بزيادة فترات الانتظار بشكل تدريجي.
  • استخدم المحاولة/باستثناء - احصل على استثناءات محددة مثل StaleElementReference.
  • تعطيل عند الفشل – يمكنك تخطي الانتظار بعد الفشل المتكرر للسماح بمواصلة الاختبارات.

مع وجود بعض المرونة المضمنة، لن تؤدي هذه المشكلات المتفرقة إلى إتلاف مكشطةك.

في انتظار اللغات الأخرى

حتى الآن كانت الأمثلة في بايثون، ولكن الانتظار الصريح متاح عبر اللغات:

  • جافا - WebDriverWait و ExpectedConditions
  • C# - WebDriverWait و ExpectedConditions
  • روبي - WebDriver::Wait و ExpectedConditions
  • جافا سكريبت - browser.wait() وأساليب المنفعة

المفاهيم متشابهة جدًا - فقط بناء الجملة يختلف قليلاً.

ما بعد السيلينيوم: المزيد من أدوات الانتظار

هناك أيضًا بعض مكتبات الانتظار المفيدة الأخرى بخلاف السيلينيوم:

  • الوقت: - time.sleep() بسيط ولكنه يوقف التنفيذ مؤقتًا.
  • إعادة المحاولة - و أعد محاولة الحزمة يجعل إعادة المحاولة والانتظار أمرًا سهلاً.
  • ايوهتب - await response.text() ينتظر مكالمات الشبكة حتى يكتمل.
  • حساء جميل - BeautifulSoup(page.content, features="lxml") سوف ننتظر التحليل الكامل.
  • Scrapy - yield scrapy.Request(url, callback=self.parse) غير متزامن.

يؤدي مزجها مع السيلينيوم إلى توفير فترات انتظار قوية عبر التعليمات البرمجية الخاصة بك.

باختصار: انتظر جيدًا وقم بالتخلص بشكل موثوق

وفي الختام، إليك خمس نقاط أساسية:

  1. استخدم الانتظار الصريح - يتجنبون المهلات غير الضرورية ويستهدفون شروطًا محددة.

  2. انتظر إشارات متعددة – الجمع بين فترات انتظار الرأس والنص والتذييل وما إلى ذلك لتأكيد جاهزية الصفحة.

  3. ضبط المهلات بحكمة – قم بتعيين القيم بناءً على بيانات تحميل الصفحة الواقعية لتحسين التأخير.

  4. توحيد فترات الانتظار – إعادة استخدام الأنماط المتسقة عبر قاعدة التعليمات البرمجية الخاصة بك.

  5. أضف المرونة – تنفيذ عمليات إعادة المحاولة ومعالجة الفشل لحساب الصفحات الديناميكية.

قد يبدو الانتظار مملاً في البداية. لكن الاستثمار في منطق الانتظار القوي سوف يكافئك بأدوات كاشطات موثوقة ومرنة مُجهزة للويب الحديث.

نأمل أن تساعدك هذه الأنماط والنصائح المستخرجة من سنوات عملي كمتخصص محترف في تجريف الويب على الانتظار بنجاح. خردة على!

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

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

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