אמ;לק
דפדפן Chrome ללא GUI זמין ב-Chrome 59. זוהי דרך להריץ את דפדפן 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
בספריית העבודה הנוכחית. אם אתם רוצים לצלם צילומי מסך של דפים מלאים, התהליך קצת יותר מורכב. יש לנו פוסט נהדר בבלוג של דיוויד שנור, שטיפלנו בו. כדאי לעיין במאמר שימוש ב-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 כדי לבדוק, לנפות באגים ולשנות את הדף כמו שאתם עושים בדרך כלל. אם משתמשים באופן פרוגרמטי ללא ממשק גרפי, הדף הזה הוא גם כלי רב-עוצמה לניפוי באגים שמאפשר לראות את כל הפקודות הגולמיות של פרוטוקול 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
כדי להפעיל את Headless
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>
של הדף באמצעות ממשקי 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 עם Headless 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 שמריץ את Headless Chrome?
ניתן לראות את 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 ברמה גבוהה יותר מפרוטוקול כלי הפיתוח.
איפה מדווחים על באגים?
אם נתקלתם בבאגים נגד דפדפן Chrome ללא ממשק גרפי, דווחו עליהם בכתובת crbug.com.
כדי לדווח על באגים בפרוטוקול של DevTools, אפשר לשלוח אותם לכתובת github.com/ChromeDevTools/devtools-protocol.