TL;DR
Chrome bez interfejsu graficznego jest dostępny w wersji 59. Jest to sposób na uruchomienie przeglądarki Chrome w sytuacji, gdy nie ma interfejsu graficznego. To tak, jakby uruchomić Chrome bez Chrome! Dodaje do wiersza poleceń wszystkie funkcje nowoczesnych platform internetowych udostępniane przez Chromium i silnik renderowania Blink.
Dlaczego to jest przydatne?
Bezgłowy przeglądarka to świetne narzędzie do automatycznego testowania i środowiska serwera, w których nie potrzebujesz widocznej powłoki interfejsu. Możesz na przykład przeprowadzić testy na rzeczywistej stronie internetowej, utworzyć jej plik PDF lub po prostu sprawdzić, jak przeglądarka renderuje URL.
Uruchamianie bez interfejsu graficznego (CLI)
Najprostszym sposobem na rozpoczęcie pracy w trybie bez obsługi grafiki jest otwarcie pliku binarnego Chrome z wiersza poleceń. Jeśli masz zainstalowaną przeglądarkę Chrome w wersji 59 lub nowszej, uruchom ją z 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
powinien wskazywać na instalację Chrome. Dokładna lokalizacja będzie się różnić w zależności od platformy. Korzystam z Maca, więc utworzyłem wygodne aliasy dla każdej zainstalowanej wersji Chrome.
Jeśli korzystasz z Chrome w wersji stabilnej i nie możesz zainstalować wersji beta, zalecam użycie 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"
Pobierz Chrome Canary tutaj.
Funkcje wiersza poleceń
W niektórych przypadkach nie trzeba programowo tworzyć skryptów dla przeglądarki bez ekranu Chrome. Do wykonywania typowych zadań możesz używać przydatnych flag wiersza poleceń.
Drukowanie DOM
Flaga --dump-dom
wypisuje document.body.innerHTML
na wyjście standardowe:
chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/
Tworzenie pliku PDF
Flaga --print-to-pdf
tworzy plik PDF strony:
chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/
Zrzuty ekranu
Aby zrobić zrzut ekranu strony, użyj flagi --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/
Wykonanie polecenia --screenshot
spowoduje utworzenie w bieżącym katalogu roboczym pliku o nazwie screenshot.png
. Jeśli chcesz uzyskać zrzuty ekranu całej strony, musisz wykonać kilka dodatkowych czynności. David Schnur napisał świetny post na blogu, który może Ci pomóc. Zapoznaj się z artykułem Używanie przeglądarki Chromium bez interfejsu jako narzędzia do automatycznego tworzenia zrzutów ekranu .
Tryb REPL (pętla odczyt-sprawdź-wypisz)
Flaga --repl
uruchamia Headless w trybie, w którym możesz oceniać wyrażenia JS w przeglądarce bezpośrednio z poziomu wiersza poleceń:
$ 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
$
Debugowanie Chrome bez interfejsu przeglądarki
Gdy uruchomisz Chrome z --remote-debugging-port=9222
, uruchomi instancję z włączonym protokołem Narzędzi deweloperskich. Protokół ten służy do komunikacji z Chrome i sterowania instancją przeglądarki pozbawionej interfejsu. Jest on również używany przez narzędzia takie jak Sublime, VS Code i Node do zdalnego debugowania aplikacji. #synergy
Ponieważ nie masz interfejsu przeglądarki, aby wyświetlić stronę, otwórz ją w innej przeglądarce (http://localhost:9222
), aby sprawdzić, czy wszystko działa. Zobaczysz listę stron do inspekcji, które możesz przeglądać, aby sprawdzić, co renderuje Headless:
Możesz tu korzystać ze znanych funkcji Narzędzi deweloperskich, aby sprawdzać, debugować i modyfikować stronę w zwykły sposób. Jeśli używasz interfejsu bez interfejsu graficznego, ta strona jest też zaawansowanym narzędziem do debugowania, w którym zobaczysz wszystkie nieprzetworzone polecenia protokołu DevNarzędzia przechodzące przez przewód, komunikujące się z przeglądarką.
Korzystanie z automatyzacji (węzeł)
Animator
Puppeteer to biblioteka Node opracowana przez zespół Chrome. Udostępnia ogólny interfejs API do sterowania Chrome bez interfejsu graficznego (lub z pełną funkcjonalnością). Jest on podobny do innych bibliotek testowania automatycznego, takich jak PhantomJS i NightmareJS, ale działa tylko w najnowszych wersjach Chrome.
Puppeteer można m.in. używać do łatwego robienia zrzutów ekranu, tworzenia plików PDF, poruszania się po stronach i pobierania informacji o tych stronach. Jeśli chcesz szybko zautomatyzować testowanie przeglądarek, zalecam użycie biblioteki. Ukrywa ona złożoność protokołu DevTools i zajmuje się zbędnymi zadaniami, takimi jak uruchamianie instancji debugowania Chrome.
Zainstaluj:
npm i --save puppeteer
Przykład: wydrukowanie klienta użytkownika
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch();
console.log(await browser.version());
await browser.close();
})();
Przykład – robienie zrzutu ekranu strony
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();
})();
Aby dowiedzieć się więcej o pełnym interfejsie API, zapoznaj się z dokumentacją Puppeteer.
Biblioteka CRI
chrome-remote-interface to biblioteka niskiego poziomu w porównaniu z interfejsem API Puppeteer. Zalecam to, jeśli chcesz mieć bezpośredni dostęp do źródeł i używać protokołu DevTools.
Uruchamianie Chrome
chrome-remote-interface nie uruchamia Chrome za Ciebie, więc musisz to zrobić samodzielnie.
W sekcji interfejsu wiersza poleceń uruchomiliśmy Chrome ręcznie za pomocą --headless --remote-debugging-port=9222
. Aby jednak w pełni zautomatyzować testy, prawdopodobnie będziesz musiał utworzyć proces Chrome w ramach aplikacji.
Jednym ze sposobów jest użycie 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) => {
...
});
Jednak jeśli potrzebujesz przenośnego rozwiązania działającego na wielu platformach, będziesz mieć problemy. Spójrzmy tylko na zakodowaną na stałe ścieżkę do Chrome :(
Korzystanie z ChromeLauncher
Lighthouse to wspaniałe narzędzie do testowania jakości aplikacji internetowych. W Lighthouse został opracowany niezawodny moduł uruchamiania Chrome, który teraz został wyodrębniony do samodzielnego użycia.
Moduł NPM chrome-launcher
znajdzie miejsce instalacji Chrome, skonfiguruje instancję debugowania, uruchomi przeglądarkę i zamknie ją, gdy program zakończy działanie. Najlepsze jest to, że dzięki Node działa na wielu platformach.
Domyślnie chrome-launcher
spróbuje uruchomić Chrome Canary (jeśli jest zainstalowany), ale możesz ręcznie wybrać wersję Chrome, której ma użyć. Aby użyć tej funkcji, najpierw zainstaluj ją z npm:
npm i --save chrome-launcher
Przykład: uruchamianie trybu bez interfejsu graficznego za pomocą 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();
});
Uruchomienie tego skryptu nie powoduje żadnych efektów, ale w Menedżerze zadań powinieneś zobaczyć instancję Chrome, która wczytała about:blank
. Pamiętaj, że nie będzie interfejsu przeglądarki. Nie mamy głowy.
Aby sterować przeglądarką, potrzebujemy protokołu DevTools.
Pobieranie informacji o stronie
Zainstaluj bibliotekę:
npm i --save chrome-remote-interface
Przykłady
Przykład: wydrukowanie klienta użytkownika
const CDP = require('chrome-remote-interface');
...
launchChrome().then(async chrome => {
const version = await CDP.Version({port: chrome.port});
console.log(version['User-Agent']);
});
Wyniki będą wyglądać mniej więcej tak: HeadlessChrome/60.0.3082.0
Przykład: sprawdź, czy witryna zawiera manifest aplikacji internetowej.
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.
});
})();
Przykład – wyodrębnij <title>
strony za pomocą interfejsów 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.
});
})();
Korzystanie z Selenium, WebDriver i ChromeDriver
Obecnie Selenium otwiera pełną instancję Chrome. Inaczej mówiąc, jest to rozwiązanie automatyczne, ale nie całkowicie pozbawione interfejsu. Jednak przy odrobinie pracy można skonfigurować Selenium do uruchamiania Chrome bez interfejsu. Jeśli potrzebujesz szczegółowych instrukcji konfiguracji, zalecamy uruchomienie Selenium w Chrome bez interfejsu graficznego. Na początek skorzystam z poniższych przykładów.
Korzystanie z ChromeDriver
ChromeDriver 2.32 używa Chrome 61 i działa dobrze z Chrome bez interfejsu graficznego.
Instalacja:
npm i --save-dev selenium-webdriver chromedriver
Przykład:
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();
Korzystanie z WebDriverIO
WebDriverIO to interfejs API wyższego poziomu będący alternatywą dla Selenium WebDriver.
Instalacja:
npm i --save-dev webdriverio chromedriver
Przykład: filtrowanie funkcji CSS na stronie 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();
})();
Dodatkowe zasoby
Oto kilka przydatnych zasobów na początek:
Dokumenty
- Wyświetlający interfejs DevTools Protocol – dokumentacja interfejsu API
Narzędzia
- chrome-remote-interface – moduł węzła opakowujący protokół Narzędzi deweloperskich
- Lighthouse – automatyczne narzędzie do testowania jakości aplikacji internetowych, które intensywnie korzysta z protokołu.
- chrome-launcher – moduł węzła do uruchamiania Chrome, gotowy do automatyzacji.
Prezentacje
- „The Headless Web” – świetny wpis na blogu Paula Kinlana na temat korzystania z Headless z api.ai.
Najczęstsze pytania
Czy potrzebuję flagi --disable-gpu
?
Tylko w systemie Windows. Na innych platformach nie jest on już wymagany. Flaga --disable-gpu
to tymczasowe obejście kilku błędów. W przyszłych wersjach Chrome nie będziesz już potrzebować tego parametru. Więcej informacji znajdziesz na stronie crbug.com/737678.
Czy nadal muszę używać Xvfb?
Nie. Chrome bez interfejsu graficznego nie używa okna, więc serwer wyświetlania taki jak Xvfb nie jest już potrzebny. Możesz bez problemu przeprowadzać testy automatyczne bez tego.
Co to jest Xvfb? Xvfb to serwer wyświetlacza w pamięci dla systemów typu Unix, który umożliwia uruchamianie aplikacji graficznych (np. Chrome) bez podłączonego wyświetlacza fizycznego. Wiele osób używa Xvfb do uruchamiania starszych wersji Chrome w celu przeprowadzenia testów bez wyświetlania interfejsu.
Jak utworzyć kontener Dockera, w którym działa Chrome bez interfejsu graficznego?
Sprawdź lighthouse-ci. Zawiera przykładowy plik Dockerfile, który używa node:8-slim
jako obrazu podstawowego, instaluje i uruchamia Lighthouse w App Engine Flex.
Czy mogę używać tej funkcji z Selenium, WebDriver lub ChromeDriver?
Tak. Zobacz Using Selenium, WebDriver i ChromeDriver.
Jak to się ma do PhantomJS?
Tryb bez głowy w Chrome jest podobny do narzędzi takich jak PhantomJS. Oba te rozwiązania można stosować do testowania automatycznego w sytuacji, gdy środowisko jest bez głowicy. Główna różnica między nimi polega na tym, że Phantom używa starszej wersji WebKit jako silnika do renderowania, a Chrome bez interfejsu użytkownika korzysta z najnowszej wersji Blink.
Obecnie Phantom udostępnia też interfejs API wyższego poziomu niż protokół Narzędzia deweloperskie.
Gdzie zgłaszam błędy?
Błędy w Chrome bez interfejsu graficznego zgłaszaj na stronie crbug.com.
Błędy w protokole DevTools zgłaszaj na stronie github.com/ChromeDevTools/devtools-protocol.