תחילת העבודה עם דפדפן 'דפדפן ללא GUI'

אמ;לק

Chrome ללא GUI נשלח ב-Chrome 59. זוהי דרך להפעיל את דפדפן Chrome בסביבה ללא GUI. למעשה, ריצה Chrome בלי Chrome! הוא כולל את כל התכונות המודרניות של פלטפורמות האינטרנט על ידי Chromium ומנוע העיבוד של Blink אל שורת הפקודה.

למה זה שימושי?

דפדפן ללא GUI הוא כלי נהדר לבדיקות אוטומטיות ולסביבות שרת שבהן שאין צורך במעטפת גלויה של ממשק המשתמש. לדוגמה, ייתכן שתרצו להריץ כמה בדיקות אפשר ליצור דף אינטרנט אמיתי, ליצור ממנו קובץ PDF או פשוט לבדוק איך הדפדפן מעבד כתובת URL.

הפעלה של 'דפדפן ללא GUI' (CLI)

הדרך הקלה ביותר להתחיל במצב 'דפדפן ללא GUI' היא לפתוח את הקובץ הבינארי של 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 מכאן.

תכונות של שורת הפקודה

במקרים מסוימים, יכול להיות שלא תצטרכו כתיבת סקריפט פרוגרמטי באמצעות דפדפן 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 ללא GUI ככלי אוטומטי לצילום מסך .

מצב REPL (לולאת קריאה-eval-print)

הדגל --repl פועל ללא GUI במצב שבו אפשר להעריך ביטויי 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 ולנהיגה בטכנולוגיית GUI מופע של דפדפן. וגם כלים כמו Sublime, VS Code ו-Node משתמשים לניפוי באגים מרחוק של אפליקציה. #synergy

אין לך ממשק משתמש של הדפדפן כדי לראות את הדף, צריך לעבור אל http://localhost:9222 בדפדפן אחר כדי לבדוק שהכול עובד. תופיע רשימה של דפים שניתן לבדוק, שבהם אפשר ללחוץ כדי לראות מה מתבצע ברינדור ללא מגע:

שלט רחוק של כלי פיתוח
ממשק המשתמש לניפוי באגים מרחוק של כלי הפיתוח

מכאן אפשר להשתמש בתכונות המוכרות של כלי הפיתוח כדי לבדוק, לנפות באגים ולשנות בהתאם בדף כרגיל. אם אתם משתמשים באופן פרוגרמטי ללא ממשק גרפי, אפשרות הוא גם כלי רב-עוצמה לניפוי באגים שמאפשר לראות את כל הפרוטוקולים הגולמיים של כלי הפיתוח פקודות שעוברות ברשת ומתקשרות עם הדפדפן.

שימוש פרוגרמטי (ב-Node)

בובנאי

Puppeteer היא ספריית Node שפותח על ידי צוות Chrome. הוא מספק API ברמה גבוהה לניהול דפדפן ללא GUI (או מלא) Chrome. היא דומה לספריות בדיקה אוטומטיות אחרות כמו Phantom ו- NightmareJS, אבל הוא פועל רק עם הגרסאות האחרונות של Chrome.

בין היתר, אפשר להשתמש בבובה כדי לצלם צילומי מסך, ליצור קובצי PDF בקלות. לנווט בדפים ומאחזרים מידע על הדפים האלה. אני ממליץ על הספרייה אם רוצים לבצע במהירות בדיקה אוטומטית של הדפדפן. הוא מסתיר את המורכבות מפרוטוקול כלי הפיתוח ומטפל במשימות מיותרות, כמו הפעלת במופע של 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 לקבלת מידע נוסף על ממשק ה-API המלא.

ספריית ה-CRI

chrome-remote-interface היא ספרייה ברמה נמוכה יותר מה-API של Puppeteer. אני ממליץ על זה אם אתם רוצים שקרובים למתכת, ומשתמשים ישירות בפרוטוקול כלי הפיתוח.

הפעלה של Chrome

ממשק Chrome-רחוק לא מפעיל את 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 ומחולץ עכשיו לשימוש עצמאי. מודול ה-NPM chrome-launcher יהיה איפה Chrome מותקן, מגדירים מופע של ניפוי באגים, מפעילים את הדפדפן ומשביתים אותו בסיום התוכנית. היתרון הכי טוב הוא שהוא פועל בפלטפורמות שונות בזכות צומת!

כברירת מחדל, 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> של הדף באמצעות ממשקי DOM API.

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. במילים אחרות, הוא פתרון אוטומטי אבל לא נטול GUI. עם זאת, סלניום יכול להיות מוגדר להפעיל את Chrome ללא GUI עם מעט עבודה. אני ממליץ/ה הרצת סלניום עם דפדפן Chrome ללא ממשק גרפי אם רוצים ההוראות המלאות להגדרה עצמית, אבל לא ירדתי הדוגמאות הבאות יעזרו לכם להתחיל.

שימוש ב-ChromeDriver

ChromeDriver 2.32 משתמש ב-Chrome 61 ועובד היטב עם Chrome ללא GUI.

התקנה:

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

כלים

  • 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 לביצוע "דפדפן ללא GUI" בדיקה.

איך יוצרים קונטיינר Docker שמריץ את Headless Chrome?

ניתן לראות את Lighthouse-ci. יש בו קובץ Docker לדוגמה שמשתמשת ב-node:8-slim כתמונה בסיסית, מתקינה + מפעיל את Lighthouse ב-App Engine Flex.

אפשר להשתמש בו עם Selenium / WebDriver או ChromeDriver?

כן. ראו שימוש ב-Selenium, ב-WebDriver וב-ChromeDriver.

איך זה קשור ל-PhantomJS?

דפדפן Chrome ללא ממשק גרפי דומה לכלים כמו PhantomJS. שתי השיטות יכול לשמש לבדיקות אוטומטיות בסביבה ללא GUI. ההבדל העיקרי בין שתי השיטות האלה הוא משתמש ב-Phantom בגרסה ישנה יותר של WebKit למרות ש-Deepless Chrome משתמש בגרסה האחרונה של Blink.

בשלב זה, Phantom מספק גם ממשק API ברמה גבוהה יותר מפרוטוקול כלי הפיתוח.

איפה מדווחים על באגים?

אם נתקלתם בבאגים נגד דפדפן Chrome ללא ממשק גרפי, דווחו עליהם בכתובת crbug.com.

אם יש באגים בפרוטוקול DevTools, צריך לדווח אותם בכתובת github.com/ChromeDevTools/devtools-protocol.