การเริ่มต้นใช้งาน Chrome แบบ Headless

สรุปคร่าวๆ

Chrome แบบไม่มีส่วนหัว จะจัดส่งใน Chrome 59 เป็นวิธีเรียกใช้เบราว์เซอร์ Chrome ในสภาพแวดล้อมที่ไม่มีส่วนหัว พูดง่ายๆ ก็คือเรียกใช้ Chrome โดยไม่ใช้ chrome! โดยจะนำฟีเจอร์แพลตฟอร์มเว็บที่ทันสมัยทั้งหมดจาก Chromium และเครื่องมือแสดงผล Blink มาไว้ในบรรทัดคำสั่ง

มีประโยชน์อย่างไร

เบราว์เซอร์แบบไม่มีส่วนหัวเป็นเครื่องมือที่ยอดเยี่ยมสำหรับการทดสอบอัตโนมัติและสภาพแวดล้อมของเซิร์ฟเวอร์ ซึ่งคุณไม่จำเป็นต้องมี UI Shell ที่มองเห็นได้ ตัวอย่างเช่น คุณอาจต้องการทดสอบบางอย่างกับหน้าเว็บจริง สร้างไฟล์ PDF หรือตรวจสอบว่าเบราว์เซอร์แสดงผล URL อย่างไร

การเริ่มเล่นแบบไม่มีส่วนหัว (CLI)

วิธีที่ง่ายที่สุดในการเริ่มต้นใช้งานโหมดไม่มีส่วนหัวคือการเปิดไบนารี 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 Flag พิมพ์ 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 ในไดเรกทอรีการทำงานปัจจุบัน ถ้าคุณกำลังมองหาภาพหน้าจอแบบเต็มหน้า สิ่งต่างๆ จะมีความเกี่ยวข้องมากกว่า เราพูดถึงบล็อกโพสต์ดีๆ จาก David Schnurr โปรดดูหัวข้อการใช้ Chrome แบบไม่มีส่วนหัวเป็นเครื่องมือภาพหน้าจออัตโนมัติ

โหมด REPL (วนซ้ำการอ่าน Eval-print)

แฟล็ก --repl เรียกใช้ส่วนหัวแบบไม่มีส่วนหัวในโหมดที่คุณประเมินนิพจน์ 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 จะเป็นการเริ่มต้นอินสแตนซ์ที่เปิดใช้โปรโตคอลเครื่องมือสำหรับนักพัฒนาเว็บ โปรโตคอลนี้ใช้สื่อสารกับ Chrome และขับเคลื่อนอินสแตนซ์เบราว์เซอร์แบบไม่มีส่วนหัว นอกจากนี้ยังเป็นเครื่องมือที่เครื่องมืออย่าง Sublime, VS Code และ Node ใช้เพื่อแก้ไขข้อบกพร่องระยะไกลของแอปพลิเคชัน #synergy

เนื่องจากคุณไม่มี UI ของเบราว์เซอร์เพื่อดูหน้านี้ ให้ไปที่ http://localhost:9222 ในเบราว์เซอร์อื่นเพื่อตรวจสอบว่าทุกอย่างใช้งานได้ คุณจะเห็นรายการหน้าเว็บที่ตรวจสอบได้ ซึ่งสามารถคลิกผ่านและดูว่า Headless มีการแสดงผลแบบใด

รีโมตเครื่องมือสำหรับนักพัฒนาเว็บ
UI การแก้ไขข้อบกพร่องระยะไกลสำหรับเครื่องมือสำหรับนักพัฒนาเว็บ

จากตรงนี้ คุณสามารถใช้ฟีเจอร์เครื่องมือสำหรับนักพัฒนาเว็บที่คุ้นเคยเพื่อตรวจสอบ แก้ไขข้อบกพร่อง และปรับแต่งหน้าเว็บได้ตามปกติ หากคุณใช้ Headless แบบเป็นโปรแกรม หน้านี้ยังเป็นเครื่องมือแก้ไขข้อบกพร่องที่มีประสิทธิภาพสำหรับดูคำสั่งโปรโตคอล DevTools ทั้งหมดผ่านสายข้อมูล และสื่อสารกับเบราว์เซอร์ได้

การใช้แบบเป็นโปรแกรม (โหนด)

นักเชิดหุ่นกระบอก

Puppeteer เป็นไลบรารีโหนด ที่พัฒนาโดยทีม Chrome โดยมอบ API ระดับสูงเพื่อควบคุม Chrome แบบไม่มีส่วนหัว (หรือเต็มรูปแบบ) คล้ายกับไลบรารีการทดสอบอัตโนมัติอื่นๆ เช่น 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 ฉันขอแนะนำว่าหากคุณต้องการอยู่ใกล้โลหะ และใช้โปรโตคอลเครื่องมือสำหรับนักพัฒนาเว็บโดยตรง

กำลังเปิด 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 เพื่อเปิดใช้ 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 อย่าลืมว่าจะไม่มี UI ของเบราว์เซอร์ เราไม่มีหัวเลย

ในการควบคุมเบราว์เซอร์ เราต้องใช้โปรโตคอลเครื่องมือสำหรับนักพัฒนาเว็บ

เรียกดูข้อมูลเกี่ยวกับหน้าเว็บ

มาติดตั้งไลบรารีกัน

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 อย่างเต็มรูปแบบแล้ว กล่าวอีกนัยหนึ่งคือเครื่องมือนี้เป็นโซลูชัน แบบอัตโนมัติ แต่มีส่วนหัวจริงๆ แต่ Selenium สามารถกำหนดค่า ให้ใช้ Chrome แบบไม่มีส่วนหัวได้โดยไม่ต้องยุ่งยาก เราขอแนะนำให้ เรียกใช้ Selenium กับ Chrome แบบ Headless หากคุณต้องการคำแนะนำทั้งหมดเกี่ยวกับวิธีตั้งค่าด้วยตนเอง แต่เราได้ยกตัวอย่างไว้ด้านล่างแล้ว

การใช้ ChromeDriver

ChromeDriver 2.32 ใช้ Chrome 61 และทำงานได้ดีกับ 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 คือ 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();

})();

แหล่งข้อมูลเพิ่มเติม

ต่อไปนี้เป็นแหล่งข้อมูลบางส่วนที่มีประโยชน์ในการเริ่มต้น:

เอกสาร

เครื่องมือ

  • chrome-remote-interface - โมดูล โหนดที่รวมโปรโตคอล DevTools
  • Lighthouse - เครื่องมืออัตโนมัติสำหรับการทดสอบคุณภาพของเว็บแอป ใช้โปรโตคอลเป็นจำนวนมาก
  • chrome-Launcher - โมดูลโหนดสำหรับเปิดใช้ Chrome พร้อมสำหรับการทำงานอัตโนมัติ

เดโม

  • "The Headless Web" - บล็อกโพสต์ที่ยอดเยี่ยมของ Paul Kinlan เกี่ยวกับการใช้ Headless กับ api.ai

คำถามที่พบบ่อย

ฉันต้องตั้งค่าสถานะ --disable-gpu ไหม

เฉพาะบน Windows คุณไม่จำเป็นต้องใช้แพลตฟอร์มอื่นๆ อีกต่อไป การแจ้ง --disable-gpu เป็นการแก้ปัญหาชั่วคราวสำหรับข้อบกพร่อง 2-3 อย่าง คุณไม่ต้องใช้แฟล็กนี้ใน 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 อย่างไร

Chrome แบบไม่มีส่วนหัวนั้นคล้ายกับเครื่องมือต่างๆ เช่น PhantomJS ทั้ง 2 อย่างใช้สำหรับการทดสอบอัตโนมัติในสภาพแวดล้อมที่ไม่มีส่วนหัว ความแตกต่างที่สำคัญระหว่างทั้ง 2 แบบคือ Phantom ใช้ WebKit เวอร์ชันเก่าเป็นเครื่องมือแสดงผล ขณะที่ Chrome แบบ Headless ใช้ Blink เวอร์ชันล่าสุด

ขณะนี้ Phantom ยังมี API ระดับที่สูงกว่าโปรโตคอล DevTools ด้วย

ฉันจะรายงานข้อบกพร่องได้ที่ใด

สำหรับข้อบกพร่องต่อ Chrome แบบ Headless ให้รายงานข้อบกพร่องที่ crbug.com

สำหรับข้อบกพร่องในโปรโตคอล DevTools โปรดรายงานข้อบกพร่องที่ github.com/ChromeDevTools/devtools-protocol