بدء استخدام 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 (حلقة read-eval-print)

تعمل علامة --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 في متصفّح آخر للتأكّد من أنّك تعمل بشكل صحيح. ستظهر لك قائمة بالصفحات غير القابلة للفحص حيث يمكنك النقر عليها ومعرفة ما يتم عرضه بلا واجهة مستخدم رسومية:

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

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

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

محرك العرائس

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

بالإضافة إلى الميزات الأخرى، يمكن استخدام Puppeteer لالتقاط لقطات شاشة بسهولة وإنشاء ملفات PDF والتنقّل بين الصفحات وجلب معلومات حول تلك الصفحات. أوصي بالمكتبة إذا كنت تريد تشغيل اختبار المتصفح تلقائيًا بسرعة. كما أنه يخفي تعقيدات بروتوكول DevTools ويهتم بالمهام المتكررة مثل تشغيل مثيل تصحيح الأخطاء في 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-remote-interface Chrome بشكلٍ تلقائي، لذا عليك الاهتمام بهذا بنفسك.

في قسم CLI، بدأنا متصفِّح 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، ويتم الآن استخراجها للاستخدام المستقل. ستعثر وحدة chrome-launcher NPM على المكان الذي تم تثبيت 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

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

استخدام ChromeDriver

يستخدم ChromeDriver 2.32 Chrome 61 ويعمل بشكل جيد مع 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- Launcher - وحدة عقدة لتشغيل Chrome، وجاهزة للتشغيل الآلي

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

  • "The Headless Web" - مشاركة مدونة رائعة لبول كينلان حول استخدام Headless مع api.ai.

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

هل أحتاج إلى العلامة --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 أيضًا واجهة برمجة تطبيقات ذات مستوى أعلى من بروتوكول DevTools.

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

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

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