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)'
للعثور على زر الإرسال، ولكنّ هذا مرتبط ارتباطًا وثيقًا بهذه النسخة بالضبط من صفحة الويب. إذا تمت إضافة عنصر لاحقًا فوق الزر، لن يعمل هذا المحدّد بعد ذلك.
هذه ليست أخبارًا جديدة لكاتبي الاختبارات: يحاول مستخدمو Puppeteer اختيار محدّدات قوية ضدّ هذه التغييرات. من خلال 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
في هذا النموذج الأولي، نفّذنا نقطة نهاية جديدة لخدمة CDP خصيصًا لطلب بيانات من شجرة تسهيل الاستخدام. بهذه الطريقة، يمكن أن يتمّ الاستعلام في الخلفية من خلال تنفيذ C++ بدلاً من سياق الصفحة من خلال JavaScript.
مقياس أداء اختبار الوحدة
يقارن الشكل التالي إجمالي وقت التشغيل لطلب البحث عن أربعة عناصر 1000 مرة في النماذج الأولية الثلاثة. تم تنفيذ الاختبار القياسي في 3 إعدادات مختلفة تختلف حسب حجم الصفحة وما إذا كان قد تم تفعيل ميزة التخزين المؤقت لعناصر تسهيل الاستخدام أم لا.
من الواضح تمامًا أنّ هناك فجوة كبيرة في الأداء بين آلية طلب البيانات المستندة إلى CDP والآليتَين الأخريتَين اللتين تم تنفيذهما في Puppeteer فقط، ويبدو أنّ الفرق النسبي يزداد بشكل كبير مع حجم الصفحة. من المثير للاهتمام إلى حدٍ ما أنّ النموذج الأولي لمسار JS DOM يستجيب بشكلٍ جيد لتفعيل ميزة التخزين المؤقت لإمكانية الاستخدام. عند إيقاف ميزة التخزين المؤقت، يتم احتساب شجرة تسهيل الاستخدام عند الطلب ويتم تجاهل الشجرة بعد كل تفاعل إذا كان النطاق غير مفعّل. يؤدي تفعيل النطاق إلى تخزين Chromium للبنية المحسوبة مؤقتًا بدلاً من ذلك.
بالنسبة إلى تنقّل JS DOM، نطلب الاسم والدور السهلَين الوصول إليهما لكل عنصر أثناء التنقّل، لذلك في حال إيقاف ميزة التخزين المؤقت، يحسب Chromium شجرة تسهيل الاستخدام ويتخلّص منها لكل عنصر نزوره. في المقابل، بالنسبة إلى الأساليب المستندة إلى CDP، لا يتم تجاهل الشجرة إلا بين كلّ طلب إلى CDP، أي لكلّ طلب بحث. تستفيد هذه الأساليب أيضًا من تفعيل ميزة التخزين المؤقت، لأنّه يتم بعد ذلك الاحتفاظ بشجرة تسهيل الاستخدام في جميع طلبات البيانات من CDP، ولكنّ تحسين الأداء يكون بالتالي أقل مقارنةً بغيرها.
على الرغم من أنّ تفعيل ميزة التخزين المؤقت يبدو أمرًا مرغوبًا فيه هنا، إلا أنّه يتطلّب تكلفة استخدام ذاكرة إضافية. بالنسبة إلى النصوص البرمجية في Puppeteer التي تُسجِّل ملفات تتبُّع مثلاً، قد يتسبب ذلك في حدوث مشاكل. لذلك، قرّرنا عدم تفعيل ميزة التخزين المؤقت لشجرة تسهيل الاستخدام تلقائيًا. يمكن للمستخدمين تفعيل ميزة التخزين المؤقت بأنفسهم من خلال تفعيل نطاق تسهيل الاستخدام في "إدارة البيانات في مراكز البيانات".
مقياس أداء مجموعة اختبارات أدوات المطوّرين
أظهر مقياس الأداء السابق أنّ تنفيذ آلية طلب البيانات في طبقة CDP يؤدي إلى تحسين الأداء في سيناريو اختبار الوحدة السريري.
لمعرفة ما إذا كان الفرق واضحًا بما يكفي ليُلاحظ في سيناريو أكثر واقعية لتشغيل مجموعة اختبارات كاملة، عدّلنا مجموعة اختبارات DevTools الشاملة للاستفادة من النماذج الأولية المستندة إلى 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 كمعالِج طلبات بحث مضمّن. نحن نتطلّع إلى معرفة كيفية استخدام المستخدمين لهذه الميزة في نصوص الاختبار، ونريد معرفة أفكارك حول كيفية تحسينها.
تنزيل قنوات المعاينة
ننصحك باستخدام إصدار Canary أو Dev أو الإصدار التجريبي من Chrome كمتصفّح التطوير التلقائي. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات DevTools، وتتيح لك اختبار واجهات برمجة تطبيقات منصات الويب المتطوّرة، وتساعدك في العثور على المشاكل في موقعك الإلكتروني قبل أن يعثر عليها المستخدمون.
التواصل مع فريق "أدوات مطوّري البرامج في Chrome"
استخدِم الخيارات التالية لمناقشة الميزات الجديدة أو التحديثات أو أي شيء آخر مرتبط بـ "أدوات مطوّري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" باستخدام رمز خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على Twitter على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات YouTube التي تعرض الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات YouTube التي تعرض نصائح حول "أدوات مطوّري البرامج".