Shell của Chrome không có giao diện người dùng

Shell Chrome không có giao diện người dùng được phát hành trong Chrome 59. Đây là một cách để chạy trình duyệt Chrome trong môi trường không có giao diện người dùng. Về cơ bản, bạn có thể chạy Chrome mà không cần Chrome! Nó mang đến tất cả các tính năng của nền tảng web hiện đại do Chromium và công cụ kết xuất Blink cung cấp cho dòng lệnh.

Trình duyệt không giao diện người dùng là một công cụ tuyệt vời cho hoạt động kiểm thử tự động và môi trường máy chủ mà bạn không cần giao diện người dùng hiển thị. Ví dụ: bạn có thể muốn chạy một số kiểm thử trên một trang web thực, tạo tệp PDF của trang web đó hoặc chỉ kiểm tra cách trình duyệt hiển thị một URL.

Bắt đầu không có giao diện người dùng (CLI)

Cách dễ nhất để bắt đầu với chế độ không có giao diện người dùng là mở tệp nhị phân Chrome từ dòng lệnh. Nếu bạn đã cài đặt Chrome 59 trở lên, hãy khởi động Chrome bằng cờ --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 phải trỏ đến bản cài đặt Chrome của bạn. Vị trí chính xác sẽ khác nhau tuỳ theo nền tảng. Vì đang dùng máy Mac, nên tôi đã tạo các bí danh thuận tiện cho từng phiên bản Chrome mà tôi đã cài đặt.

Nếu bạn đang dùng kênh ổn định của Chrome và không thể tải được phiên bản Beta, hãy sử dụng 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"

Tải Chrome Canary xuống.

Tính năng dòng lệnh

Trong một số trường hợp, bạn có thể không cần tạo tập lệnh theo phương thức lập trình cho Chrome không có giao diện người dùng. Có một số cờ dòng lệnh hữu ích để thực hiện các tác vụ thông thường.

In DOM

Cờ --dump-dom in document.body.innerHTML ra stdout:

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


### Create a PDF

The `--print-to-pdf` flag creates a PDF of the page:

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

Chụp ảnh màn hình

Để chụp ảnh màn hình của một trang, hãy dùng cờ --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/

Khi chạy với --screenshot, một tệp có tên screenshot.png sẽ được tạo trong thư mục đang hoạt động. Nếu bạn đang tìm ảnh chụp màn hình toàn trang, thì mọi thứ sẽ phức tạp hơn một chút. David Schnurr đã viết một bài đăng rất hay trên blog để giải thích vấn đề này. Hãy xem bài viết Sử dụng Chrome không có giao diện người dùng làm công cụ chụp ảnh màn hình tự động .

Chế độ REPL (vòng lặp đọc-đánh giá-in)

Cờ --repl chạy Headless ở chế độ mà bạn có thể đánh giá các biểu thức JS trong trình duyệt, ngay từ dòng lệnh:

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

Gỡ lỗi Chrome mà không có giao diện người dùng trình duyệt?

Khi bạn chạy Chrome bằng --remote-debugging-port=9222, Chrome sẽ khởi động một phiên bản có bật giao thức DevTools. Giao thức này được dùng để giao tiếp với Chrome và điều khiển phiên bản trình duyệt không có giao diện người dùng. Đây cũng là những công cụ như Sublime, VS Code và Node dùng để gỡ lỗi từ xa cho một ứng dụng. #synergy

Vì bạn không có giao diện người dùng trình duyệt để xem trang, hãy chuyển đến http://localhost:9222 trong một trình duyệt khác để kiểm tra xem mọi thứ có hoạt động hay không. Bạn sẽ thấy danh sách các trang có thể kiểm tra. Tại đây, bạn có thể nhấp vào các trang đó để xem Headless đang kết xuất nội dung gì:

Công cụ cho nhà phát triển từ xa
Giao diện người dùng gỡ lỗi từ xa của Công cụ cho nhà phát triển

Từ đây, bạn có thể sử dụng các tính năng quen thuộc của Công cụ cho nhà phát triển để kiểm tra, gỡ lỗi và điều chỉnh trang như bình thường. Nếu bạn đang sử dụng Headless theo cách lập trình, thì trang này cũng là một công cụ gỡ lỗi mạnh mẽ để xem tất cả các lệnh giao thức DevTools thô đang truyền qua mạng, giao tiếp với trình duyệt.

Sử dụng theo phương thức lập trình (Node)

Puppeteer

Puppeteer là một thư viện Node do nhóm Chrome phát triển. Nó cung cấp một API cấp cao để kiểm soát Chrome không giao diện người dùng (hoặc Chrome đầy đủ). Thư viện này tương tự như các thư viện kiểm thử tự động khác như Phantom và NightmareJS, nhưng chỉ hoạt động với các phiên bản mới nhất của Chrome.

Ngoài những việc khác, bạn có thể dùng Puppeteer để dễ dàng chụp ảnh màn hình, tạo tệp PDF, di chuyển giữa các trang và tìm nạp thông tin về những trang đó. Bạn nên dùng thư viện này nếu muốn tự động hoá nhanh quá trình kiểm thử trình duyệt. Nó ẩn đi sự phức tạp của giao thức DevTools và xử lý các tác vụ dư thừa như khởi chạy một phiên bản gỡ lỗi của Chrome.

Cài đặt:

npm i --save puppeteer

Ví dụ – in tác nhân người dùng

const puppeteer = require('puppeteer');

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

Ví dụ – chụp ảnh màn hình trang

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

Hãy xem tài liệu của Puppeteer để tìm hiểu thêm về toàn bộ API.

Thư viện CRI

chrome-remote-interface là một thư viện cấp thấp hơn so với API của Puppeteer. Tôi khuyên bạn nên dùng nếu muốn gần với phần cứng và sử dụng trực tiếp giao thức DevTools.

Khởi chạy Chrome

chrome-remote-interface không khởi chạy Chrome cho bạn, vì vậy, bạn sẽ phải tự lo việc đó.

Trong phần CLI, chúng ta đã khởi động Chrome theo cách thủ công bằng cách sử dụng --headless --remote-debugging-port=9222. Tuy nhiên, để tự động hoá hoàn toàn các kiểm thử, có lẽ bạn sẽ muốn tạo Chrome từ ứng dụng của mình.

Một cách là sử dụng 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) => {
  ...
});

Nhưng mọi thứ sẽ trở nên phức tạp nếu bạn muốn có một giải pháp di động hoạt động trên nhiều nền tảng. Chỉ cần xem đường dẫn được mã hoá cứng đến Chrome :(

Sử dụng ChromeLauncher

Lighthouse là một công cụ tuyệt vời để kiểm tra chất lượng của các ứng dụng web. Một mô-đun mạnh mẽ để chạy Chrome đã được phát triển trong Lighthouse và hiện đã được trích xuất để sử dụng độc lập. chrome-launcher Mô-đun NPM sẽ tìm vị trí cài đặt Chrome, thiết lập một phiên bản gỡ lỗi, khởi chạy trình duyệt và tắt trình duyệt khi chương trình của bạn hoàn tất. Điều tuyệt vời nhất là ứng dụng này hoạt động trên nhiều nền tảng nhờ Node!

Theo mặc định, chrome-launcher sẽ cố gắng chạy Chrome Canary (nếu đã cài đặt), nhưng bạn có thể thay đổi chế độ này để chọn Chrome cần dùng theo cách thủ công. Để sử dụng, trước tiên hãy cài đặt từ npm:

npm i --save chrome-launcher

Ví dụ – sử dụng chrome-launcher để chạy 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();
});

Việc chạy tập lệnh này không có nhiều tác dụng, nhưng bạn sẽ thấy một phiên bản Chrome khởi động trong trình quản lý tác vụ đã tải about:blank. Hãy nhớ rằng sẽ không có giao diện người dùng trình duyệt. Chúng tôi không có giao diện người dùng.

Để kiểm soát trình duyệt, chúng ta cần giao thức DevTools!

Truy xuất thông tin về trang

Cách cài đặt thư viện:

npm i --save chrome-remote-interface
Ví dụ

Ví dụ – in tác nhân người dùng

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

...

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

Kết quả sẽ có dạng như: HeadlessChrome/60.0.3082.0

Ví dụ – kiểm tra xem trang web có tệp kê khai ứng dụng web hay khô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.
});

})();

Ví dụ – trích xuất <title> của trang bằng cách sử dụng API DOM.

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

})();

Sử dụng Selenium, WebDriver và ChromeDriver

Hiện tại, Selenium sẽ mở một phiên bản đầy đủ của Chrome. Nói cách khác, đây là một giải pháp tự động nhưng không hoàn toàn không cần giao diện. Tuy nhiên, bạn có thể định cấu hình Selenium để chạy Chrome cho môi trường không có giao diện người dùng chỉ bằng một chút công sức. Tôi đề xuất Chạy Selenium bằng Chrome không có giao diện người dùng nếu bạn muốn xem hướng dẫn đầy đủ về cách tự thiết lập mọi thứ. Tuy nhiên, tôi đã thêm một số ví dụ bên dưới để giúp bạn bắt đầu.

Sử dụng ChromeDriver

ChromeDriver 2.32 sử dụng Chrome 61 và hoạt động tốt với Chrome không có giao diện người dùng.

Cài đặt:

npm i --save-dev selenium-webdriver chromedriver

Ví dụ:

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

Sử dụng WebDriverIO

WebDriverIO là một API cấp cao hơn dựa trên Selenium WebDriver.

Cài đặt:

npm i --save-dev webdriverio chromedriver

Ví dụ: lọc các tính năng CSS trên 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();

})();

Tải Chrome không có giao diện người dùng cũ xuống dưới dạng chrome-headless-shell

Kể từ Chrome phiên bản 112, Chế độ không giao diện người dùng mới của Chrome (--headless=new) đã có sẵn. Chế độ này cho phép nhà phát triển chạy chính Chrome thay vì một tệp nhị phân riêng biệt trong một môi trường không cần giám sát mà không có giao diện người dùng nào hiển thị. Chế độ này hữu ích cho các trường hợp sử dụng kiểm thử và tự động hoá.

Có những trường hợp sử dụng riêng biệt cho shell không có giao diện người dùng cũ và Chế độ không có giao diện người dùng mới:

  1. Headless shell cũ là một trình bao bọc gọn nhẹ xung quanh mô-đun //content của Chromium, do đó, nó có ít phần phụ thuộc hơn đáng kể. Cụ thể, nó không yêu cầu X11/Wayland, D-Bus và có hiệu suất cao hơn trình duyệt Chrome đầy đủ theo một số cách. Điều này khiến cho tính năng này phù hợp với các trường hợp sử dụng như chụp ảnh màn hình tự động hoặc trích xuất dữ liệu trên web.
  2. Mặt khác, Chế độ không giao diện người dùng mới là trình duyệt Chrome thực sự, do đó, chế độ này xác thực, đáng tin cậy hơn và cung cấp nhiều tính năng hơn. Điều này khiến chế độ này phù hợp hơn cho việc kiểm thử ứng dụng web toàn diện có độ chính xác cao hoặc kiểm thử tiện ích trình duyệt.

Nói cách khác, có sự đánh đổi giữa hiệu suất và tính xác thực. Chế độ Headless nào phù hợp nhất với bạn? Điều này tuỳ thuộc vào trường hợp sử dụng của bạn.

Sơ đồ minh hoạ thông tin được cung cấp trong danh sách trước đó.

Những nhà phát triển và người kiểm thử không cần đầy đủ chức năng của Chrome cho các trường hợp sử dụng tính năng tự động hoá có thể muốn sử dụng Headless cũ. Nếu không, Headless mới có thể là lựa chọn tốt nhất.

Để đảm bảo nhà phát triển và người kiểm thử vẫn có thể lựa chọn giữa hai lựa chọn này, chúng tôi rất vui mừng thông báo rằng giờ đây, bạn có thể sử dụng chế độ triển khai Headless cũ dưới dạng tệp nhị phân chrome-headless-shell độc lập. Các tệp nhị phân chrome-headless-shell mới này được tạo cho mọi bản phát hành Chrome mà người dùng nhìn thấy và có thể tải xuống thông qua cơ sở hạ tầng Chrome for Testing bắt đầu từ Chrome 120.

Làm cách nào để tôi có thể nhận được tệp nhị phân chrome-headless-shell?

Giống như các tệp nhị phân Chrome for Testing khác, cách dễ nhất để tải chrome-headless-shell xuống cho nền tảng của bạn là sử dụng tiện ích dòng lệnh @puppeteer/browsers của chúng tôi, có sẵn khi dùng npm. Dưới đây là một số ví dụ:

# Download the latest available `chrome-headless-shell` binary corresponding to the Stable channel.
npx @puppeteer/browsers install chrome-headless-shell@stable

# Download a specific `chrome-headless-shell` version.
npx @puppeteer/browsers install chrome-headless-shell@120.0.6098.0

Nếu bạn muốn tự tạo tập lệnh tự động để tải các tệp nhị phân chrome-headless-shell xuống, chúng tôi sẽ hỗ trợ bạn. Chrome for Testing cung cấp các điểm cuối JSON API với các phiên bản mới nhất hiện có theo từng kênh phát hành của Chrome (Ổn định, Beta, Dev và Canary). Để xem thông tin tổng quan ngắn gọn về trạng thái mới nhất, hãy tham khảo trang tổng quan về phạm vi cung cấp Chrome for Testing.

Phản hồi

Chúng tôi rất mong nhận được ý kiến phản hồi của bạn về chrome-headless-shell. Nếu bạn gặp phải vấn đề, vui lòng báo cáo vấn đề đó.

Tài nguyên khác

Dưới đây là một số tài nguyên hữu ích giúp bạn bắt đầu:

Tài liệu

Công cụ

  • chrome-remote-interface – mô-đun nút bao bọc giao thức DevTools
  • Lighthouse – công cụ tự động để kiểm thử chất lượng ứng dụng web; sử dụng nhiều giao thức
  • chrome-launcher – mô-đun nút để chạy Chrome, sẵn sàng cho quy trình tự động hoá

Bản minh hoạ

  • "The Headless Web" (Web không có giao diện người dùng) – Bài đăng tuyệt vời trên blog của Paul Kinlan về việc sử dụng Headless với api.ai.

Câu hỏi thường gặp

Tôi có cần cờ --disable-gpu không?

Chỉ có trên Windows. Các nền tảng khác không còn yêu cầu điều này nữa. Cờ --disable-gpu là giải pháp tạm thời cho một số lỗi. Bạn sẽ không cần cờ này trong các phiên bản Chrome sau này. Hãy xem crbug.com/737678 để biết thêm thông tin.

Vậy tôi vẫn cần Xvfb?

Không. Chrome không có giao diện người dùng không sử dụng cửa sổ nên không cần đến máy chủ hiển thị như Xvfb nữa. Bạn có thể thoải mái chạy các kiểm thử tự động mà không cần đến thành phần này.

Xvfb là gì? Xvfb là một máy chủ hiển thị trong bộ nhớ cho các hệ thống tương tự như Unix, cho phép bạn chạy các ứng dụng đồ hoạ (như Chrome) mà không cần màn hình thực được kết nối. Nhiều người sử dụng Xvfb để chạy các phiên bản Chrome cũ hơn nhằm thực hiện kiểm thử "không có giao diện người dùng".

Làm cách nào để tạo một vùng chứa Docker chạy Chrome không có giao diện người dùng?

Hãy xem lighthouse-ci. Nó có một Dockerfile mẫu sử dụng node:8-slim làm hình ảnh cơ sở, cài đặt + chạy Lighthouse trên App Engine linh hoạt.

Tôi có thể sử dụng tính năng này với Selenium / WebDriver / ChromeDriver không?

Có. Xem phần Sử dụng Selenium, WebDriver và ChromeDriver.

Điều này liên quan như thế nào đến PhantomJS?

Headless Chrome tương tự như các công cụ như PhantomJS. Bạn có thể dùng cả hai để kiểm thử tự động trong môi trường không có giao diện người dùng. Điểm khác biệt chính giữa hai công cụ này là Phantom sử dụng phiên bản WebKit cũ hơn làm công cụ kết xuất, trong khi Chrome không có giao diện người dùng sử dụng phiên bản Blink mới nhất.

Hiện tại, Phantom cũng cung cấp API cấp cao hơn so với giao thức DevTools.

Tôi có thể báo cáo lỗi ở đâu?

Đối với các lỗi liên quan đến Chrome không có giao diện người dùng, hãy báo cáo trên crbug.com.

Đối với các lỗi trong giao thức DevTools, hãy báo cáo tại github.com/ChromeDevTools/devtools-protocol.