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

TL;DR

Chrome بلا واجهة مستخدم رسومية متاح في الإصدار 59 من Chrome. وهي طريقة لتشغيل متصفّح 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 هنا.

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

في بعض الحالات، قد لا تحتاج إلى إنشاء نصوص برمجية آليًا لتطبيق Headless 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 على تشغيل Headless في وضع يمكنك من خلاله تقييم تعبيرات JS في المتصفّح مباشرةً من سطر الأوامر:

$ 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 مع تفعيل بروتوكول أدوات المطوّرين. يُستخدَم بروتوكول للتواصل مع Chrome وتشغيل مثيل المتصفّح بلا واجهة مستخدم. وهي أيضًا الطريقة التي تستخدمها أدوات مثل Sublime وVS Code وNode ل debugging عن بُعد لتطبيق. #synergy

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

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

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

باستخدام أسلوب برمجي (Node)

محرك العرائس

Puppeteer هي مكتبة Node طوَّرها فريق Chrome. وتوفّر واجهة برمجة تطبيقات عالية المستوى للتحكّم في Chrome بدون واجهة مستخدم (أو كامل). وهي مشابهة لغيرها من مكتبات الاختبار المبرمَج، مثل Phantom وNightmareJS، ولكنها لا تعمل إلا مع أحدث إصدارات 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. ننصحك باستخدام هذا الإجراء إذا كنت تريد الاقتراب من المعدن واستخدام بروتوكول DevTools مباشرةً.

تشغيل Chrome

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

للتحكّم في المتصفّح، نحتاج إلى بروتوكول DevTools.

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

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

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 مع Headless Chrome إذا كنت تريد الحصول على تعليمات كاملة حول كيفية إعداد العناصر بنفسك، ولكننا أضفنا بعض المثال أدناه لمساعدتك في البدء.

استخدام ChromeDriver

يستخدم الإصدار 2.32 من ChromeDriver 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: وحدة node التي تغلف بروتوكول DevTools
  • Lighthouse: أداة مبرمَجة لاختبار جودة تطبيقات الويب، وتستخدم البروتوكول بشكل كبير
  • chrome-launcher: وحدة node لتشغيل 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 بدون واجهة مستخدم؟

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

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

نعم. راجِع مقالة استخدام Selenium وWebDriver وChromeDriver.

ما هي علاقة ذلك ببرنامج PhantomJS؟

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

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

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

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

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