بدء استخدام Chrome بلا واجهة مستخدم رسومية

الملخّص

يتم شحن Chrome بلا واجهة مستخدم رسومية في Chrome 59. وهي طريقة لتشغيل متصفِّح Chrome في بيئة بلا واجهة مستخدم رسومية. وبشكل أساسي، تشغيل Chrome بدون chrome! فهو يجمع جميع ميزات النظام الأساسي للويب الحديثة التي يوفرها Chromium ومحرك عرض Blink إلى سطر الأوامر.

لماذا يعتبر ذلك مفيدًا؟

يُعدّ المتصفّح بلا واجهة مستخدم رسومية أداة رائعة للاختبارات المبرمَجة وبيئات الخوادم التي لا تحتاج فيها إلى واجهة مستخدم مرئية. على سبيل المثال، قد تحتاج إلى إجراء بعض الاختبارات على صفحة ويب حقيقية، أو إنشاء ملف PDF منها، أو فقط فحص كيفية عرض المتصفح لعنوان URL.

بداية بلا واجهة مستخدم رسومية (CLI)

أسهل طريقة لبدء استخدام وضع التشغيل بلا واجهة مستخدم رسومية هي فتح برنامج Chrome الثنائي من سطر الأوامر. إذا كان لديك الإصدار 59 من Chrome أو الإصدارات الأحدث، يمكنك بدء Chrome باستخدام العلامة --headless:

chrome \
--headless \                   # Runs Chrome in headless mode.
--disable-gpu \                # Temporarily needed if running on Windows.
--remote-debugging-port=9222 \
https://www.chromestatus.com   # URL to open. Defaults to about:blank.

يجب أن يشير chrome إلى تثبيت Chrome. سيختلف الموقع الدقيق من منصة إلى أخرى. نظرًا لأنني أستخدم نظام التشغيل Mac، فقد قمتُ بإنشاء أسماء مستعارة مناسبة لكل إصدار من Chrome قمت بتثبيته.

إذا كنت تستخدم القناة الثابتة من Chrome ولا يمكنك الحصول على الإصدار التجريبي، أنصحك باستخدام chrome-canary:

alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
alias chrome-canary="/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary"
alias chromium="/Applications/Chromium.app/Contents/MacOS/Chromium"

نزِّل Chrome Canary من هنا.

ميزات سطر الأوامر

في بعض الحالات، قد لا تحتاج إلى كتابة نص برمجي آليًا لمتصفِّح Chrome بلا واجهة مستخدم رسومية. هناك بعض علامات سطر الأوامر المفيدة لتنفيذ المهام الشائعة.

طباعة نموذج العناصر في المستند (DOM)

يطبع علم --dump-dom بـ document.body.innerHTML إلى تنسيق stdout:

    chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/

إنشاء ملف PDF

تنشئ العلامة --print-to-pdf ملف PDF للصفحة:

chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/

أخذ لقطات شاشة

لأخذ لقطة شاشة لصفحة، استخدِم العلامة --screenshot:

chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/

# Size of a standard letterhead.
chrome --headless --disable-gpu --screenshot --window-size=1280,1696 https://www.chromestatus.com/

# Nexus 5x
chrome --headless --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/

سيؤدي تنفيذ --screenshot إلى إنشاء ملف باسم screenshot.png في دليل العمل الحالي. إذا كنت تبحث عن لقطات شاشة كاملة للصفحة، فإن الأمور أكثر تعقيدًا. هناك مشاركة مدونة رائعة من ديفيد شنير قد تناولتها. يُرجى الاطّلاع على استخدام Chrome بلا واجهة مستخدم رسومية كأداة مبرمَجة لأخذ لقطات الشاشة .

وضع REPL (تكرار القراءة والتقييم)

تعمل العلامة --repl بلا واجهة مستخدم رسومية في وضع يمكنك من خلاله تقييم تعبيرات JavaScript في المتصفّح مباشرةً من سطر الأوامر:

$ chrome --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://www.chromestatus.com/
[0608/112805.245285:INFO:headless_shell.cc(278)] Type a Javascript expression to evaluate or "quit" to exit.
>>> location.href
{"result":{"type":"string","value":"https://www.chromestatus.com/features"}}
>>> quit
$

هل تريد تصحيح الأخطاء في Chrome بدون واجهة مستخدم للمتصفّح؟

عند تشغيل Chrome باستخدام --remote-debugging-port=9222، يتم تشغيل مثيل مع تفعيل بروتوكول أدوات مطوّري البرامج. يُستخدم البروتوكول للتواصل مع Chrome وتشغيل مثيل المتصفح بلا واجهة مستخدم رسومية. إنه أيضًا ما تستخدمه أدوات مثل Sublime وVS Code وNode لتصحيح أخطاء أحد التطبيقات عن بُعد. #synergy

بما أنّه لا تتوفّر لديك واجهة مستخدم للمتصفّح تتيح لك الاطّلاع على الصفحة، انتقِل إلى http://localhost:9222 في متصفّح آخر للتأكّد من أنّ كل شيء يعمل. ستظهر لك قائمة بالصفحات القابلة للفحص حيث يمكنك النقر عليها ومعرفة ما تعرضه بلا واجهة مستخدم رسومية:

جهاز تحكُّم عن بُعد في أدوات مطوّري البرامج
واجهة المستخدم لتصحيح الأخطاء عن بُعد في "أدوات مطوري البرامج"

من هنا، يمكنك استخدام ميزات "أدوات مطوري البرامج" المألوفة لفحص الصفحة وتصحيح أخطائها وتعديلها كما تفعل عادةً. إذا كنت تستخدم أداة "بدون واجهة مستخدم رسومية" آليًا، ستكون هذه الصفحة أيضًا أداة فعّالة لتصحيح الأخطاء تتيح لك الاطّلاع على جميع أوامر بروتوكول "أدوات مطوري البرامج" الأولية التي تم إرسالها عبر الكابل والتواصل مع المتصفّح.

استخدام (عقدة) آليًا

محرك العرائس

Puppeteer هو مكتبة عُقد طورها فريق Chrome. توفر واجهة برمجة تطبيقات عالية المستوى للتحكم في Chrome بلا واجهة مستخدم رسومية (أو كاملة). وهو مشابه لمكتبات الاختبار الآلية الأخرى، مثل Phantom و NightmareJS، لكنّه يعمل فقط مع أحدث إصدارات Chrome.

ويمكن استخدام Puppeteer لالتقاط لقطات شاشة بسهولة وإنشاء ملفات PDF والتنقل بين الصفحات وجلب معلومات حول تلك الصفحات إلى جانب أشياء أخرى. أنصحك باستخدام المكتبة إذا كنت تريد إجراء اختبار تلقائي للمتصفّح بسرعة. إنه يُخفي تعقيدات بروتوكول "أدوات مطوري البرامج" ويعتني بالمهام المتكررة مثل تشغيل مثيل تصحيح الأخطاء في Chrome.

تثبيته:

npm i --save puppeteer

مثال - طباعة وكيل المستخدم

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  console.log(await browser.version());
  await browser.close();
})();

مثال: أخذ لقطة شاشة للصفحة

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.chromestatus.com', {waitUntil: 'networkidle2'});
  await page.pdf({path: 'page.pdf', format: 'A4'});

  await browser.close();
})();

راجع مستندات Puppeteer لمعرفة المزيد حول واجهة برمجة التطبيقات الكاملة.

مكتبة CRI

chrome-remote-interface هي مكتبة ذات مستوى أقل من واجهة برمجة تطبيقات Puppeteer. أنصحك باستخدامه إذا أردت الاقتراب من الميت باستخدام بروتوكول أدوات مطوّري البرامج مباشرةً.

إطلاق Chrome

لا تعمل واجهة chrome-عن بُعد على تشغيل Chrome من أجلك، لذا سينبغي لك الاهتمام بذلك بنفسك.

في قسم واجهة سطر الأوامر، بدأنا تشغيل Chrome يدويًا باستخدام --headless --remote-debugging-port=9222. ومع ذلك، لإجراء الاختبارات بشكل آلي بالكامل، ربما تريد نشر Chrome من تطبيقك.

إحدى الطرق هي استخدام child_process:

const execFile = require('child_process').execFile;

function launchHeadlessChrome(url, callback) {
  // Assuming MacOSx.
  const CHROME = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome';
  execFile(CHROME, ['--headless', '--disable-gpu', '--remote-debugging-port=9222', url], callback);
}

launchHeadlessChrome('https://www.chromestatus.com', (err, stdout, stderr) => {
  ...
});

لكن الأمور تصبح صعبة إذا كنت تريد حلاً محمولاً يعمل عبر أنظمة أساسية متعددة. ما عليك سوى النظر إلى هذا المسار غير القابل للتغيير في Chrome :(

استخدام ChromeLauncher

تُعدّ أداة Lighthouse أداة رائعة لاختبار جودة تطبيقات الويب. تم تطوير وحدة فعّالة لإطلاق Chrome ضمن أداة Lighthouse ويتم استخراجها الآن للاستخدام المستقل. ستعثر وحدة NPM chrome-launcher على مكان تثبيت Chrome وتضبط مثيل تصحيح الأخطاء وتشغِّل المتصفّح وتغلقه عندما ينتهي البرنامج. وأفضل ما في الأمر هو أنّها تعمل على عدّة أنظمة أساسية بفضل Node!

بشكل تلقائي، سيحاول chrome-launcher تشغيل Chrome Canary (إذا كان مثبّتًا)، ولكن يمكنك تغيير ذلك لتحديد Chrome الذي تريد استخدامه يدويًا. لاستخدامها، قم أولاً بالتثبيت من npm:

npm i --save chrome-launcher

مثال: استخدام chrome-launcher لتشغيل النسخة بلا واجهة مستخدم رسومية

const chromeLauncher = require('chrome-launcher');

// Optional: set logging level of launcher to see its output.
// Install it using: npm i --save lighthouse-logger
// const log = require('lighthouse-logger');
// log.setLevel('info');

/**
 * Launches a debugging instance of Chrome.
 * @param {boolean=} headless True (default) launches Chrome in headless mode.
 *     False launches a full version of Chrome.
 * @return {Promise<ChromeLauncher>}
 */
function launchChrome(headless=true) {
  return chromeLauncher.launch({
    // port: 9222, // Uncomment to force a specific port of your choice.
    chromeFlags: [
      '--window-size=412,732',
      '--disable-gpu',
      headless ? '--headless' : ''
    ]
  });
}

launchChrome().then(chrome => {
  console.log(`Chrome debuggable on port: ${chrome.port}`);
  ...
  // chrome.kill();
});

لا يؤدي تشغيل هذا النص البرمجي إلى حدوث الكثير، ولكن من المفترَض أن يظهر لك مثيل من Chrome يتم تشغيله في قسم "إدارة المهام" الذي حمَّل about:blank. وتذكر أنه لن تكون هناك أي واجهة مستخدم للمتصفح. نحن بلا واجهة مستخدم رسومية.

يجب استخدام بروتوكول "أدوات مطوري البرامج" للتحكّم في المتصفّح.

جارٍ استرداد معلومات حول الصفحة

لنبدأ في تثبيت المكتبة:

npm i --save chrome-remote-interface
أمثلة

مثال - طباعة وكيل المستخدم

const CDP = require('chrome-remote-interface');

...

launchChrome().then(async chrome => {
  const version = await CDP.Version({port: chrome.port});
  console.log(version['User-Agent']);
});

النتائج المتعلقة بموضوع مثل: HeadlessChrome/60.0.3082.0

مثال: التحقّق مما إذا كان الموقع الإلكتروني يتضمّن بيان تطبيق الويب

const CDP = require('chrome-remote-interface');

...

(async function() {

const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});

// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page} = protocol;
await Page.enable();

Page.navigate({url: 'https://www.chromestatus.com/'});

// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
  const manifest = await Page.getAppManifest();

  if (manifest.url) {
    console.log('Manifest: ' + manifest.url);
    console.log(manifest.data);
  } else {
    console.log('Site has no app manifest');
  }

  protocol.close();
  chrome.kill(); // Kill Chrome.
});

})();

مثال: يمكنك استخراج <title> للصفحة باستخدام واجهات برمجة تطبيقات DOM.

const CDP = require('chrome-remote-interface');

...

(async function() {

const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});

// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page, Runtime} = protocol;
await Promise.all([Page.enable(), Runtime.enable()]);

Page.navigate({url: 'https://www.chromestatus.com/'});

// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
  const js = "document.querySelector('title').textContent";
  // Evaluate the JS expression in the page.
  const result = await Runtime.evaluate({expression: js});

  console.log('Title of page: ' + result.result.value);

  protocol.close();
  chrome.kill(); // Kill Chrome.
});

})();

استخدام Selenium وWebDriver وChromeDriver

في الوقت الحالي، يفتح سيلينيوم مثيلاً كاملاً من Chrome. بعبارة أخرى، إنه حل تلقائي ولكنه ليس بلا واجهة مستخدم رسومية تمامًا. ومع ذلك، يمكن تهيئة السيلينيوم لتشغيل Chrome بلا واجهة مستخدم رسومية بقليل من العمل. أنصحك باستخدام استخدام Selenium باستخدام Chrome بلا واجهة مستخدم رسومية إذا كنت تريد الحصول على التعليمات الكاملة حول كيفية إعداد الجهاز بنفسك، لكنني ذكرتُ بعض الأمثلة أدناه لمساعدتك في البدء.

استخدام ChromeDriver

يستخدم ChromeDriver 2.32 الإصدار 61 من Chrome ويعمل بشكل جيد مع Chrome بلا واجهة مستخدم رسومية.

ثبِّت المكتبة:

npm i --save-dev selenium-webdriver chromedriver

مثال:

const fs = require('fs');
const webdriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');

const chromeCapabilities = webdriver.Capabilities.chrome();
chromeCapabilities.set('chromeOptions', {args: ['--headless']});

const driver = new webdriver.Builder()
  .forBrowser('chrome')
  .withCapabilities(chromeCapabilities)
  .build();

// Navigate to google.com, enter a search.
driver.get('https://www.google.com/');
driver.findElement({name: 'q'}).sendKeys('webdriver');
driver.findElement({name: 'btnG'}).click();
driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);

// Take screenshot of results page. Save to disk.
driver.takeScreenshot().then(base64png => {
  fs.writeFileSync('screenshot.png', new Buffer(base64png, 'base64'));
});

driver.quit();

استخدام WebDriverIO

WebDriverIO هي واجهة برمجة تطبيقات ذات مستوى أعلى بالإضافة إلى Selenium WebDriver.

ثبِّت المكتبة:

npm i --save-dev webdriverio chromedriver

مثال: فلترة ميزات CSS على chromestatus.com

const webdriverio = require('webdriverio');
const chromedriver = require('chromedriver');

const PORT = 9515;

chromedriver.start([
  '--url-base=wd/hub',
  `--port=${PORT}`,
  '--verbose'
]);

(async () => {

const opts = {
  port: PORT,
  desiredCapabilities: {
    browserName: 'chrome',
    chromeOptions: {args: ['--headless']}
  }
};

const browser = webdriverio.remote(opts).init();

await browser.url('https://www.chromestatus.com/features');

const title = await browser.getTitle();
console.log(`Title: ${title}`);

await browser.waitForText('.num-features', 3000);
let numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} total features`);

await browser.setValue('input[type="search"]', 'CSS');
console.log('Filtering features...');
await browser.pause(1000);

numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} CSS features`);

const buffer = await browser.saveScreenshot('screenshot.png');
console.log('Saved screenshot...');

chromedriver.stop();
browser.end();

})();

موارد أخرى

وفي ما يلي بعض الموارد المفيدة لمساعدتك على البدء:

مستندات Google

الأدوات

  • chrome-remote-interface - وحدة العُقد التي تشمل بروتوكول أدوات مطوّري البرامج
  • Lighthouse - أداة تلقائية لاختبار جودة تطبيق الويب؛ تستخدم بشكل مكثّف للبروتوكول
  • chrome-مشغّل التطبيقات - وحدة عقدة لتشغيل Chrome، جاهزة للتشغيل الآلي

إصدارات تجريبية

الأسئلة الشائعة

هل أحتاج إلى علامة --disable-gpu؟

على نظام التشغيل Windows فقط. لم تعُد الأنظمة الأساسية الأخرى تتطلب هذا الإجراء. يتم حلّ بعض الأخطاء مؤقتًا في العلامة --disable-gpu. لن تحتاج إلى هذه العلامة في إصدارات Chrome المستقبلية. للاطّلاع على مزيد من المعلومات، يمكنك الانتقال إلى crbug.com/737678.

هل ما زلت بحاجة إلى Xvfb؟

لا، لأنّ Chrome بلا واجهة مستخدم رسومية لا يستخدم نافذة، وبالتالي لم تعُد هناك حاجة إلى خادم عرض، مثل Xvfb. يمكنك إجراء اختباراتك المبرمَجة بدونها بنجاح.

ما هو Xvfb؟ Xvfb هو خادم عرض في الذاكرة للأنظمة الشبيهة بـ Unix ويتيح لك تشغيل تطبيقات رسومية (مثل Chrome) بدون شاشة مادية مرفقة. ويستخدم العديد من الأشخاص Xvfb لتشغيل إصدارات سابقة من Chrome لإجراء اختبار "بلا واجهة مستخدم رسومية".

كيف يمكنني إنشاء حاوية Docker تشغّل Chrome بلا واجهة مستخدم رسومية؟

اطّلع على منارة-ci. ويحتوي على مثال Dockerfile يستخدم node:8-slim كصورة أساسية، ويثبّت + يشغّل Lighthouse على App Engine Flex.

هل يمكنني استخدام هذا مع Selenium / WebDriver / ChromeDriver؟

رائع راجِع استخدام Selenium وWebDriver وChromeDriver.

ما علاقة ذلك بتطبيق PhantomJS؟

إنّ متصفِّح Chrome بلا واجهة مستخدم رسومية يشبه أدوات مثل PhantomJS. ويمكن استخدام كليهما للاختبار الآلي في بيئة بلا واجهة مستخدم رسومية. يتمثل الاختلاف الرئيسي بين الاثنين في أن Phantom تستخدم إصدارًا قديمًا من WebKit كمحرك للعرض، في حين يستخدم Chrome بلا واجهة مستخدم رسومية أحدث إصدار من Blink.

في الوقت الحالي، يوفّر Phantom أيضًا واجهة برمجة تطبيقات بمستوى أعلى من بروتوكول أدوات مطوّري البرامج.

أين يمكنني الإبلاغ عن الأخطاء؟

للإبلاغ عن الأخطاء في Chrome بلا واجهة مستخدم رسومية، يُرجى الإبلاغ عنها على crbug.com.

بالنسبة إلى الأخطاء في بروتوكول أدوات مطوّري البرامج، يُرجى تقديمها على github.com/ChromeDevTools/devtools-protocol.