用Python驱动Headless Chrome
Headless Browser(無頭的瀏覽器)是什么鬼?
簡而言之,Headless Browser是沒有圖形用戶界面(GUI)的web瀏覽器,通常是通過編程或命令行界面來控制的。
Headless Browser的許多用處之一是自動化可用性測試或測試瀏覽器交互。如果您正在嘗試檢查頁面在不同的瀏覽器中呈現的方式,或者確認頁面元素在用戶啟動某個工作流之后出現,那么使用Headless Browser可以提供大量的幫助。除此之外,如果內容是動態呈現的(比如通過Javascript),web抓取等傳統的面向web的任務就很難做了。使用Headless Browser可以方便地訪問這些內容,因為內容的呈現方式與完全瀏覽器中的內容完全相同。
基于不同的瀏覽器,有不同的瀏覽器引擎。(http://www.cnblogs.com/wangjunqiao/p/5212561.html)
主流瀏覽器所使用的內核分類
Trident內核:IE,MaxThon,TT,The World,360,搜狗瀏覽器等
 Gecko內核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等
 Presto內核:Opera7及以上
 Webkit內核:Safari,Chrome等
先讓我們看看瀏覽器處理過程中的每一個步驟:
1.處理HTML腳本,生成DOM樹
 2.處理CSS腳本,生成CSSOM樹 (DOM和CSSOM是獨立的數據結構)
 3.將DOM樹和CSSOM樹合并為渲染樹
 4.對渲染樹中的內容進行布局,計算每個節點的幾何外觀
 5.將渲染樹中的每個節點繪制到屏幕中
Headless Browser實際就是節約了第4,5步的時間。
3年前,無頭瀏覽器 PhantomJS 已經如火如荼出現了,緊跟著 NightmareJS 也成為一名巨星。無頭瀏覽器帶來巨大便利性:頁面爬蟲、自動化測試、WebAutomation...用過PhantomJS的都知道,它的環境是運行在一個封閉的沙盒里面,在環境內外完全不可通信,包括API、變量、全局方法調用等。
Headless Chrome和Python
 在發布Headless Chrome之前,當你需要自動化瀏覽器的時候隨時都有可能涉及多個窗口或標簽,你必須擔心CPU和/或內存的使用。這兩種方式都與必須從被請求的URL中顯示顯示的圖形的瀏覽器相關聯。
當使用一個無頭的瀏覽器時,我們不用擔心這個。因此,我們可以預期我們編寫的腳本的內存開銷會降低,執行速度也會更快。
 而Chrome從59版本開始 推出了 headless mode(當時僅支持Mac和Linux),而目前最新的Chrome63版已經開始在windows上支持headless mode。
安裝Headless Chrome 在windows
 Selenium操作chrome瀏覽器需要有ChromeDriver驅動來協助。
 什么是ChromeDriver?
ChromeDriver是Chromium team開發維護的,它是實現WebDriver有線協議的一個單獨的服務。ChromeDriver通過chrome的自動代理框架控制瀏覽器,建議從以下地址直接下載最新的版本:ChromeDriver 2.34
 它才可以支持Chrome v61-63。
 可以將此driver放置于:C:\Program Files\Google\Chrome\Application\ (對應的Chrome安裝目錄下)
安裝Selenium 在windows
 cmd命令里面運行:
 $pip install selenium
編寫對應的腳本
 編寫一個對應的百度搜索的腳本
?
以上的腳本運行完成后,你會在你的當前目錄看到一個類似于下面畫面的screen.png.
?
?
可以看出上面的寫法和直接使用Selenium調用Chrome瀏覽器的時候極其類似,只是多添加了對chrome_options的重寫。
據運行的試驗表明,Headelss 的確比Headed的瀏覽器在內存消耗,運行時間,CPU占用上面都有一定的優勢。
headless對比.png
使用Headless Chrome也許能讓你的自動化測試運行更快,而且在視覺測試上面也有一定的優勢。感興趣的朋友可以上手試試。
 鏈接:https://www.jianshu.com/p/11d519e2d0cb
 ?
?
?
Headless Chrome 入門
reflink:https://linux.cn/article-8850-1.html
在 Chrome 59 中開始搭載 Headless Chrome。這是一種在無需顯示headless的環境下運行 Chrome 瀏覽器的方式。從本質上來說,就是不用 chrome 瀏覽器來運行 Chrome 的功能!它將 Chromium 和 Blink 渲染引擎提供的所有現代 Web 平臺的功能都帶入了命令行。
?
它有什么用?
?
無需顯示headless的瀏覽器對于自動化測試和不需要可視化 UI 界面的服務器環境是一個很好的工具。例如,你可能需要對真實的網頁運行一些測試,創建一個 PDF,或者只是檢查瀏覽器如何呈現 URL。
?
注意: Mac 和 Linux 上的 Chrome 59 都可以運行無需顯示模式。對 Windows 的支持將在 Chrome 60 中提供。要檢查你使用的 Chrome 版本,請在瀏覽器中打開 chrome://version。
?
開啟無需顯示headless模式(命令行界面)
開啟無需顯示headless模式最簡單的方法是從命令行打開 Chrome 二進制文件。如果你已經安裝了 Chrome 59 以上的版本,請使用 --headless 標志啟動 Chrome:
chrome \--headless \? ? ? ? ? ? ? ? ? ?# Runs Chrome in headless mode.--disable-gpu \? ? ? ? ? ? ? ? # Temporarily needed for now.--remote-debugging-port=9222 \https://www.chromestatus.com? ?# URL to open. Defaults to about:blank.注意:目前你仍然需要使用 --disable-gpu 標志。但它最終會不需要的。
?
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 Cannary。
?
命令行的功能
在某些情況下,你可能不需要以腳本編程的方式操作 Headless Chrome。可以使用一些有用的命令行標志來執行常見的任務。
?
打印 DOM
--dump-dom 標志將打印 document.body.innerHTML 到標準輸出:
?
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 5xchrome --headless --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/使用 --screenshot 標志運行 Headless Chrome 將在當前工作目錄中生成一個名為 screenshot.png 的文件。如果你正在尋求整個頁面的截圖,那么會涉及到很多事情。來自 David Schnurr 的一篇很棒的博文已經介紹了這一內容。請查看 使用 headless Chrome 作為自動截屏工具。
?
REPL 模式 (read-eval-print loop)
--repl 標志可以使 Headless Chrome 運行在一個你可以使用瀏覽器評估 JS 表達式的模式下。執行下面的命令:
$ chrome --headless --disable-gpu --repl 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
當你使用 --remote-debugging-port=9222 運行 Chrome 時,它會啟動一個支持 DevTools 協議的實例。該協議用于與 Chrome 進行通信,并且驅動 Headless Chrome 瀏覽器實例。它也是一個類似 Sublime、VS Code 和 Node 的工具,可用于應用程序的遠程調試。#協同效應
?
由于你沒有瀏覽器用戶界面可用來查看網頁,請在另一個瀏覽器中輸入 http://localhost:9222,以檢查一切是否正常。你將會看到一個可檢查的inspectable頁面的列表,可以點擊它們來查看 Headless Chrome 正在呈現的內容:
?
DevTools 遠程調試界面
?
從這里,你就可以像往常一樣使用熟悉的 DevTools 來檢查、調試和調整頁面了。如果你以編程方式使用 Headless Chrome,這個頁面也是一個功能強大的調試工具,用于查看所有通過網絡與瀏覽器交互的原始 DevTools 協議命令。
?
使用編程模式 (Node)
Puppeteer 庫 API
Puppeteer 是一個由 Chrome 團隊開發的 Node 庫。它提供了一個高層次的 API 來控制無需顯示版(或 完全版)的 Chrome。它與其他自動化測試庫,如 Phantom 和 NightmareJS 相類似,但是只適用于最新版本的 Chrome。
?
除此之外,Puppeteer 還可用于輕松截取屏幕截圖,創建 PDF,頁面間導航以及獲取有關這些頁面的信息。如果你想快速地自動化進行瀏覽器測試,我建議使用該庫。它隱藏了 DevTools 協議的復雜性,并可以處理諸如啟動 Chrome 調試實例等繁冗的任務。
?
安裝:
?
yarn add puppeteer
例子 - 打印用戶代理:
?
const puppeteer = require('puppeteer');
(async() => {
? const browser = await puppeteer.launch();
? console.log(await browser.version());
? 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: 'networkidle'});
await page.pdf({path: 'page.pdf', format: 'A4'});
browser.close();
})();
查看 Puppeteer 的文檔,了解完整 API 的更多信息。
?
CRI 庫
chrome-remote-interface 是一個比 Puppeteer API 更低層次的庫。如果你想要更接近原始信息和更直接地使用 DevTools 協議的話,我推薦使用它。
?
啟動 Chrome
?
chrome-remote-interface 不會為你啟動 Chrome,所以你要自己啟動它。
?
在前面的 CLI 章節中,我們使用 --headless --remote-debugging-port=9222 手動啟動了 Chrome。但是,要想做到完全自動化測試,你可能希望從你的應用程序中啟動 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 是一個令人稱奇的網絡應用的質量測試工具。Lighthouse 內部開發了一個強大的用于啟動 Chrome 的模塊,現在已經被提取出來單獨使用。chrome-launcher NPM 模塊 可以找到 Chrome 的安裝位置,設置調試實例,啟動瀏覽器和在程序運行完之后將其殺死。它最好的一點是可以跨平臺工作,感謝 Node!
?
默認情況下,chrome-launcher 會嘗試啟動 Chrome Canary(如果已經安裝),但是你也可以更改它,手動選擇使用的 Chrome 版本。要想使用它,首先從 npm 安裝:
?
yarn add chrome-launcher
例子 - 使用 chrome-launcher 啟動 Headless Chrome:
?
const chromeLauncher = require('chrome-launcher');
// Optional: set logging level of launcher to see its output.
// Install it using: yarn add 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。記住,它不會有任何的瀏覽器界面,我們是無需顯示的。
?
為了控制瀏覽器,我們需要 DevTools 協議!
?
檢索有關頁面的信息
警告: DevTools 協議可以做一些有趣的事情,但是起初可能有點令人生畏。我建議先花點時間瀏覽 DevTools 協議查看器。然后,轉到 chrome-remote-interface 的 API 文檔,看看它是如何包裝原始協議的。
?
我們來安裝該庫:
?
yarn add 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']);
});
結果是類似這樣的東西:HeadlessChrome/60.0.3082.0。
?
例子 - 檢查網站是否有 Web 應用程序清單:
?
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. 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 提取頁面的 <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. 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 只需要進行小小的配置即可運行 Headless Chrome。如果你想要關于如何自己設置的完整說明,我建議你閱讀“使用 Headless Chrome 來運行 Selenium”,不過你可以從下面的一些示例開始。
?
使用 ChromeDriver
ChromeDriver 2.3.0 支持 Chrome 59 及更新版本,可與 Headless Chrome 配合使用。在某些情況下,你可能需要等到 Chrome 60 以解決 bug。例如,Chrome 59 中屏幕截圖已知存在問題。
?
安裝:
?
yarn add selenium-webdriver chromedriver
例子:
?
const fs = require('fs');
const webdriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');
// This should be the path to your Canary installation.
// I'm assuming Mac for the example.
const PATH_TO_CANARY = '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary';
const chromeCapabilities = webdriver.Capabilities.chrome();
chromeCapabilities.set('chromeOptions', {
? binary: PATH_TO_CANARY // Screenshots require Chrome 60\. Force Canary.
? '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 WebDrive 上構建的更高層次的 API。
?
安裝:
?
yarn add webdriverio chromedriver
例子:過濾 chromestatus.com 上的 CSS 功能:
?
const webdriverio = require('webdriverio');
const chromedriver = require('chromedriver');
// This should be the path to your Canary installation.
// I'm assuming Mac for the example.
const PATH_TO_CANARY = '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary';
const PORT = 9515;
chromedriver.start([
? '--url-base=wd/hub',
? `--port=${PORT}`,
? '--verbose'
]);
(async () => {
const opts = {
? port: PORT,
? desiredCapabilities: {
? ? browserName: 'chrome',
? ? chromeOptions: {
? ? ? binary: PATH_TO_CANARY // Screenshots require Chrome 60\. Force Canary.
? ? ? 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 Protocol Viewer - API 參考文檔
工具
?
chrome-remote-interface - 基于 DevTools 協議的 node 模塊
Lighthouse - 測試 Web 應用程序質量的自動化工具;大量使用了協議
chrome-launcher - 用于啟動 Chrome 的 node 模塊,可以自動化
樣例
?
"The Headless Web" - Paul Kinlan 發布的使用了 Headless 和 api.ai 的精彩博客
常見問題
我需要 --disable-gpu 標志嗎?
?
目前是需要的。--disable-gpu 標志在處理一些 bug 時是需要的。在未來版本的 Chrome 中就不需要了。查看 https://crbug.com/546953#c152 和 https://crbug.com/695212 獲取更多信息。
?
所以我仍然需要 Xvfb 嗎?
?
不。Headless Chrome 不使用窗口,所以不需要像 Xvfb 這樣的顯示服務器。沒有它你也可以愉快地運行你的自動化測試。
?
什么是 Xvfb?Xvfb 是一個用于類 Unix 系統的運行于內存之內的顯示服務器,可以讓你運行圖形應用程序(如 Chrome),而無需附加的物理顯示器。許多人使用 Xvfb 運行早期版本的 Chrome 進行 “headless” 測試。
?
如何創建一個運行 Headless Chrome 的 Docker 容器?
?
查看 lighthouse-ci。它有一個使用 Ubuntu 作為基礎鏡像的 Dockerfile 示例,并且在 App Engine Flexible 容器中安裝和運行了 Lighthouse。
?
我可以把它和 Selenium / WebDriver / ChromeDriver 一起使用嗎?
?
是的。查看 Using Selenium, WebDrive, or ChromeDriver。
?
它和 PhantomJS 有什么關系?
?
Headless Chrome 和 PhantomJS 是類似的工具。它們都可以用來在無需顯示的環境中進行自動化測試。兩者的主要不同在于 Phantom 使用了一個較老版本的 WebKit 作為它的渲染引擎,而 Headless Chrome 使用了最新版本的 Blink。
?
目前,Phantom 提供了比 DevTools protocol 更高層次的 API。
?
我在哪兒提交 bug?
?
對于 Headless Chrome 的 bug,請提交到 crbug.com。
?
對于 DevTools 協議的 bug,請提交到 github.com/ChromeDevTools/devtools-protocol。
編譯自:https://developers.google.com/web/updates/2017/04/headless-chrome作者: Eric Bidelman
 原創:LCTT?https://linux.cn/article-8850-1.html譯者: Firmy Yang
 鏈接:http://www.imooc.com/article/81012
?
selenium+headless chrome安裝使用
pip install selenium
因為phantomJS將停止維護,所以建議使用headless chrome
 ChromeDriver is a separate executable that WebDriver uses to control Chrome.
1、確保谷歌瀏覽器安裝在可以找到的位置(默認位置或自己指定的位置)。
 如果不是默認位置,則需要用下面的代碼來指定谷歌瀏覽器的安裝位置:
 ChromeOptions options = new ChromeOptions();
 options.setBinary("/path/to/other/chrome/binary");
2、下載你系統上所需要的ChromeDriver文件,windows所需下載地址為:
 https://chromedriver.storage.googleapis.com/index.html?path=2.35/
3、幫助WebDriver找到你下載的ChromeDriver文件:
 將ChromeDriver文件存放在PATH目錄下或
 或
 from selenium import webdriver
 driver = webdriver.Chrome('/path/to/chromedriver')
4、(可選)啟動和退出ChromeDriver server需要一些時間,所以提供了兩種方法來
 解決這個問題:
 1、使用ChromeDriverService
 2、作為一個服務器單獨啟動ChromeDriver server,然后用Remote WebDriver連接它。
from selenium import webdriver
 from selenium.webdriver.chrome.options import Options
 chrome_options = Options()
 chrome_options.add_argument('--headless')
 chrome_options.add_argument('--disable-gpu')
 driver = webdriver.Chrome(chrome_options=chrome_options)
 driver.get("https://www.baidu.com")
 print(driver.title)
 driver.quit()
參考鏈接:
 https://sites.google.com/a/chromium.org/chromedriver/home 介紹地址
 https://sites.google.com/a/chromium.org/chromedriver/getting-started 入門地址
?
?
puppeteer,新款headless chrome!
puppeteer
puppeteer是一種谷歌開發的Headless Chrome,因為puppeteer的出現,業內許多自動化測試庫停止維護,比如PhantomJS,Selenium IDE for Firefox 。
puppeteer是干啥用的?
官方給了一些功能:
- 頁面生成pdf
- 爬spa/ssr類的網站
- 自動提交表單,模擬用戶操作,ui測試等等
- 提供自動化測試環境
- 分析網頁性能問題,基于chrome timeline
其實對于這么一個瀏覽器,我們能做的還有很多,比如前端監控,定期查詢頁面異常。這種思想產生的page-monitor。主要的功能其實就是基于它是一個瀏覽器,它可以模擬用戶輸入。能做什么依賴你的想象。
用code介紹一下puppeteer
頁面生成pdf
const puppeteer = require('puppeteer');(async () => {const browser = await puppeteer.launch();const page = await browser.newPage();await page.goto('http://open.toutiao.com', {waitUntil: 'networkidle2'});await page.pdf({path: 'hn.pdf', format: 'A4'});await browser.close(); })();puppeteer是基于node v6.4.0,但是await/async的語法需要node v7.6.0以上才支持。
可以npm i puppeteer然后在命令行看一下效果。
 代碼都是api沒有什么可以講的。需要說的一點就是open.toutiao.com下面的文章內容都是異步接口請求,puppeteer是怎么獲取內容的?
page.goto的配置項waitUntil:networkidle2, 等待一直到500ms內的請求數不超過2個。其實不保證準確獲得內容,那把等待時間寫長一點就可以了。
await page.waitFor(2000);
調試
- puppeteer并不是只有headless模式,打開puppeteer的ui界面:puppeteer.launch({headless: false),再放慢puppeteer執行的動作puppeteer.launch({headless: false, slowMo: 250}),就可以輕松調試。
- ‘打call?’?page.on('console', msg => console.log('PAGE LOG:', msg.text()));?事件監聽輕松打出頁面的log。
爬蟲
這里爬一下頭條的新聞標題:
(async () => {const browser = await puppeteer.launch({headless: false, slowMo: 250});const page = (await browser.pages())[0];page.on('console', msg => console.log('PAGE LOG:', msg.text()));await page.goto('https://open.toutiao.com');await page.evaluate(() => console.log(`url is ${location.href}`));const newsTitle = await page.evaluate((sel) => {const $els = document.querySelectorAll(sel);return Array.from($els).map((v) => {console.log(v.innerText); // 會被page.on 'console' 監聽到return v.innerText})}, 'section h3');console.log(newsTitle) // 可以處理新聞標題。await page.screenshot({path: 'toutiao.png'}); // 屏幕快照await browser.close(); })();模擬用戶操作
這個功能用途挺多的,比如自動登陸,e2e測試,刷贊,搶票什么的,當然如果能跳過驗證碼的話。
github 登陸
模擬輸入用戶名和密碼。
await page.goto('https://github.com/login');await page.click('#login_field');await page.type('username');await page.click('#password');await page.type('password');await page.click('#login > form > div.auth-form-body.mt-3 > input.btn.btn-primary.btn-block');await page.waitForNavigation();puppetter提供了page.focus,page.click,page.type,page.$eval(獲取dom屬性)等等api,鼠標位置,按鍵按下,tap,頁面跳轉眾多用戶可操作的api,都可以通過程序來模擬。
對這種模擬登陸,puppeteer還貼心的提供了這種api - -!page.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
ui測試
之前分享過的testcafe,跟puppeteer的api非常像,testcafe是一個自動化測試框架,他與puppeteer不同的一點就是他集成了mocha斷言庫。
 puppeteer和testcafe都提供了一套自動化測試的環境。puppeteer做e2e的測試需要自己選一個斷言庫,不過無傷大雅。
請求攔截/模擬請求
puppeteer比testcafe好的一點就是支持請求攔截,記得當初用testcafe測試請求是否被發出用了很多黑科技,提過issue。。
const puppeteer = require('puppeteer');puppeteer.launch({headless: false, slowMo: 250}).then(async browser => {const page = await browser.newPage();await page.setRequestInterception(true);page.on('console', msg => console.log('PAGE LOG:', msg.text()));page.on('request', interceptedRequest => {if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))interceptedRequest.abort();elseinterceptedRequest.continue();});await page.goto('https://open.toutiao.com'); // await browser.close(); });提供了request,response事件,可以攔截請求,首先需要打開這個開關await page.setRequestInterception(true);。
 這里的例子就是停掉所有的png和jpg請求。
 攔截能做的東西有很多,比如一些爬蟲可以通過攔截請求捕獲一些數據,來處理一些東西。
修改環境
puppeteer可以通過page.setViewport,page.setUserAgent來修改訪問的環境。
await page.setViewport({width: 1920,height: 1080 });await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36');puppeteer/DeviceDescriptors還給我們封裝好了一些環境,比如:
const puppeteer = require('puppeteer'); const devices = require('puppeteer/DeviceDescriptors'); const iPhone = devices['iPhone 6'];puppeteer.launch().then(async browser => {const page = await browser.newPage();await page.emulate(iPhone); // emulate的配置有Viewport,UserAgent等等。之前的setUserAgent等方法是它的語法糖。await page.goto('https://www.google.com');// other actions...await browser.close(); });性能測試
可以生成一個trace.json的文件,供chrome控制臺解析,await page.metrics()還可以給出一些性能測試的數據。
const puppeteer = require('puppeteer'); (async () => {const browser = await puppeteer.launch();const page = await browser.newPage();await page.tracing.start({path: 'trace.json'})await page.goto('https://open.toutiao.com')await page.tracing.stop()const metrics = await page.metrics()console.log(metrics)await browser.close(); })(); // output { Timestamp: 27888.820538,Documents: 2,Frames: 1,JSEventListeners: 58,Nodes: 171,LayoutCount: 20,RecalcStyleCount: 26,LayoutDuration: 0.042335,RecalcStyleDuration: 0.010091,ScriptDuration: 0.124838,TaskDuration: 0.000039,JSHeapUsedSize: 6388448,JSHeapTotalSize: 10334208 }https://www.cnblogs.com/dh-dh/p/8490047.html
?
linux 安裝 Headless Chrome
https://chromedriver.storage.googleapis.com/index.html?path=2.34/
?http://chromedriver.storage.googleapis.com/index.html
?
?看到網上基本沒有最新的chromedriver與chrome的對應關系表,便興起整理了一份如下,希望對大家有用:
| v2.35 | v62-64 | 
| v2.34 | v61-63 | 
| v2.33 | v60-62 | 
| v2.32 | v59-61 | 
| v2.31 | v58-60 | 
| v2.30 | v58-60 | 
| v2.29 | v56-58 | 
| v2.28 | v55-57 | 
| v2.27 | v54-56 | 
| v2.26 | v53-55 | 
| v2.25 | v53-55 | 
| v2.24 | v52-54 | 
| v2.23 | v51-53 | 
| v2.22 | v49-52 | 
| v2.21 | v46-50 | 
| v2.20 | v43-48 | 
| v2.19 | v43-47 | 
| v2.18 | v43-46 | 
| v2.17 | v42-43 | 
| v2.13 | v42-45 | 
| v2.15 | v40-43 | 
| v2.14 | v39-42 | 
| v2.13 | v38-41 | 
| v2.12 | v36-40 | 
| v2.11 | v36-40 | 
| v2.10 | v33-36 | 
| v2.9 | v31-34 | 
| v2.8 | v30-33 | 
| v2.7 | v30-33 | 
| v2.6 | v29-32 | 
| v2.5 | v29-32 | 
| v2.4 | v29-32 | 
綜合上面的表格,我們下載chrome可以到這里選擇
https://dl.lancdn.com/landian/software/chrome/m/
?
chromedriver可以到這里下載
https://chromedriver.storage.googleapis.com/index.html
?
下面是一片chrome和phantomjs的文章
http://insights.thoughtworks.cn/phantomjs-and-chrome-headless/
附:
 所有chromedriver均可在下面鏈接中下載到:
http://chromedriver.storage.googleapis.com/index.html
現在有一點好的是出了按照chrome版本對應的driver,直接按照瀏覽器版本去找對應的driver(只對應大版本就行),不用再費心去對應了,大家可以嘗試一下:
 有些同學說下不了,到taobao下也是可以的:
http://npm.taobao.org/mirrors/chromedriver/
?
前言
 公司的爬蟲項目使用了selenium+phantomjs,這個做過爬蟲的應該都用過,但是缺點也很明顯,慢,占用資源等,本身還有很多小坑就不一一列舉了
后來無意中發現了headless,
 參考這篇文章:https://intoli.com/blog/making-chrome-headless-undetectable/
經過安裝測試,效果確實比phantomjs好很多,目前已有的資料都是使用nodejs,python相關的資料不多,只能等大佬們整理了。
這里記錄一下安裝的過程
安裝chrome
 需要V59以上版本
 下載地址:https://www.landiannews.com/archives/36966.html
下載driver
 selenium調用需要使用到
 下載地址:https://sites.google.com/a/chromium.org/chromedriver/downloads
安裝調試
 下載安裝chromedriver
 下載安裝chrome
 調試安裝結果
 新建一個.py文件
 原文鏈接:https://blog.csdn.net/goodzyw/article/details/77269875
?
?
phantomJs之殤,chrome-headless之生
技術雷達快訊:自2017年中以來,Chrome用戶可以選擇以headless模式運行瀏覽器。此功能非常適合運行前端瀏覽器測試,而無需在屏幕上顯示操作過程。在此之前,這主要是PhantomJS的領地,但Headless Chrome正在迅速取代這個由JavaScript驅動的WebKit方法。Headless Chrome瀏覽器的測試運行速度要快得多,而且行為上更像一個真正的瀏覽器,雖然我們的團隊發現它比PhantomJS使用更多的內存。有了這些優勢,用于前端測試的Headless Chrome很可能成為事實上的標準。
隨著Google在Chrome 59版本放出了headless模式,Ariya Hidayat決定放棄對Phantom.js的維護,這也標示著Phantom.js 統治fully functional headless browser的時代將被chrome-headless代替。
Headless Browser
也許很多人對無頭瀏覽器還是很陌生,我們先來看看維基百科的解釋:
A headless browser is a?web browser?without a?graphical user interface.
Headless browsers provide automated control of a web page in an environment similar to popular web browsers, but are executed via a?command-line interface?or using network communication.
對,就是沒有頁面的瀏覽器。多用于測試web、截圖、圖像對比、測試前端代碼、爬蟲(雖然很慢)、監控網站性能等。
為什么要使用headless測試?
headless broswer可以給測試帶來顯著好處:
headless browser有什么缺陷?
以phantomjs為例
那么Headless Chrome與上面提到fully functional headless browser又有什么不同呢?
什么是Headless Chrome?
Headless Chrome 是 Chrome 瀏覽器的無界面形態,可以在不打開瀏覽器的前提下,使用所有Chrome支持的特性,在命令行中運行你的腳本。相比于其他瀏覽器,Headless Chrome 能夠更加便捷的運行web自動化測試、編寫爬蟲、截取圖等功能。
有的人肯定會問:看起來它的作用和phantomjs沒什么具體的差別?
對,是的,Headless Chrome 發布就是來代替phantomjs。
我們憑什么換用Headless Chrome?
能帶給QA以及項目什么好處?
前端測試改進
以目前的項目來說,之前的前端單元測試以及組件測試是用karma在phantomjs運行的,非常不穩定,在遠端CI上運行時經常會莫名其妙的掛掉,也找不出來具體的原因,自從Headless Chrome推出后,我們將phantomjs切換成Headless Chrome,再也沒有出現過異常情況,切換也非常簡單,只需要把karma.conf.js文件中的配置改下就OK了。如下
customLaunchers: { myChrome: { base: 'ChromeHeadless', flags: ['--no-sandbox', '--disable-gpu', '--remote-debugging-port=9222'] } },browsers: ['myChrome'],UI功能測試改進
原因一,Chrome-headless能夠完全像真實瀏覽器一樣完成用戶所有操作,再也不用擔心跑測試時,瀏覽器受到干擾,造成測試失敗
原因二,之前如果我們像要在CI上運行UI自動化測試,非常麻煩。必須使用Xvfb幫助才能在無界面的Linux上 運行UI自動化測試。(Xvfb是一個實現了X11顯示服務協議的顯示服務器。 不同于其他顯示服務器,Xvfb在內存中執行所有的圖形操作,不需要借助任何顯示設備。)現在也只需要在webdriver啟動時,設置一下chrome option即可,以capybara為例:
Capybara.register_driver :selenium_chrome do |app| Capybara::Selenium::Driver.new(app, browser: :chrome,desired_capabilities: {"chromeOptions" => {"args" => [ "--incognito","--allow-running-insecure-content","--headless","--disable-gpu"]}}) end無縫切換,只需更改下配置,就可以提高運行速度與穩定性,何樂而不為。
Google終極大招
Google 最近放出了終極大招——Puppeteer(Puppeteer is a Node library which provides a high-level API to control?headless?Chrome over the?DevTools Protocol. It can also be configured to use full (non-headless) Chrome.)
類似于webdriver的高級別的api,去幫助我們通過DevTools協議控制無界面Chrome。
在puppteteer之前,我們要控制chrome headless需要使用chrome-remote-interface來實現,但是它比 Puppeteer API 更接近低層次實現,無論是閱讀還是編寫都要比puppteteer更復雜。也沒有具體的dom操作,尤其是我們要模擬一下click事件,input事件等,就顯得力不從心了。
我們用同樣2段代碼來對比一下2個庫的區別。
首先來看看 chrome-remote-interface
const chromeLauncher = require('chrome-launcher'); const CDP = require('chrome-remote-interface'); const fs = require('fs'); 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' : '' ] }); } (async function() {const chrome = await launchChrome();const protocol = await CDP({port: chrome.port});const {Page, Runtime} = protocol;await Promise.all([Page.enable(), Runtime.enable()]);Page.navigate({url: 'https://www.github.com/'});await Page.loadEventFired(console.log("start"));const {data} = await Page.captureScreenshot();fs.writeFileSync('example.png', Buffer.from(data, 'base64'));// Wait for window.onload before doing stuff. protocol.close();chrome.kill(); // Kill Chrome.再來看看 puppeteer
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://www.github.com'); await page.screenshot({path: 'example.png'}); await browser.close(); })();對,就是這么簡短明了,更接近自然語言。沒有callback,幾行代碼就能搞定我們所需的一切。
總結
目前Headless Chrome仍然存在一些問題,還需要不斷完善,我們應該擁抱變化,適應它,讓它給我們的工作帶來更多幫助。
總結
以上是生活随笔為你收集整理的用Python驱动Headless Chrome的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 进销存先进先出法是如何计算的?
- 下一篇: MC中使用鼠标的坐标值(xmouse/y
