אמ;לק
Headless Chrome מופיע בגרסה 59 של Chrome. זוהי דרך להריץ את דפדפן Chrome בסביבה ללא ממשק משתמש (headless). בעצם, הפעלת Chrome בלי Chrome! הוא מביא לשורת הפקודה את כל התכונות המודרניות של פלטפורמת האינטרנט ש-Chromium ומנוע הרינדור Blink מספקים.
למה זה מועיל?
דפדפן ללא ראש הוא כלי מצוין לבדיקות אוטומטיות ולסביבות שרת שבהן לא צריך מעטפת ממשק משתמש גלויה. לדוגמה, יכול להיות שתרצו להריץ כמה בדיקות בדף אינטרנט אמיתי, ליצור קובץ PDF שלו או פשוט לבדוק איך הדפדפן מעבד כתובת URL.
הפעלה ללא ממשק משתמש (CLI)
הדרך הקלה ביותר להתחיל להשתמש במצב ללא ממשק משתמש היא לפתוח את קובץ הבינארי של Chrome משורת הפקודה. אם מותקנת אצלכם גרסת Chrome 59 ואילך, עליכם להפעיל את 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) שיעזור לכם. כדאי לעיין במאמר שימוש ב-Headless 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
, מופעל מכונה עם פרוטוקול DevTools מופעל. הפרוטוקול משמש לתקשורת עם Chrome ולניהול של מכונה ללא ראש של דפדפן. זה גם מה שכלי כמו Sublime, VS Code ו-Node משתמשים בו לניפוי באגים מרחוק באפליקציה. #synergy
מכיוון שאין לכם ממשק משתמש בדפדפן כדי לראות את הדף, עליכם לעבור אל http://localhost:9222
בדפדפן אחר כדי לבדוק שהכול פועל. תוצג רשימה של דפים שאפשר לבדוק, שבהם תוכלו ללחוץ ולראות מה המערכת מבצעת רינדור ל-Headless:
מכאן תוכלו להשתמש בתכונות המוכרות של DevTools כדי לבדוק, לנפות באגים ולשנות את הדף כמו שאתם עושים בדרך כלל. אם אתם משתמשים ב-Headless באופן פרוגרמטי, הדף הזה הוא גם כלי יעיל לניפוי באגים, שמאפשר לראות את כל פקודות הפרוטוקול הגולמיות של DevTools שנשלחות דרך החיבור ומנהלות תקשורת עם הדפדפן.
שימוש באופן פרוגרמטי (Node)
בובנאי
Puppeteer היא ספריית Node שפותחה על ידי צוות Chrome. הוא מספק ממשק API ברמה גבוהה לצורך שליטה ב-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();
})();
מידע נוסף על ה-API המלא זמין במסמכי התיעוד של Puppeteer.
ספריית CRI
chrome-remote-interface היא ספרייה ברמה נמוכה יותר מ-API של Puppeteer. מומלץ להשתמש באפשרות הזו אם רוצים להיות קרובים לקוד ולהשתמש בפרוטוקול DevTools ישירות.
הפעלת 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 הוא כלי מצוין לבדיקת האיכות של אפליקציות האינטרנט שלכם. פיתחנו ב-Lighthouse מודול חזק להפעלת Chrome, ועכשיו הוא מופק לשימוש עצמאי.
מודול ה-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
. חשוב לזכור שלא יהיה ממשק משתמש בדפדפן. אנחנו ללא ממשק משתמש גרפי.
כדי לשלוט בדפדפן, אנחנו צריכים את פרוטוקול 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>
של הדף באמצעות ממשקי API של 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. במילים אחרות, זהו פתרון אוטומטי, אבל לא ללא ממשק משתמש (headless). עם זאת, אפשר להגדיר את Selenium כך שיפעיל Chrome ללא ממשק משתמש (headless) עם קצת עבודה. מומלץ לקרוא את המאמר הפעלת Selenium עם Chrome ללא ממשק משתמש כדי לקבל הוראות מלאות להגדרה בעצמכם, אבל הוספת כמה דוגמאות למטה כדי לעזור לכם להתחיל.
שימוש ב-ChromeDriver
ChromeDriver 2.32 משתמש ב-Chrome 61 ופועל היטב עם Headless 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 הוא ממשק API ברמה גבוהה יותר שמבוסס על 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();
})();
מקורות מידע נוספים
ריכזנו כאן כמה מקורות מידע שימושיים שיעזרו לכם להתחיל:
Docs
- DevTools Protocol Viewer – מסמכי עזרה בנושא ממשקי API
כלים
- chrome-remote-interface – מודול של Node שעוטף את פרוטוקול DevTools
- Lighthouse – כלי אוטומטי לבדיקת איכות אפליקציות אינטרנט, שמשתמש הרבה בפרוטוקול
- chrome-launcher – מודול Node להפעלת Chrome, מוכן לאוטומציה
הדגמות
- The Headless Web – פוסט מעולה בבלוג של Paul Kinlan על שימוש ב-Headless עם api.ai.
שאלות נפוצות
האם צריך את הדגל --disable-gpu
?
רק ב-Windows. בפלטפורמות אחרות כבר אין צורך בכך. הדגל --disable-gpu
הוא פתרון זמני לבעיות ספציפיות. לא תצטרכו את הדגל הזה בגרסאות עתידיות של Chrome. מידע נוסף זמין בכתובת crbug.com/737678.
אז עדיין צריך Xvfb?
לא. ב-Chrome ללא ממשק משתמש לא נעשה שימוש בחלון, ולכן אין יותר צורך בשרת תצוגה כמו Xvfb. אפשר להריץ את הבדיקות האוטומטיות בלי זה.
מהו Xvfb? Xvfb הוא שרת תצוגה בזיכרון למערכות שדומות ל-Unix, שמאפשר להריץ אפליקציות גרפיות (כמו Chrome) בלי מסך פיזי מחובר. אנשים רבים משתמשים ב-Xvfb כדי להריץ גרסאות קודמות של Chrome ולבצע בדיקות 'ללא ממשק משתמש'.
איך יוצרים קונטיינר ב-Docker שבו פועל Chrome ללא ממשק משתמש (Headless)?
כדאי לעיין ב-lighthouse-ci. יש בו דוגמה לקובץ Dockerfile שמשתמש ב-node:8-slim
כתמונת בסיס, מתקין ומריץ את Lighthouse ב-App Engine Flex.
האם אפשר להשתמש בזה עם Selenium / WebDriver / ChromeDriver?
כן. שימוש ב-Selenium, ב-WebDriver וב-ChromeDriver
מה הקשר ל-PhantomJS?
Headless Chrome דומה לכלים כמו PhantomJS. אפשר להשתמש בשניהם לבדיקות אוטומטיות בסביבה ללא ממשק משתמש. ההבדל העיקרי בין השניים הוא ש-Phantom משתמש בגרסה ישנה יותר של WebKit כמנוע הרינדור שלו, בעוד ש-Headless Chrome משתמש בגרסה העדכנית ביותר של Blink.
נכון לעכשיו, Phantom מספק גם ממשק API ברמה גבוהה יותר מפרוטוקול DevTools.
איפה מדווחים על באגים?
כדי לדווח על באגים ב-Headless Chrome, צריך לשלוח אותם לכתובת crbug.com.
כדי לדווח על באגים בפרוטוקול של DevTools, אפשר לשלוח אותם לכתובת github.com/ChromeDevTools/devtools-protocol.