Headless Chrome入門

原文地址:Getting Started with Headless Chrome  By Eric Bidelman  Engineer @ Google working on web tooling: Headless Chrome, Puppeteer, Lighthousenode

Headless Chrome在Chrome59中發佈,用於在headless環境中運行Chrome瀏覽器,也就是在非Chrome環境中運行Chrome。它將Chromium和Blink渲染引擎提供的全部現代Web平臺功能引入命令行。git

它有什麼用處呢?github

headless瀏覽器是自動測試和服務器環境的絕佳工具,您不須要可見的UI shell。例如,針對真實的網頁進行測試,建立網頁的PDF,或者只是檢查瀏覽器如何呈現URL。web

0. 開始

最簡單的開始使用headless模式的方法是從命令行打開Chrome。若是你已經安裝了Chrome59+的版本,可使用 --headless 標籤:chrome

chrome \
  --headless \                   # 在headless模式運行Chrome
  --disable-gpu \                # 在Windows上運行時須要--remote-debugging-port=9222 \
  https://www.chromestatus.com   # 打開URL. 默認爲about:blank

注意:若在Windows中運行,則須要在命令行添加 --disable-gpu 。shell

 chrome 命令須要指向Chrome的安裝路徑。(即在Chrome的安裝路徑下運行)express

1. 命令行功能

在某些狀況下,您可能不須要以編程方式編寫Headless Chrome腳本。下面是一些有用的命令行標誌來執行常見任務。npm

1.1 打印DOM --dump-dom 

將 document.body.innerHTML 在stdout打印出來:編程

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

1.2 建立PDF --print-to-pdf :

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

演示:在chrome安裝目錄下運行 chrome --headless --disable-gpu --print-to-pdf https://www.baidu.com/ api

生成PDF文件:C:\Program Files (x86)\Google\Chrome\Application\69.0.3497.81\output.pdf

 

1.3 截屏 --screenshot 

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

# 標準屏幕大小
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 做爲自動截屏工具

1.4 REPL模式(read-eval-print loop) --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
$

注意:使用repl模式時須要添加 --crash-dumps-dir 命令。

2. 在沒有瀏覽器界面狀況下調試Chrome

當使用 --remote-debugging-port=9222 運行Chrome時,會啓用DevTools協議的實例。該協議用於與Chrome通訊而且驅動headless瀏覽器實例。除此以外,它仍是一個相似於 Sublime, VS Code, 和Node的工具,可用於遠程調試一個應用。

因爲沒有瀏覽器UI來查看頁面,所以須要在另外一個瀏覽器中導航到http:// localhost:9222以檢查一切是否正常。這將看到一個可查看頁面的列表,能夠在其中單擊並查看Headless正在呈現的內容:

DevTools遠程調試界面

在這裏,你可使用熟悉的DecTools功能來查看、調試、修改頁面。若以編程方式(programmatically)使用Headless,該頁面的功能更強大,能夠用於查看全部的DecTools協議的命令,並與瀏覽器進行通訊。

3. 使用編程模式(Node)

3.1 Puppeteer

Puppeteer 由Chrome團隊開發的Node庫。它提供了控制headless Chrome的高階API。相似於 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's 文檔 學習Puppeteer的更多用法。

3.2 CRI庫

相對於Puppeteer's API來講,chrome-remote-interface 是一個低階的庫,推薦使用它更接近底層地直接使用DevTools協議。

打開Chrome

chrome-remote-interface不能打開Chrome,所以須要本身打開Chrome。

在CLI部分,咱們使用--headless --remote-debugging-port = 9222手動打開Chrome。可是,要實現徹底自動化測試,您可能但願從應用程序中生成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的硬編碼路徑吧:(

使用ChromeLaucher

Lighthouse 是測試web應用質量絕佳工具。用於啓動Chrome的強大的模塊就是在Lighthouse中開發的,如今能夠單獨使用。  chrome-launcher NPM module 能夠找到Chrome的安裝路徑,設置調試實例,打開瀏覽器,而且當程序運行完成時關掉它。最棒的是,因爲Node,它能夠跨平臺工做!

默認狀況下,chrome-launcher會嘗試啓動Chrome Canary(若是已安裝),但能夠更改它以手動選擇要使用的Chrome。要使用它,首先從npm安裝:

npm i --save chrome-launcher

例子-使用 chrome-launcher 啓動Headless模式

const chromeLauncher = require('chrome-launcher');

// 可選: 設置launcher的日誌記錄級別以查看其輸出
// 安裝:: npm i --save lighthouse-logger
// const log = require('lighthouse-logger');
// log.setLevel('info');

/**
 * 啓動Chrome的調試實例
 * @param {boolean=} headless True (default) 啓動headless模式的Chrome.
 *     False 啓動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 。可是沒有瀏覽器界面。由於是headless模式。

要控制瀏覽器,咱們須要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.
});

})();

例子-使用DOM API提取頁面的<title>

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

})();

4. 使用Selenium,W​​ebDriver和ChromeDriver

如今,Selenium打開了一個完整地Chrome的實例,也就是說,換句話說,它是一種自動化解決方案,但並不是徹底headless。可是,Selenium能夠經過一些配置來運行headless Chrome。我建議使用headless Chrome運行Selenium,若你仍是想要如何本身設置的完整說明,我已經在下面的一些例子中展現瞭如何讓你放棄。

使用ChromeDriver

ChromeDriver 2.32使用了Chrome61,而且在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 是Selenium WebDriver之上的更高階的API。

安裝:

npm i --save-dev webdriverio chromedriver

例子-chromestatus.com上的CSS filter功能

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

})();

5. 更多資源

如下是一些有用的資源,可幫助您入門:

文檔:

工具:

演示:

6. FAQ

6.1 是否須要 --disable-gpu 命令?

僅Windows平臺須要。其餘平臺不須要。--disable-gpu命令是一個臨時解決一些錯誤的方案。在未來的Chrome版本中,再也不須要此命令。有關更多信息,請參閱 crbug.com/737678

6.2 是否須要 Xvfb

不須要。Headless Chrome不使用窗口,所以再也不須要像Xvfb這樣的顯示服務器。沒有它,也能夠愉快地運行自動化測試。

什麼是Xvfb?Xvfb是一種用於類Unix系統的內存顯示服務器,它使您可以運行圖形應用程序(如Chrome)而無需附加物理顯示設備。許多人使用Xvfb運行早期版本的Chrome進行「headless」測試。

6.3 如何建立運行Headless Chrome的Docker容器?

看看lighthouse-ci。它有一個示例 Dockerfile ,它使用node:8-slim做爲基本映像,在App Engine Flex上安裝+ 運行Lighthouse 

6.4 Headless Chrome與PhantomJS有什麼關係?

Headless Chrome與PhantomJS等工具相似。二者均可用於headless環境中的自動化測試。二者之間的主要區別在於Phantom使用較舊版本的WebKit做爲其渲染引擎,而Headless Chrome使用最新版本的Blink。

目前,Phantom還提供了比DevTools 協議更高級別的API。

6.5 在哪裏提交bugs?

對於Headless Chrome的bugs,請在crbug.com上提交。

對於DevTools協議中的錯誤,請將它們發送到github.com/ChromeDevTools/devtools-protocol

相關文章
相關標籤/搜索