Puppeteer وأسلوبه في استخدام أدوات الاختيار
Puppeteer هي مكتبة لتشغيل المتصفحات تلقائيًا في Node: تتيح لك التحكّم في المتصفّح باستخدام واجهة برمجة تطبيقات JavaScript بسيطة ومعاصرة.
إنّ المهمة الأكثر بروزًا للمتصفّح هي بالطبع تصفُّح صفحات الويب. تؤدي أتمتة هذه المهمة إلى أتمتة التفاعلات مع صفحة الويب.
في Puppeteer، يتم تحقيق ذلك من خلال طلب عناصر نموذج DOM باستخدام أدوات اختيار مستندة إلى السلاسل وتنفيذ إجراءات مثل النقر على العناصر أو كتابة نص عليها. على سبيل المثال، يمكن أن يظهر نص برمجي يفتح developer.google.com، ويعثر على مربّع البحث، وعمليات البحث عن puppetaria
يمكن أن يظهر على النحو التالي:
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://developers.google.com/', { waitUntil: 'load' });
// Find the search box using a suitable CSS selector.
const search = await page.$('devsite-search > form > div.devsite-search-container');
// Click to expand search box and focus it.
await search.click();
// Enter search string and press Enter.
await search.type('puppetaria');
await search.press('Enter');
})();
وبالتالي، فإنّ كيفية تحديد العناصر باستخدام أدوات اختيار طلبات البحث هي جزء أساسي من تجربة Puppeteer. حتى الآن، كانت أدوات الاختيار في Puppeteer تقتصر على أدوات اختيار CSS وXPath، والتي يمكن أن يكون لها عيوب في الحفاظ على تفاعلات المتصفّح في النصوص البرمجية، على الرغم من أنّها قوية جدًا من الناحية التعبيرية.
أدوات الاختيار النحوية مقابل أدوات الاختيار الدلالية
إنّ أدوات اختيار CSS نحوية بطبيعتها، وهي مرتبطة ارتباطًا وثيقًا بالعمل الداخلي للتمثيل النصي لشجرة DOM، وذلك من حيث أنّها تشير إلى أرقام التعريف وأسماء الفئات من DOM. وبالتالي، توفّر هذه الأدوات أداة متكاملة لمطوّري الويب لتعديل الأنماط أو إضافتها إلى عنصر في الصفحة، ولكن في هذا السياق، يتحكّم المطوّر بشكل كامل في الصفحة وشجرة DOM الخاصة بها.
من ناحية أخرى، ملفّ نصي Puppeteer هو مراقب خارجي للصفحة، لذا عند استخدام أدوات اختيار CSS في هذا السياق، يقدّم افتراضات مخفية حول كيفية تنفيذ الصفحة التي لا يتحكّم فيها ملفّ نصي Puppeteer.
ويؤدي ذلك إلى أنّ هذه النصوص البرمجية قد تكون غير متّسقة وعرضة لتغييرات رمز المصدر. لنفترض مثلاً أنّ أحد المستخدمين يستخدم نصوص Puppeteer البرمجية للاختبار الآلي لتطبيق ويب يحتوي على العقدة <button>Submit</button>
كعنصر ثانوي ثالث للعنصر body
. قد يبدو مقتطف واحد من نموذج الاختبار على النحو التالي:
const button = await page.$('body:nth-child(3)'); // problematic selector
await button.click();
ونستخدم هنا أداة الاختيار 'body:nth-child(3)'
للعثور على زر الإرسال، إلا أنّ هذا العنصر مقترن بشدة بهذا الإصدار من صفحة الويب. وإذا تمت إضافة عنصر لاحقًا فوق الزر، فلن تعمل أداة الاختيار هذه بعد ذلك.
هذا ليس خبرًا لاختبار المؤلفين: يحاول مستخدمو الدمى المتحركة اختيار أدوات اختيار فعّالة لمثل هذه التغييرات. من خلال Puppetaria، نمنح المستخدمين أداة جديدة في هذه الرحلة.
يتضمّن Puppeteer الآن معالج طلبات بحث بديلاً يستند إلى طلب البحث في شجرة تسهيل الاستخدام بدلاً من الاعتماد على أدوات اختيار لغة CSS. إنّ الفلسفة الأساسية هنا هي أنّه إذا لم يتغيّر العنصر المحدّد الذي نريد اختياره، من المفترض ألا يتغيّر عقدة تسهيل الاستخدام المقابلة أيضًا.
ونُطلق على أدوات الاختيار هذه "أدوات اختيار ARIA"، ونتيح الاستعلام عن اسم الوصول المحسوب ودور شجرة تسهيل الاستخدام. مقارنةً بأدوات اختيار لغة CSS، تكون هذه الخصائص ذات طبيعة دلالية. وهي ليست مرتبطة بالسمات النحوية لنموذج DOM، بل هي بدلاً من ذلك أوصاف لكيفية رصد الصفحة من خلال التكنولوجيات المساعِدة، مثل برامج قراءة الشاشة.
في مثال النص البرمجي للاختبار أعلاه، يمكننا بدلاً من ذلك استخدام أداة الاختيار aria/Submit[role="button"]
لاختيار الزر المطلوب، حيث يشير Submit
إلى الاسم الذي يمكن الوصول إليه للعنصر:
const button = await page.$('aria/Submit[role="button"]');
await button.click();
والآن، إذا قررنا لاحقًا تغيير المحتوى النصي للزر من Submit
إلى Done
، سيفشل الاختبار مرة أخرى، ولكن في هذه الحالة يكون ذلك مرغوبًا. عن طريق تغيير اسم الزر، نغيّر محتوى الصفحة بدلاً من عرضها المرئي أو كيفية تنظيمها في نموذج العناصر في المستند (DOM). من المفترض أن تحذّرنا اختباراتنا من حدوث هذه التغييرات للتأكّد من أنّها مقصودة.
بالعودة إلى المثال الأكبر حجمًا في شريط البحث، يمكننا الاستفادة من معالج aria
الجديد واستبدال
const search = await page.$('devsite-search > form > div.devsite-search-container');
مع
const search = await page.$('aria/Open search[role="button"]');
لتحديد مكان شريط البحث.
بشكل عام، نعتقد أنّ استخدام أدوات اختيار ARIA هذه يمكن أن يوفّر للمستخدِمين في Puppeteer المزايا التالية:
- جعل أدوات الاختيار في النصوص البرمجية الاختبارية أكثر مرونة في مواجهة تغييرات الرمز المصدر
- تسهيل قراءة النصوص البرمجية للاختبار (الأسماء التي يمكن الوصول إليها هي كلمات وصفية دلالية)
- حفِّز الممارسات الجيدة لتعيين خصائص تسهيل الاستخدام للعناصر.
تتناول بقية هذه المقالة تفاصيل حول كيفية تنفيذ مشروع Puppetaria.
عملية التصميم
الخلفية
كما هو موضح أعلاه، نريد تمكين عناصر الاستعلام حسب الاسم والدور المتاحين. هذه هي سمات شجرة تسهيل الاستخدام، وهي شجرة مشابهة لشجرة DOM المعتادة، وتستخدمها الأجهزة، مثل تطبيقات قراءة الشاشة، لعرض صفحات الويب.
من خلال الاطّلاع على مواصفات احتساب الاسم القابل للوصول، يتضح أنّ احتساب اسم العنصر مهمة ليست سهلة، لذلك قرّرنا منذ البداية إعادة استخدام البنية الأساسية الحالية في Chromium لتنفيذ ذلك.
كيفية تنفيذ هذه الميزة
على الرغم من أنّنا حصرنا باستخدام شجرة تسهيل الاستخدام في Chromium، هناك بعض الطرق التي يمكننا من خلالها تنفيذ طلب ARIA في أداة Puppeteer. لمعرفة السبب، لنلقِ نظرة أولاً على كيفية التحكّم في المتصفّح باستخدام Puppeteer.
يعرض المتصفّح واجهة تصحيح أخطاء من خلال بروتوكول يُعرف باسم بروتوكول أدوات مطوّري البرامج في Chrome (CDP). ويؤدي ذلك إلى عرض وظائف مثل "إعادة تحميل الصفحة" أو "تنفيذ هذا الجزء من JavaScript في الصفحة وعرض النتيجة" من خلال واجهة لا تعتمد على اللغة.
تستخدم كلّ من واجهة DevTools الأمامية وPuppeteer إطار عمل CDP للتواصل مع المتصفّح. لتنفيذ أوامر CDP، تتوفّر بنية أساسية لأدوات مطوّري البرامج في جميع مكوّنات Chrome: في المتصفّح وبرنامج التحويل وغير ذلك. تتولى منصّة CDP توجيه الأوامر إلى المكان الصحيح.
يتم تنفيذ إجراءات Puppeteer، مثل طلب البيانات والنقر وتقييم التعبيرات، من خلال الاستفادة من أوامر CDP، مثل Runtime.evaluate
التي تقيِّم JavaScript مباشرةً في سياق الصفحة وتُعيد النتيجة. إنّ إجراءات Puppeteer الأخرى، مثل محاكاة عجز الرؤية اللونية أو أخذ لقطات شاشة أو تسجيل عمليات التتبّع، تستخدم CDP للتواصل مباشرةً مع عملية عرض Blink.
يترك لنا ذلك طريقتَين لتنفيذ وظيفة طلب البحث:
- كتابة منطق الاستعلام الخاص بنا بلغة JavaScript وإدخال ذلك في الصفحة باستخدام
Runtime.evaluate
، أو - استخدِم نقطة نهاية CDP يمكنها الوصول إلى شجرة تسهيل الاستخدام وإجراء طلب بحث عنها مباشرةً في عملية Blink.
نفّذنا 3 نماذج أولية:
- التنقّل في نموذج عناصر JavaScript (DOM): يستند إلى حقن JavaScript في الصفحة.
- التنقّل في AXTree باستخدام Puppeteer: استنادًا إلى استخدام إذن الوصول الحالي إلى CDP في شجرة تسهيل الاستخدام
- التنقّل في نموذج DOM في CDP: باستخدام نقطة نهاية جديدة في CDP تم إنشاؤها خصيصًا لطلب البحث في شجرة تسهيل الاستخدام
اجتياز نموذج DOM في JavaScript
يُجري هذا النموذج الأوّلي عملية اجتياز كاملة لنموذج DOM ويستخدم element.computedName
وelement.computedRole
، وهما مرتبطان بعلامة التشغيل ComputedAccessibilityInfo
لاسترداد اسم كل عنصر ودوره أثناء الاجتياز.
اجتياز Puppeteer AXTree
في هذه الحالة، نسترِد بدلاً من ذلك شجرة تسهيل الاستخدام الكاملة من خلال CDP وننتقل خلالها في Puppeteer. بعد ذلك، يتمّ ربط عقد تسهيل الاستخدام الناتجة بعقد DOM.
اجتياز شجرة DOM في ميزة "الاستهداف بالاستناد إلى البيانات السلوكية"
في هذا النموذج الأولي، نفّذنا نقطة نهاية جديدة لخدمة CDP خصيصًا لطلب البحث في شجرة تسهيل الاستخدام. بهذه الطريقة، يمكن أن يتمّ الاستعلام في الخلفية من خلال تنفيذ C++ بدلاً من سياق الصفحة من خلال JavaScript.
مقياس أداء اختبار الوحدة
يقارن الشكل التالي إجمالي وقت التشغيل لطلب البحث عن أربعة عناصر 1000 مرة في النماذج الأولية الثلاثة. تم تنفيذ الاختبار القياسي في 3 إعدادات مختلفة تختلف حسب حجم الصفحة وما إذا كان قد تم تفعيل ميزة التخزين المؤقت لعناصر تسهيل الاستخدام أم لا.
من الواضح تمامًا أنّ هناك فجوة كبيرة في الأداء بين آلية طلب البحث المستندة إلى CDP والآلية الأخرى التي تم تنفيذها في Puppeteer، ويبدو أن الفرق النسبي يزداد بشكل كبير مع حجم الصفحة. من المثير للاهتمام إلى حدٍ ما أنّ النموذج الأولي لمسار JS DOM يستجيب بشكلٍ جيد لتفعيل ميزة التخزين المؤقت لإمكانية الاستخدام. عند إيقاف ميزة التخزين المؤقت، يتم احتساب شجرة تسهيل الاستخدام عند الطلب ويتم تجاهل الشجرة بعد كل تفاعل إذا كان النطاق غير مفعّل. يؤدي تفعيل النطاق إلى تخزين Chromium للبنية المحسوبة مؤقتًا بدلاً من ذلك.
بالنسبة إلى تنقّل JS DOM، نطلب الاسم والدور السهلَين الوصول إليهما لكل عنصر أثناء التنقّل، لذلك إذا تم إيقاف ميزة التخزين المؤقت، يحسب Chromium شجرة تسهيل الاستخدام ويتخلّص منها لكل عنصر نزوره. أمّا بالنسبة إلى المناهج المستندة إلى CDP، فيتم تجاهل العرض التدرّجي فقط بين كل طلب إلى CDP، أي لكل طلب بحث. تستفيد هذه الأساليب أيضًا من تفعيل ميزة التخزين المؤقت، لأنّه يتم بعد ذلك الاحتفاظ بشجرة تسهيل الاستخدام في جميع طلبات البيانات من CDP، ولكنّ تحسين الأداء يكون بالتالي أقل مقارنةً بغيرها.
وعلى الرغم من أنّ تفعيل التخزين المؤقت يبدو مرغوبًا هنا، فإنّه يفرض تكلفة إضافية على استخدام الذاكرة. بالنسبة إلى النصوص البرمجية في Puppeteer التي تُسجِّل ملفات تتبُّع مثلاً، قد يتسبب ذلك في حدوث مشاكل. لذلك، قرّرنا عدم تفعيل ميزة التخزين المؤقت لشجرة تسهيل الاستخدام تلقائيًا. يمكن للمستخدمين تفعيل ميزة التخزين المؤقت بأنفسهم من خلال تفعيل نطاق تسهيل الاستخدام في "إدارة البيانات في مراكز البيانات".
مقياس أداء مجموعة أدوات اختبار أدوات مطوّري البرامج
أظهر المعيار السابق أنّ تنفيذ آلية طلب البحث في طبقة CDP يمنحنا تحسينًا في الأداء في سيناريو اختبار الوحدة السريرية.
لمعرفة ما إذا كان الاختلاف واضحًا بما يكفي لملاحظته في سيناريو أكثر واقعية لتشغيل مجموعة اختبار كاملة، شرعنا في تصحيح مجموعة الاختبار المتكامل لـ "أدوات مطوّري البرامج" للاستفادة من النماذج الأولية المستندة إلى JavaScript وCDP ومقارنة أوقات التشغيل. في هذا المقياس، غيّرنا إجمالي 43 أداة اختيار من [aria-label=…]
إلى أداة معالجة طلبات بحث مخصّصة aria/…
، ثم نفّذناها باستخدام كل نموذج أولي.
يتم استخدام بعض المحدّدات عدة مرات في نصوص اختبارات، لذا كان العدد الفعلي لعمليات تنفيذ معالِج طلبات البحث aria
هو 113 عملية لكلّ مرة يتم فيها تشغيل المجموعة. كان إجمالي عدد اختيارات طلبات البحث 2253، لذا لم يتم اختيار سوى جزء من طلبات البحث من خلال النماذج الأولية.
كما هو موضّح في الشكل أعلاه، هناك فرق ملحوظ في إجمالي وقت التشغيل. البيانات مزعجة جدًا بحيث لا يمكن استنتاج أي شيء محدد، ولكن من الواضح أن فجوة الأداء بين النموذجين الأوليين تظهر في هذا السيناريو أيضًا.
نقطة نهاية جديدة في "إدارة العملاء بالاستناد إلى البيانات"
في ضوء مقاييس الأداء المذكورة أعلاه، ولأنّ النهج المستنِد إلى علامة الإطلاق لم يكن مرغوبًا فيه بشكل عام، قرّرنا المضي قدمًا في تنفيذ أمر CDP جديد للاستعلام عن شجرة تسهيل الاستخدام. الآن، كان علينا معرفة واجهة هذه النهاية الجديدة.
في حالة الاستخدام في Puppeteer، نحتاج إلى أن تأخذ نقطة النهاية ما يُعرف باسم RemoteObjectIds
كوسيطة، ولتمكيننا من العثور على عناصر DOM المقابلة بعد ذلك، يجب أن تعرض قائمة بالكائنات التي تحتوي على backendNodeIds
لعناصر DOM.
كما هو موضّح في الرسم البياني أدناه، جرّبنا العديد من الأساليب التي تتوافق مع هذه الواجهة. ومن خلال هذا التحليل، تبيّن لنا أنّ حجم العناصر المعروضة، أي ما إذا كانت العقد الكاملة لسهولة الاستخدام أو backendNodeIds
فقط، لا يُحدث فرقًا ملحوظًا. من ناحية أخرى، تبيّن لنا أنّ استخدام NextInPreOrderIncludingIgnored
الحالي كان خيارًا سيئًا لتنفيذ منطق التنقّل هنا، لأنّ ذلك أدّى إلى إبطاء ملحوظ.
الخلاصة
الآن، بعد أن تمّت إتاحة نقطة نهاية "إدارة العملاء بالاستناد إلى البيانات"، نفّذنا معالِج الاستعلامات على جانب Puppeteer. كان الهدف الأساسي من العمل هنا هو إعادة هيكلة رمز معالجة طلبات البحث لتتمكّن طلبات البحث من المعالجة مباشرةً من خلال CDP بدلاً من إجراء طلب البحث من خلال JavaScript الذي يتم تقييمه في سياق الصفحة.
ما هي الخطوات التالية؟
تم تضمين معالِج aria
الجديد مع الإصدار 5.4.0 من Puppeteer كمعالِج استعلامات مضمّن. نحن نتطلع إلى رؤية كيفية اعتماد المستخدمين له في نصوص الاختبار الخاصة بهم، ولا يسعنا الانتظار لسماع أفكارك حول كيفية جعل ذلك أكثر فائدة!
تنزيل قنوات المعاينة
يمكنك استخدام Chrome كناري، أو إصدار مطوّري البرامج، أو الإصدار التجريبي، كمتصفِّح التطوير التلقائي. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات DevTools، وتتيح لك اختبار واجهات برمجة تطبيقات منصات الويب المتطوّرة، وتساعدك في العثور على المشاكل في موقعك الإلكتروني قبل أن يعثر عليها المستخدمون.
التواصل مع فريق "أدوات مطوّري البرامج في Chrome"
يمكنك استخدام الخيارات التالية لمناقشة الميزات أو التحديثات الجديدة أو أي معلومات أخرى متعلّقة بـ "أدوات مطوري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" باستخدام رمز خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على Twitter على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات YouTube التي تعرض الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات YouTube التي تعرض نصائح حول "أدوات مطوّري البرامج".