Puppeteer介紹
從Node.js中以程式化方式控制Chrome。
Puppeteer是一個我們可以使用的Node庫,用於控制一個無界面Chrome實例。我們基本上是使用Chrome,但以JavaScript進行程式化操作。
使用它,我們可以:
爬取網頁
自動化表單提交
執行任何種類的瀏覽器自動化
追蹤頁面加載性能
創建使用服務器端渲染的單頁應用程序的版本
生成網頁的截圖
創建自動化測試
從網頁生成PDF文件
它是由Google開發的。它本身並不會解鎖任何新功能,但它將我們需要處理的許多細節抽象出來,而不使用它。
簡而言之,它讓事情變得非常簡單。
由於它在初始化時啟動一個新的Chrome實例,它可能不是最高效的。然而,它是使用實際瀏覽器(在幕後)進行自動測試的最準確的方法。
準確地說,它使用的是Chromium,也就是Chrome的開源部分。這意味著您沒有Google許可的專有編解碼器(MP3,AAC,H.264等),也沒有與Google服務(如崩潰報告,Google更新等)的集成,但從編程的角度來看,它應該與Chrome完全相似(除了媒體播放,如前所述)。
安裝Puppeteer
首先要使用以下命令安裝:
1 | npm install puppeteer |
這將下載並打包最新版本的Chromium。
您也可以選擇安裝puppeteer-core
,讓puppeteer運行您已安裝的本地Chrome。在某些特殊情況下這很有用(參見[puppeteer vs puppeteer-core](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteer-vs-puppeteer-core))。通常,您只需選擇`puppeteer`。
使用Puppeteer
在一個Node.js文件中,引入它:
1 | const puppeteer = require('puppeteer'); |
然後,我們可以使用launch()
方法創建一個瀏覽器實例:
1 | (async () => { |
我們也可以這樣寫:
1 | puppeteer.launch().then(async browser => { |
您可以將選項對像傳遞給puppeteer.launch()
。最常見的選項是:
1 | puppeteer.launch({ headless:false }) |
以在Puppeteer執行操作時顯示Chrome。這樣可以方便地查看正在發生的事情並進行調試。
我們使用await
,所以我們必須將此方法調用包裹在一個異步函數中,我們會立即調用它(/ javascript-iife /)。
接下來,我們可以使用browser
對像上的newPage()
方法獲取page
對象:
1 | (async () => { |
隨後,我們調用page
對象上的goto()
方法來加載該頁面:
1 | (async () => { |
我們也可以使用promise而不是async / await,但使用後者會使代碼讀起來更加清晰:
1 | (() => { |
獲取頁面內容
在頁面加載了URL後,我們可以使用page
的evaluate()
方法來獲取頁面的內容:
1 | (async () => { |
此方法接受一個回調函數,我們可以在其中添加所需的代碼來檢索頁面上需要的元素。然後,我們返回一個新對象,這將成為我們的evaluate()
方法調用的結果。
我們可以使用page.$()
方法來訪問文檔上的選擇器API方法querySelector()
,並使用page.$$()
作為querySelectorAll()
的別名。
完成計算後,我們在browser
上調用close()
方法:
1 | browser.close() |
頁面方法
我們在上面看到了從調用browser.newPage()
獲得的page
對象,並在其中調用了goto()
和evaluate()
方法。
所有方法都返回一個promise,因此它們通常以await
闕字開頭。
讓我們查看我們將調用的一些最常見的方法。您可以在Puppeteer文檔中查看完整列表。
page.$()
可以在頁面上使用選擇器API方法querySelector()
page.$$()
可以在頁面上使用選擇器API方法querySelectorAll()
page.$eval()
接受2個或多個參數。第一個是選擇器,第二個是函數。如果有更多參數,則將其作為附加參數傳遞給函數。
它在頁面上運行querySelectorAll()
,使用第一個參數作為選擇器,然後將該參數用作函數的第一個參數。
1 | const innerTextOfButton = await page.$eval('button#submit', el => el.innerText) |
click()
對參數中傳遞的元素執行鼠標單擊事件
1 | await page.click('button#submit') |
我們可以傳遞一個帶有選項對象的附加參數:
button
可以設置為left
(默認值),right
或middle
clickCount
是一個數字,默認為1,設置了元素應該被點擊的次數delay
是點擊之間的毫秒數。默認是0
content()
獲取頁面的HTML源代碼
1 | const source = await page.content() |
emulate()
模擬設備。它將用戶代理設置為特定設備,並相應地設置視圖。
支持的設備列表可在此文件中找到: DeviceDescriptors.js。
這是如何模擬iPhone X的方法:
iPhone X
1 | const puppeteer = require('puppeteer'); |
evaluate()
在頁面上下文中評估一個函數。在此函數內部,我們可以訪問document
對象,因此可以調用任何DOM API:
1 | const puppeteer = require('puppeteer'); |
我們在此處調用的內容是在頁面上下文中執行的,因此如果我們運行console.log()
,我們將無法在Node.js上下文中看到結果,因為它是在無界面瀏覽器中執行的。
我們可以在這裡計算值並返回JavaScript對象,但如果我們想從evaluate()返回DOM元素並在Node.js上下文中訪問它,則必須使用不同的方法,evaluateHandle()
。如果我們從evaluate()返回DOM元素,我們只會得到一個空對象。
evaluateHandle()
與evaluate()類似,但如果我們返回DOM元素,則會得到正確的對象,而不是空對象:
1 | const puppeteer = require('puppeteer'); |
exposeFunction()
此方法允許您在瀏覽器上下文中添加一個新的函數,該函數在Node.js上下文中執行。
這意味著我們可以在瀏覽器中添加一個函數,在其中運行Node.js代碼。
此示例在瀏覽器上下文中添加一個名為test()的函數,該函數從文件系統中讀取與腳本相對路徑的“app.js”文件:
1 | const puppeteer = require('puppeteer'); |
focus()
將焦點放在作為參數傳遞的選擇器上
1 | await page.focus('input#name') |
goBack()
在頁面導航歷史記錄中返回
1 | await page.goBack() |
goForward()
在頁面導航歷史記錄中前進
1 | await page.goForward() |
goto()
打開一個新頁面。
1 | await page.goto('https://flaviocopes.com') |
您可以傳遞一個對象作為第二個參數,帶有選項。如果傳遞了waitUntil
選項,並將其設置為networkidle2
值,則將等待到導航完成:
1 | await page.goto('https://flaviocopes.com', {waitUntil: 'networkidle2'}) |
hover()
在作為參數傳遞的選擇器上進行鼠標懸停操作
1 | await page.hover('input#name') |
pdf()
從頁面生成PDF。您可以:
1 | await page.pdf({ path: 'file.pdf }) |
您可以將許多選項傳遞給此方法,以設置所生成PDF的詳細信息。查看官方文檔。
reload()
重新加載頁面
1 | await page.reload() |
screenshot()
對頁面進行PNG截圖,並將其保存到使用path
選中的文件名中。
1 | await page.screenshot({path: 'screenshot.png'}) |
select()
選擇由參數標識的DOM元素
1 | await page.select('input#name') |
setContent()
您可以設置頁面的內容,而不是打開現有的網頁。
可用於使用現有的HTML程序生成PDF或截圖:
1 | const html = '<h1>Hello!</h1>' |
setViewPort()
默認情況下,視圖口的大小為800x600像素。如果您想要不同的視圖口,也許是為了截圖,則調用setViewport
,並傳遞一個具有width
和height
屬性的對象。
1 | await page.setViewport({ width: 1280, height: 800 }) |
title()
獲取頁面標題
1 | await page.title() |
type()
在標識表單元素的選擇器中輸入文字
1 | await page.type('input#name', 'Flavio') |
delay
選項允許模擬現實世界中的打字,每個字符之間添加延遲:
1 | await page.type('input#name', 'Flavio', {delay: 100}) |
url()
獲取頁面URL
1 | await page.url() |
viewport()
獲取頁面視圖口
1 | await page.viewport() |
waitFor()
等待某些特定的事情發生。有以下快捷函數:
waitForFunction
waitForNavigation
waitForRequest
waitForResponse
waitForSelector
waitForXPath
示例:
1 | await page.waitFor(waitForNameToBeFilled) |
頁面命名空間
一個頁面對象提供了訪問多個不同對象的方式:
這些中的每一個都提供了許多新的功能。
keyboard
和mouse
在嘗試自動化事務時最有可能使用。
例如,如何觸發對元素的輸入(應事先選擇):
1 | await page.keyboard.type('hello!') |
鍵盤的其他方法是
keyboard.down()
發送keydown事件keyboard.press()
發送keydown和keyup(模擬正常鍵輸入)。主要用於修飾鍵(Shift,Ctrl,Cmd)keyboard.sendCharacter()
發送keypress事件keyboard.type()
發送keydown,keypress和keyup事件keyboard.up()
發送keyup事件
所有這些都使用鍵盤鍵碼,該碼在美國鍵盤布局文件中定義為: https://github.com/GoogleChrome/puppeteer/blob/master/lib/USKeyboardLayout.js。常規字符和數字保持原樣,而特殊鍵有一個特殊的代碼來定義它們。
mouse
提供4種方法:
mouse.click()
模擬單擊:mousedown和mouseup事件mouse.down()
模擬mousedown事件mouse.move()
移動到不同的坐標mouse.up()
模擬mouseup事件