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

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

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

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

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

محرك العرائس

Puppeteer هو مكتبة عُقد طورها فريق 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. أنصحك باستخدام هذا الخيار إذا أردت الاقتراب من الميت باستخدام بروتوكول أدوات مطوّري البرامج مباشرةً.

إطلاق 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، وتم استخراجها الآن لاستخدامها بشكل مستقل. ستعثر 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.