TL;DR
Chrome แบบ Headless พร้อมให้บริการใน Chrome 59 เป็นวิธีเรียกใช้เบราว์เซอร์ Chrome ในสภาพแวดล้อมแบบ Headless พูดง่ายๆ คือ เรียกใช้ Chrome โดยไม่ใช้ Chrome ซึ่งจะนําฟีเจอร์ทั้งหมดของแพลตฟอร์มเว็บสมัยใหม่ที่ Chromium และเครื่องมือแสดงผล Blink ให้บริการมาไว้ในบรรทัดคําสั่ง
เหตุใดจึงมีประโยชน์
เบราว์เซอร์แบบ Headless เป็นเครื่องมือที่ยอดเยี่ยมสำหรับการทดสอบอัตโนมัติและสภาพแวดล้อมเซิร์ฟเวอร์ที่คุณไม่จำเป็นต้องมีเชลล์ UI ที่มองเห็นได้ เช่น คุณอาจต้องการทำการทดสอบกับหน้าเว็บจริง สร้าง PDF ของหน้าเว็บ หรือตรวจสอบวิธีที่เบราว์เซอร์แสดงผล URL
การเริ่มใช้งานแบบไม่มีส่วนหัว (CLI)
วิธีที่ง่ายที่สุดในการเริ่มต้นใช้งานโหมด headless คือเปิดไบนารีของ Chrome จากบรรทัดคำสั่ง หากติดตั้ง Chrome 59 ขึ้นไป ให้เริ่ม Chrome ด้วย 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
ควรชี้ไปยังการติดตั้ง 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 มีFlag บรรทัดคำสั่งที่มีประโยชน์บางอย่างสำหรับดำเนินการงานทั่วไป
การพิมพ์ DOM
แฟล็ก --dump-dom
จะพิมพ์ document.body.innerHTML
ไปยัง stdout
chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/
สร้าง PDF
Flag --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
ในไดเรกทอรีทํางานปัจจุบัน หากต้องการภาพหน้าจอแบบเต็มหน้า กระบวนการจะซับซ้อนขึ้นเล็กน้อย เรามีบล็อกโพสต์ที่ยอดเยี่ยมจาก David Schnurr ที่จะช่วยเหลือคุณ ดูหัวข้อการใช้ Chrome แบบ Headless เป็นเครื่องมือจับภาพหน้าจออัตโนมัติ
โหมด REPL (อ่าน ประเมิน และแสดงผลซ้ำ)
Flag --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 โดยไม่ใช้ UI ของเบราว์เซอร์ใช่ไหม
เมื่อคุณเรียกใช้ Chrome ด้วย --remote-debugging-port=9222
ระบบจะเริ่มอินสแตนซ์ที่เปิดใช้โปรโตคอล DevTools โปรโตคอลนี้ใช้เพื่อสื่อสารกับ Chrome และขับเคลื่อนอินสแตนซ์เบราว์เซอร์แบบ Headless นอกจากนี้ ยังเป็นเครื่องมือที่ Sublime, VS Code และ Node ใช้สำหรับการแก้ไขข้อบกพร่องระยะไกลของแอปพลิเคชันด้วย #synergy
เนื่องจากคุณไม่มี UI ของเบราว์เซอร์เพื่อดูหน้าเว็บ ให้ไปที่ http://localhost:9222
ในเบราว์เซอร์อื่นเพื่อตรวจสอบว่าทุกอย่างทำงานได้ คุณจะเห็นรายการหน้าเว็บที่ตรวจสอบได้ ซึ่งคุณสามารถคลิกดูและดูสิ่งที่ Headless แสดงผล
จากตรงนี้ คุณสามารถใช้ฟีเจอร์เครื่องมือสำหรับนักพัฒนาเว็บที่คุ้นเคยเพื่อตรวจสอบ แก้ไขข้อบกพร่อง และปรับแต่งหน้าเว็บได้ตามปกติ หากคุณใช้ Headless แบบเป็นโปรแกรม หน้านี้ยังเป็นเครื่องมือแก้ไขข้อบกพร่องที่มีประสิทธิภาพในการดูคําสั่งโปรโตคอล DevTools ดิบทั้งหมดที่ส่งผ่านเครือข่ายเพื่อสื่อสารกับเบราว์เซอร์
การใช้แบบเป็นโปรแกรม (Node)
ผู้เชิดหุ่น
Puppeteer เป็นไลบรารี Node ที่พัฒนาโดยทีม Chrome ซึ่งให้บริการ API ระดับสูงเพื่อควบคุม Chrome แบบ Headless (หรือแบบเต็ม) ซึ่งคล้ายกับไลบรารีการทดสอบอัตโนมัติอื่นๆ เช่น Phantom และ NightmareJS แต่ใช้งานได้กับ Chrome เวอร์ชันล่าสุดเท่านั้น
นอกเหนือจากนี้ Puppeteer ยังใช้ถ่ายภาพหน้าจอ สร้าง PDF ไปยังส่วนต่างๆ ในหน้าเว็บ และดึงข้อมูลเกี่ยวกับหน้าเว็บเหล่านั้นได้อย่างง่ายดาย เราขอแนะนําให้ใช้ไลบรารีนี้หากต้องการทําให้การทดสอบเบราว์เซอร์เป็นแบบอัตโนมัติอย่างรวดเร็ว ซึ่งจะซ่อนความซับซ้อนของโปรโตคอล DevTools และจัดการงานที่ซ้ำซ้อน เช่น การเปิดตัวอินสแตนซ์แก้ไขข้อบกพร่องของ Chrome
วิธีติดตั้ง
npm i --save puppeteer
ตัวอย่าง - พิมพ์ User Agent
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 เป็นเครื่องมือที่ยอดเยี่ยมสำหรับการทดสอบคุณภาพของเว็บแอป โมดูลที่มีประสิทธิภาพสำหรับการเปิดใช้ Chrome ได้รับการพัฒนาภายใน Lighthouse และตอนนี้ได้แยกออกมาเพื่อใช้งานแบบสแตนด์อโลนแล้ว
chrome-launcher
โมดูล NPM จะค้นหาตำแหน่งที่ติดตั้ง Chrome, ตั้งค่าอินสแตนซ์แก้ไขข้อบกพร่อง, เปิดเบราว์เซอร์ และปิดเบราว์เซอร์เมื่อโปรแกรมเสร็จสิ้น ที่สำคัญคือใช้งานได้ข้ามแพลตฟอร์มด้วย Node
โดยค่าเริ่มต้น chrome-launcher
จะพยายามเปิด Chrome Canary (หากติดตั้งไว้) แต่คุณสามารถเปลี่ยนเพื่อเลือก Chrome ที่จะใช้ด้วยตนเองได้ หากต้องการใช้ ให้ติดตั้งจาก npm ก่อน โดยทำดังนี้
npm i --save chrome-launcher
ตัวอย่าง - การใช้ chrome-launcher
เพื่อเปิดใช้โหมดไม่มีส่วนหัว
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
โปรดทราบว่าจะไม่มี UI ของเบราว์เซอร์ เราไม่มีส่วนหัว
เราต้องใช้โปรโตคอล DevTools เพื่อควบคุมเบราว์เซอร์
ดึงข้อมูลเกี่ยวกับหน้าเว็บ
มาติดตั้งไลบรารีกัน
npm i --save chrome-remote-interface
ตัวอย่าง
ตัวอย่าง - พิมพ์ User Agent
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
ตัวอย่าง - ตรวจสอบว่าเว็บไซต์มีไฟล์ Manifest ของเว็บแอปหรือไม่
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 แบบเต็ม กล่าวคือ เป็นโซลูชันอัตโนมัติแต่ไม่ใช่แบบ Headless ทั้งหมด อย่างไรก็ตาม Selenium สามารถกําหนดค่าให้ทํางานแบบ Headless Chrome ได้ เราขอแนะนําให้เรียกใช้ Selenium ด้วย Headless Chrome หากต้องการวิธีการแบบเต็มในการตั้งค่าด้วยตนเอง แต่เราได้ใส่ตัวอย่างบางส่วนไว้ด้านล่างเพื่อช่วยให้คุณเริ่มต้นใช้งาน
การใช้ ChromeDriver
ChromeDriver 2.32 ใช้ Chrome 61 และทำงานร่วมกับ Chrome แบบ Headless ได้ดี
ติดตั้ง
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();
})();
แหล่งข้อมูลเพิ่มเติม
แหล่งข้อมูลที่มีประโยชน์สำหรับการเริ่มต้นใช้งานมีดังนี้
เอกสาร
- เครื่องมือดูโปรโตคอลของ DevTools - เอกสารอ้างอิง API
เครื่องมือ
- chrome-remote-interface - โมดูล Node ที่รวมโปรโตคอล DevTools
- Lighthouse - เครื่องมืออัตโนมัติสำหรับทดสอบคุณภาพเว็บแอป ใช้งานโปรโตคอลนี้อย่างหนัก
- chrome-launcher - โมดูล Node สำหรับเปิด Chrome พร้อมใช้งานการทำงานอัตโนมัติ
เดโม
- "The Headless Web" - บล็อกโพสต์ที่ยอดเยี่ยมของ Paul Kinlan เกี่ยวกับการใช้ Headless กับ api.ai
คำถามที่พบบ่อย
ฉันต้องใช้การแจ้งว่าไม่เหมาะสม --disable-gpu
ไหม
เฉพาะใน Windows เท่านั้น แพลตฟอร์มอื่นๆ ไม่จำเป็นต้องใช้ การแจ้งว่าไม่เหมาะสม --disable-gpu
เป็นวิธีแก้ปัญหาชั่วคราวสำหรับข้อบกพร่องบางรายการ คุณจะไม่ต้องใช้ Flag นี้ใน Chrome เวอร์ชันต่อๆ ไป ดูข้อมูลเพิ่มเติมได้ที่ crbug.com/737678
ฉันยังต้องใช้ Xvfb อยู่ใช่ไหม
ไม่ Chrome แบบ headless ไม่ได้ใช้หน้าต่าง คุณจึงไม่จำเป็นต้องใช้เซิร์ฟเวอร์การแสดงผลอย่าง Xvfb อีกต่อไป คุณทําการทดสอบอัตโนมัติได้โดยไม่ต้องใช้
Xvfb คืออะไร Xvfb เป็นเซิร์ฟเวอร์การแสดงผลในหน่วยความจำสำหรับระบบที่คล้าย Unix ซึ่งช่วยให้คุณเรียกใช้แอปพลิเคชันกราฟิก (เช่น Chrome) ได้โดยไม่ต้องต่อจอแสดงผล ผู้ใช้จํานวนมากใช้ Xvfb เพื่อเรียกใช้ Chrome เวอร์ชันเก่าสําหรับการทดสอบ "Headless"
ฉันจะสร้างคอนเทนเนอร์ Docker ที่เรียกใช้ Chrome แบบ Headless ได้อย่างไร
โปรดดู lighthouse-ci ไฟล์นี้มีตัวอย่าง Dockerfile ใช้ node:8-slim
เป็นอิมเมจฐาน ติดตั้ง + เรียกใช้ Lighthouse ใน App Engine Flex
ฉันใช้ Selenium / WebDriver / ChromeDriver กับเครื่องมือนี้ได้ไหม
ได้ ดูการใช้ Selenium, WebDriver และ ChromeDriver
การดำเนินการนี้เกี่ยวข้องกับ PhantomJS อย่างไร
Headless Chrome คล้ายกับเครื่องมืออย่าง PhantomJS ทั้ง 2 รายการนี้ใช้สำหรับการทดสอบอัตโนมัติในสภาพแวดล้อมแบบ Headless ได้ ความแตกต่างหลักระหว่าง 2 อย่างนี้คือ Phantom ใช้ WebKit เวอร์ชันเก่าเป็นเครื่องมือแสดงผล ส่วน Headless Chrome ใช้ Blink เวอร์ชันล่าสุด
ปัจจุบัน Phantom ยังมี API ระดับสูงกว่าโปรโตคอล DevTools ด้วย
ฉันจะรายงานข้อบกพร่องได้ที่ใด
สำหรับข้อบกพร่องเกี่ยวกับ Chrome แบบ Headless โปรดรายงานที่ crbug.com
สำหรับข้อบกพร่องในโปรโตคอล DevTools โปรดรายงานที่ github.com/ChromeDevTools/devtools-protocol