Mulai Menggunakan Chrome Headless

TL;DR

Chrome Headless disertakan dalam Chrome 59. Ini adalah cara untuk menjalankan browser Chrome di lingkungan headless. Intinya, menjalankan Chrome tanpa chrome. Chromium OS Flex menghadirkan semua fitur platform web modern yang disediakan oleh Chromium dan mesin rendering Blink ke command line.

Mengapa hal itu berguna?

Browser headless adalah alat yang bagus untuk pengujian otomatis dan lingkungan server tempat Anda tidak memerlukan shell UI yang terlihat. Misalnya, Anda mungkin ingin menjalankan beberapa pengujian terhadap halaman web yang sebenarnya, membuat PDF-nya, atau hanya memeriksa cara browser merender URL.

Memulai Headless (CLI)

Cara termudah untuk memulai mode headless adalah dengan membuka biner Chrome dari command line. Jika Anda telah menginstal Chrome 59+, mulai Chrome dengan flag --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 akan mengarah ke penginstalan Chrome Anda. Lokasi persisnya akan bervariasi dari platform ke platform. Karena saya menggunakan Mac, saya membuat alias yang mudah untuk setiap versi Chrome yang telah saya instal.

Jika Anda menggunakan saluran stabil Chrome dan tidak dapat mendapatkan versi Beta, sebaiknya gunakan 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"

Download Chrome Canary di sini.

Fitur command line

Dalam beberapa kasus, Anda mungkin tidak perlu membuat skrip terprogram Chrome Headless. Ada beberapa flag command line yang berguna untuk melakukan tugas umum.

Mencetak DOM

Flag --dump-dom mencetak document.body.innerHTML ke stdout:

    chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/

Membuat PDF

Flag --print-to-pdf membuat PDF halaman:

chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/

Mengambil screenshot

Untuk mengambil screenshot halaman, gunakan tanda --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/

Menjalankan dengan --screenshot akan menghasilkan file bernama screenshot.png di direktori kerja saat ini. Jika Anda mencari screenshot halaman penuh, hal-hal akan sedikit lebih rumit. Ada posting blog yang bagus dari David Schnurr yang membahasnya untuk Anda. Lihat artikel Menggunakan Chrome headless sebagai alat screenshot otomatis .

Mode REPL (read-eval-print loop)

Flag --repl menjalankan Headless dalam mode yang memungkinkan Anda mengevaluasi ekspresi JS di browser, langsung dari command line:

$ 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
$

Men-debug Chrome tanpa UI browser?

Saat Anda menjalankan Chrome dengan --remote-debugging-port=9222, Chrome akan memulai instance dengan protokol DevTools yang diaktifkan. Protokol ini digunakan untuk berkomunikasi dengan Chrome dan menjalankan instance browser headless. Ini juga yang digunakan alat seperti Sublime, VS Code, dan Node untuk men-debug aplikasi dari jarak jauh. #synergy

Karena Anda tidak memiliki UI browser untuk melihat halaman, buka http://localhost:9222 di browser lain untuk memeriksa apakah semuanya berfungsi. Anda akan melihat daftar halaman yang dapat diperiksa tempat Anda dapat mengklik dan melihat apa yang dirender Headless:

Remote DevTools
UI proses debug jarak jauh DevTools

Dari sini, Anda dapat menggunakan fitur DevTools yang sudah dikenal untuk memeriksa, men-debug, dan menyesuaikan halaman seperti biasa. Jika Anda menggunakan Headless secara terprogram, halaman ini juga merupakan alat proses debug yang efektif untuk melihat semua perintah protokol DevTools mentah yang dikirim melalui jaringan, yang berkomunikasi dengan browser.

Menggunakan secara terprogram (Node)

Puppeteer

Puppeteer adalah library Node yang dikembangkan oleh tim Chrome. Library ini menyediakan API tingkat tinggi untuk mengontrol Chrome headless (atau lengkap). Library ini mirip dengan library pengujian otomatis lainnya seperti Phantom dan NightmareJS, tetapi hanya berfungsi dengan Chrome versi terbaru.

Di antara hal lainnya, Puppeteer dapat digunakan untuk mengambil screenshot dengan mudah, membuat PDF, membuka halaman, dan mengambil informasi tentang halaman tersebut. Saya merekomendasikan library ini jika Anda ingin mengotomatiskan pengujian browser dengan cepat. API ini menyembunyikan kompleksitas protokol DevTools dan menangani tugas yang berlebihan seperti meluncurkan instance debug Chrome.

Instal:

npm i --save puppeteer

Contoh - mencetak agen pengguna

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  console.log(await browser.version());
  await browser.close();
})();

Contoh - mengambil screenshot halaman

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();
})();

Lihat dokumentasi Puppeteer untuk mempelajari API lengkap lebih lanjut.

Library CRI

chrome-remote-interface adalah library level rendah dibandingkan API Puppeteer. Sebaiknya gunakan jika Anda ingin lebih dekat dengan hardware dan menggunakan protokol DevTools secara langsung.

Meluncurkan Chrome

chrome-remote-interface tidak meluncurkan Chrome untuk Anda, jadi Anda harus menanganinya sendiri.

Di bagian CLI, kita memulai Chrome secara manual menggunakan --headless --remote-debugging-port=9222. Namun, untuk mengotomatiskan pengujian sepenuhnya, Anda mungkin ingin membuat Chrome dari aplikasi Anda.

Salah satu caranya adalah dengan menggunakan 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) => {
  ...
});

Namun, hal ini akan menjadi rumit jika Anda menginginkan solusi portabel yang berfungsi di beberapa platform. Lihat saja jalur hard code ke Chrome :(

Menggunakan ChromeLauncher

Lighthouse adalah alat yang luar biasa untuk menguji kualitas aplikasi web Anda. Modul andal untuk meluncurkan Chrome dikembangkan dalam Lighthouse dan kini diekstrak untuk penggunaan mandiri. Modul NPM chrome-launcher akan menemukan tempat Chrome diinstal, menyiapkan instance debug, meluncurkan browser, dan menghentikannya setelah program Anda selesai. Bagian terbaiknya adalah ini berfungsi lintas platform berkat Node.

Secara default, chrome-launcher akan mencoba meluncurkan Chrome Canary (jika diinstal), tetapi Anda dapat mengubahnya untuk memilih Chrome yang akan digunakan secara manual. Untuk menggunakannya, pertama-tama instal dari npm:

npm i --save chrome-launcher

Contoh - menggunakan chrome-launcher untuk meluncurkan 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();
});

Tidak banyak yang bisa dilakukan dengan menjalankan skrip ini, tetapi Anda akan melihat instance Chrome aktif di pengelola tugas yang memuat about:blank. Ingat, tidak akan ada UI browser apa pun. Kami headless.

Untuk mengontrol browser, kita memerlukan protokol DevTools.

Mengambil informasi tentang halaman

Mari kita instal library:

npm i --save chrome-remote-interface
Contoh

Contoh - cetak agen pengguna

const CDP = require('chrome-remote-interface');

...

launchChrome().then(async chrome => {
  const version = await CDP.Version({port: chrome.port});
  console.log(version['User-Agent']);
});

Hasilnya akan terlihat seperti ini: HeadlessChrome/60.0.3082.0

Contoh - memeriksa apakah situs memiliki manifes aplikasi web

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.
});

})();

Contoh - ekstrak <title> halaman menggunakan 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.
});

})();

Menggunakan Selenium, WebDriver, dan ChromeDriver

Saat ini, Selenium membuka instance Chrome lengkap. Dengan kata lain, ini adalah solusi otomatis, tetapi tidak sepenuhnya headless. Namun, Selenium dapat dikonfigurasi untuk menjalankan Chrome headless dengan sedikit upaya. Sebaiknya Jalankan Selenium dengan Headless Chrome jika Anda menginginkan petunjuk lengkap tentang cara menyiapkannya sendiri, tetapi saya telah menyertakan beberapa contoh di bawah untuk membantu Anda memulai.

Menggunakan ChromeDriver

ChromeDriver 2.32 menggunakan Chrome 61 dan berfungsi dengan baik dengan Chrome headless.

Instal:

npm i --save-dev selenium-webdriver chromedriver

Contoh:

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();

Menggunakan WebDriverIO

WebDriverIO adalah API level yang lebih tinggi selain Selenium WebDriver.

Instal:

npm i --save-dev webdriverio chromedriver

Contoh: memfilter fitur CSS di 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();

})();

Aset lainnya

Berikut beberapa referensi berguna untuk membantu Anda memulai:

Dokumen

Alat

  • chrome-remote-interface - modul node yang menggabungkan protokol DevTools
  • Lighthouse - alat otomatis untuk menguji kualitas aplikasi web; memanfaatkan banyak protokol
  • chrome-launcher - modul node untuk meluncurkan Chrome, siap untuk otomatisasi

Demo

  • "The Headless Web" - Postingan blog menarik dari Paul Kinlan tentang penggunaan Headless dengan api.ai.

FAQ

Apakah saya memerlukan flag --disable-gpu?

Hanya di Windows. Platform lain tidak lagi memerlukannya. Flag --disable-gpu adalah solusi sementara untuk beberapa bug. Anda tidak memerlukan flag ini di versi Chrome mendatang. Lihat crbug.com/737678 untuk mengetahui informasi selengkapnya.

Jadi, saya masih memerlukan Xvfb?

Tidak. Chrome Headless tidak menggunakan jendela sehingga server tampilan seperti Xvfb tidak diperlukan lagi. Anda dapat menjalankan pengujian otomatis dengan lancar tanpanya.

Apa yang dimaksud dengan Xvfb? Xvfb adalah server tampilan dalam memori untuk sistem seperti Unix yang memungkinkan Anda menjalankan aplikasi grafis (seperti Chrome) tanpa adanya layar fisik yang terpasang. Banyak orang menggunakan Xvfb untuk menjalankan Chrome versi sebelumnya guna melakukan pengujian "headless".

Bagaimana cara membuat container Docker yang menjalankan Headless Chrome?

Lihat lighthouse-ci. Aplikasi ini memiliki contoh Dockerfile yang menggunakan node:8-slim sebagai image dasar, menginstal + menjalankan Lighthouse di App Engine Flex.

Dapatkah saya menggunakannya dengan Selenium / WebDriver / ChromeDriver?

Ya. Lihat Menggunakan Selenium, WebDriver, dan ChromeDriver.

Bagaimana hubungannya dengan PhantomJS?

Headless Chrome mirip dengan alat seperti PhantomJS. Keduanya dapat digunakan untuk pengujian otomatis di lingkungan headless. Perbedaan utama antara keduanya adalah Phantom menggunakan WebKit versi lama sebagai mesin renderingnya, sedangkan Chrome Headless menggunakan Blink versi terbaru.

Saat ini, Phantom juga menyediakan API level yang lebih tinggi daripada protokol DevTools.

Di mana saya dapat melaporkan bug?

Untuk bug terkait Headless Chrome, laporkan di crbug.com.

Untuk bug dalam protokol DevTools, laporkan di github.com/ChromeDevTools/devtools-protocol.