Headless Chrome入門

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

Headless Chrome在Chrome59中發(fā)布,用于在headless環(huán)境中運(yùn)行Chrome瀏覽器,也就是在非Chrome環(huán)境中運(yùn)行Chrome。它將Chromium和Blink渲染引擎提供的所有現(xiàn)代Web平臺(tái)功能引入命令行。

它有什么用處呢?

headless瀏覽器是自動(dòng)測(cè)試和服務(wù)器環(huán)境的絕佳工具,您不需要可見(jiàn)的UI shell。例如,針對(duì)真實(shí)的網(wǎng)頁(yè)進(jìn)行測(cè)試,創(chuàng)建網(wǎng)頁(yè)的PDF,或者只是檢查瀏覽器如何呈現(xiàn)URL。

0. 開(kāi)始

最簡(jiǎn)單的開(kāi)始使用headless模式的方法是從命令行打開(kāi)Chrome。如果你已經(jīng)安裝了Chrome59+的版本,可以使用 --headless 標(biāo)簽:

chrome \
  --headless \                   # 在headless模式運(yùn)行Chrome
  --disable-gpu \                # 在Windows上運(yùn)行時(shí)需要--remote-debugging-port=9222 \
  https://www.chromestatus.com   # 打開(kāi)URL. 默認(rèn)為about:blank

注意:若在Windows中運(yùn)行,則需要在命令行添加 --disable-gpu 。

chrome 命令需要指向Chrome的安裝路徑。(即在Chrome的安裝路徑下運(yùn)行)

1. 命令行功能

在某些情況下,您可能不需要以編程方式編寫Headless Chrome腳本。下面是一些有用的命令行標(biāo)志來(lái)執(zhí)行常見(jiàn)任務(wù)。

1.1 打印DOM --dump-dom

將 document.body.innerHTML 在stdout打印出來(lái):

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

1.2 創(chuàng)建PDF --print-to-pdf :

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

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

命令行運(yùn)行

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

生成的pdf文件

1.3 截屏 --screenshot

chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/
 # 標(biāo)準(zhǔn)屏幕大小
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/

運(yùn)行 --screenshot將會(huì)在當(dāng)前運(yùn)行目錄下生成一個(gè) screenshot.png 文件。若想給整個(gè)頁(yè)面的截圖,那么會(huì)比較復(fù)雜。來(lái)自 David Schnurr 的一篇很棒的博文介紹了這一內(nèi)容。請(qǐng)查看 使用 headless Chrome 作為自動(dòng)截屏工具。

1.4 REPL模式(read-eval-print loop) --repl

在REPL模式運(yùn)行Headless,該模式允許通過(guò)命令行在瀏覽器中評(píng)估JS表達(dá)式:

$ 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模式時(shí)需要添加 --crash-dumps-dir 命令。

2. 在沒(méi)有瀏覽器界面情況下調(diào)試Chrome

當(dāng)使用 --remote-debugging-port=9222 運(yùn)行Chrome時(shí),會(huì)啟用DevTools協(xié)議的實(shí)例。該協(xié)議用于與Chrome通信并且驅(qū)動(dòng)headless瀏覽器實(shí)例。除此之外,它還是一個(gè)類似于 Sublime, VS Code, 和Node的工具,可用于遠(yuǎn)程調(diào)試一個(gè)應(yīng)用。

由于沒(méi)有瀏覽器UI來(lái)查看頁(yè)面,因此需要在另一個(gè)瀏覽器中導(dǎo)航到http:// localhost:9222以檢查一切是否正常。這將看到一個(gè)可查看頁(yè)面的列表,可以在其中單擊并查看Headless正在呈現(xiàn)的內(nèi)容:

DevTools遠(yuǎn)程調(diào)試界面

在這里,你可以使用熟悉的DecTools功能來(lái)查看、調(diào)試、修改頁(yè)面。若以編程方式(programmatically)使用Headless,該頁(yè)面的功能更強(qiáng)大,可以用于查看所有的DecTools協(xié)議的命令,并與瀏覽器進(jìn)行通信。

3. 使用編程模式(Node)

3.1 Puppeteer

Puppeteer 由Chrome團(tuán)隊(duì)開(kāi)發(fā)的Node庫(kù)。它提供了控制headless Chrome的高階API。類似于 Phantom 和 NightmareJS這樣的自動(dòng)測(cè)試庫(kù),但它只用于最新版本的Chrome。

除此之外,Puppeteer還可用于截屏,創(chuàng)建PDF,頁(yè)面導(dǎo)航,以及獲取有關(guān)這些頁(yè)面的信息。如果需要快速進(jìn)行瀏覽器的自動(dòng)化測(cè)試,建議使用該庫(kù)。它隱藏了DevTools協(xié)議的復(fù)雜性,并負(fù)責(zé)啟動(dòng)Chrome的調(diào)試實(shí)例等冗余任務(wù)。

安裝:

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 文檔 學(xué)習(xí)Puppeteer的更多用法。

3.2 CRI庫(kù)

相對(duì)于Puppeteer's API來(lái)說(shuō),chrome-remote-interface 是一個(gè)低階的庫(kù),推薦使用它更接近底層地直接使用DevTools協(xié)議。

打開(kāi)Chrome

chrome-remote-interface不能打開(kāi)Chrome,因此需要自己打開(kāi)Chrome。

在CLI部分,我們使用--headless --remote-debugging-port = 9222手動(dòng)打開(kāi)Chrome。但是,要實(shí)現(xiàn)完全自動(dòng)化測(cè)試,您可能希望從應(yīng)用程序中生成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) => {
  ...
});

但是如果你想要一個(gè)適用于多個(gè)平臺(tái)的可移植解決方案,那么事情會(huì)變得棘手??纯碈hrome的硬編碼路徑吧:(

使用ChromeLaucher

Lighthouse 是測(cè)試web應(yīng)用質(zhì)量絕佳工具。用于啟動(dòng)Chrome的強(qiáng)大的模塊就是在Lighthouse中開(kāi)發(fā)的,現(xiàn)在可以單獨(dú)使用。 chrome-launcher NPM module 可以找到Chrome的安裝路徑,設(shè)置調(diào)試實(shí)例,打開(kāi)瀏覽器,并且當(dāng)程序運(yùn)行完成時(shí)關(guān)掉它。最棒的是,由于Node,它可以跨平臺(tái)工作!

默認(rèn)情況下,chrome-launcher會(huì)嘗試啟動(dòng)Chrome Canary(如果已安裝),但可以更改它以手動(dòng)選擇要使用的Chrome。要使用它,首先從npm安裝:

npm i --save chrome-launcher

例子-使用 chrome-launcher 啟動(dòng)Headless模式

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

/**
 * 啟動(dòng)Chrome的調(diào)試實(shí)例
 * @param {boolean=} headless True (default) 啟動(dòng)headless模式的Chrome.
 *     False 啟動(dòng)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();
});

運(yùn)行此腳本并沒(méi)有太大作用,但在任務(wù)管理器中應(yīng)該可以看到Chrome實(shí)例已啟動(dòng),內(nèi)容為 about:blank 。但是沒(méi)有瀏覽器界面。因?yàn)槭莌eadless模式。

要控制瀏覽器,我們需要DevTools協(xié)議!

檢索有關(guān)頁(yè)面的信息

安裝:

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']);
});

結(jié)果類似于: HeadlessChrome/60.0.3082.0

例子-檢查網(wǎng)站是否有應(yīng)用列表

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提取頁(yè)面的<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

現(xiàn)在,Selenium打開(kāi)了一個(gè)完整地Chrome的實(shí)例,也就是說(shuō),換句話說(shuō),它是一種自動(dòng)化解決方案,但并非完全headless。但是,Selenium可以通過(guò)一些配置來(lái)運(yùn)行headless Chrome。我建議使用headless Chrome運(yùn)行Selenium,若你還是想要如何自己設(shè)置的完整說(shuō)明,我已經(jīng)在下面的一些例子中展示了如何讓你放棄。

使用ChromeDriver

ChromeDriver 2.32使用了Chrome61,并且在headless Chrome運(yùn)行的更好。

安裝:

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. 更多資源

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

文檔:

工具:

  • chrome-remote-interface - 包裝DevTools協(xié)議的節(jié)點(diǎn)模塊
  • Lighthouse - 用于測(cè)試Web應(yīng)用質(zhì)量的自動(dòng)化工具;大量使用協(xié)議
  • chrome-launcher - 節(jié)點(diǎn)模塊,用于啟動(dòng)Chrome,為自動(dòng)化做好準(zhǔn)備

演示:

  • "The Headless Web" - Paul Kinlan關(guān)于使用Headless和api.ai的博客文章

6. FAQ

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

僅Windows平臺(tái)需要。其他平臺(tái)不需要。--disable-gpu命令是一個(gè)臨時(shí)解決一些錯(cuò)誤的方案。在將來(lái)的Chrome版本中,不再需要此命令。有關(guān)更多信息,請(qǐng)參閱 crbug.com/737678。

6.2 是否需要 Xvfb?

不需要。Headless Chrome不使用窗口,因此不再需要像Xvfb這樣的顯示服務(wù)器。沒(méi)有它,也可以愉快地運(yùn)行自動(dòng)化測(cè)試。

什么是Xvfb?Xvfb是一種用于類Unix系統(tǒng)的內(nèi)存顯示服務(wù)器,它使您能夠運(yùn)行圖形應(yīng)用程序(如Chrome)而無(wú)需附加物理顯示設(shè)備。許多人使用Xvfb運(yùn)行早期版本的Chrome進(jìn)行“headless”測(cè)試。

6.3 如何創(chuàng)建運(yùn)行Headless Chrome的Docker容器?

看看lighthouse-ci。它有一個(gè)示例 Dockerfile ,它使用node:8-slim作為基本映像,在App Engine Flex上安裝+ 運(yùn)行Lighthouse

6.4 Headless Chrome與PhantomJS有什么關(guān)系?

Headless Chrome與PhantomJS等工具類似。兩者都可用于headless環(huán)境中的自動(dòng)化測(cè)試。兩者之間的主要區(qū)別在于Phantom使用較舊版本的WebKit作為其渲染引擎,而Headless Chrome使用最新版本的Blink。

目前,Phantom還提供了比DevTools 協(xié)議更高級(jí)別的API。

6.5 在哪里提交bugs?

對(duì)于Headless Chrome的bugs,請(qǐng)?jiān)?a target="_blank" rel="nofollow">crbug.com上提交。

對(duì)于DevTools協(xié)議中的錯(cuò)誤,請(qǐng)將它們發(fā)送到github.com/ChromeDevTools/devtools-protocol。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 技術(shù)雷達(dá)快訊:自2017年中以來(lái),Chrome用戶可以選擇以headless模式運(yùn)行瀏覽器。此功能非常適合運(yùn)行前端...
    ThoughtWorks閱讀 8,043評(píng)論 8 28
  • 1.puppeteer簡(jiǎn)介 puppeteer是一個(gè)node庫(kù),是Google chrome團(tuán)隊(duì)官方的無(wú)界面(he...
    伊人風(fēng)采_690d閱讀 7,812評(píng)論 0 11
  • 昨晚先生單位有事,搞的一晚上沒(méi)有睡好。昨天剛買的煎餅檔本想退回去,想想就算了,將就用吧,早上和兒子一起合作好早飯,...
    姣燕閱讀 355評(píng)論 0 1
  • 風(fēng)告訴了我該去的地方, 云擋住了我的去路; 我不知道云里是什么, 于是我扎進(jìn)了云里, 可是我還什么都沒(méi)有看清云就隨...
    空氣中的魚(yú)閱讀 292評(píng)論 0 3

友情鏈接更多精彩內(nèi)容