前端工程師的一大神器——Puppeteer
本文主要講述一下Google出版并一直在不斷維護的神器puppeteer,通過學(xué)習(xí)本文你將了解其基本使用和常用功能。
一、Puppeteer簡介
Puppeteer 是一個 Node 庫,它提供了一個高級 API 來通過 DevTools 協(xié)議控制 Chromium 或 Chrome,利用Puppeteer可以獲取頁面DOM節(jié)點、網(wǎng)絡(luò)請求和響應(yīng)、程序化操作頁面行為、進行頁面的性能監(jiān)控和優(yōu)化、獲取頁面截圖和PDF等,利用該神器就可以操作Chrome瀏覽器玩出各種花樣。
二、Puppeteer核心組成結(jié)構(gòu)
Puppeteer的結(jié)構(gòu)也反映了瀏覽器的結(jié)構(gòu),其核心結(jié)構(gòu)如下所示:
- Browser:這是一個瀏覽器實例,可以擁有瀏覽器上下文,可通過 puppeteer.launch 或 puppeteer.connect 創(chuàng)建一個 Browser 對象。
 - BrowserContext:該實例定義了一個瀏覽器上下文,可擁有多個頁面,創(chuàng)建瀏覽器實例時默認(rèn)會創(chuàng)建一個瀏覽器上下文(不能關(guān)閉),此外可以利用 browser.createIncognitoBrowserContext()創(chuàng)建一個匿名的瀏覽器上下文(不會與其它瀏覽器上下文共享cookie/cache).
 - Page:至少包含一個主框架,除了主框架外還有可能存在其它框架,例如iframe。
 - Frame:頁面中的框架,在每個時間點,頁面通過page.mainFrame()和frame.childFrames()方法暴露當(dāng)前框架的細節(jié)。對于該框架中至少有一個執(zhí)行上下文
 - ExecutionCOntext:表示一個JavaScript的執(zhí)行上下文。
 - Worker:具有單個執(zhí)行上下文,便于與 WebWorkers 交互。
 
三、基本使用和常用功能
該神器整體使用起來比較簡單,下面就開始我們的使用之路。
3.1 啟動Browser
核心函數(shù)就是異步調(diào)用puppeteer.launch()函數(shù),根據(jù)相應(yīng)的配置參數(shù)創(chuàng)建一個Browser實例。
- const path = require('path');
 - const puppeteer = require('puppeteer');
 - const chromiumPath = path.join(__dirname, '../', 'chromium/chromium/chrome.exe');
 - async function main() {
 - // 啟動chrome瀏覽器
 - const browser = await puppeteer.launch({
 - // 指定該瀏覽器的路徑
 - executablePath: chromiumPath,
 - // 是否為無頭瀏覽器模式,默認(rèn)為無頭瀏覽器模式
 - headless: false
 - });
 - }
 - main();
 
3.2 訪問頁面
訪問頁面首先需要創(chuàng)建一個瀏覽器上下文,然后基于該上下文創(chuàng)建一個新的page,最后指定要訪問的網(wǎng)址。
- async function main() {
 - // 啟動chrome瀏覽器
 - // ……
 - // 在一個默認(rèn)的瀏覽器上下文中被創(chuàng)建一個新頁面
 - const page1 = await browser.newPage();
 - // 空白頁訪問該指定網(wǎng)址
 - await page1.goto('https://51yangsheng.com');
 - // 創(chuàng)建一個匿名的瀏覽器上下文
 - const browserContext = await browser.createIncognitoBrowserContext();
 - // 在該上下文中創(chuàng)建一個新頁面
 - const page2 = await browserContext.newPage();
 - page2.goto('https://www.baidu.com');
 - }
 - main();
 
3.3 設(shè)備模擬
經(jīng)常需要不同類型的機型的瀏覽結(jié)果,此時就可以采用設(shè)備模擬實現(xiàn),下面模擬一個iPhone X的設(shè)備的瀏覽器結(jié)果
- async function main() {
 - // 啟動瀏覽器
 - // 設(shè)備模擬:模擬一個iPhone X
 - // user agent
 - await page1.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1')
 - // 視口(viewport)模擬
 - await page1.setViewport({
 - width: 375,
 - height: 812
 - });
 - // 訪問某頁面
 - }
 - main();
 
3.4 獲取DOM節(jié)點
獲取DOM節(jié)點有兩種方式,一種方式是直接調(diào)用page所帶的原生函數(shù),另一種是通過執(zhí)行js代碼獲取。
- async function main() {
 - // 啟動chrome瀏覽器
 - const browser = await puppeteer.launch({
 - // 指定該瀏覽器的路徑
 - executablePath: chromiumPath,
 - // 是否為無頭瀏覽器模式,默認(rèn)為無頭瀏覽器模式
 - headless: false
 - });
 - // 在一個默認(rèn)的瀏覽器上下文中被創(chuàng)建一個新頁面
 - const page1 = await browser.newPage();
 - // 空白頁訪問該指定網(wǎng)址
 - await page1.goto('https://www.baidu.com');
 - // 等待title節(jié)點出現(xiàn)
 - await page1.waitForSelector('title');
 - // 用page自帶的方法獲取節(jié)點
 - const titleDomText1 = await page1.$eval('title', el => el.innerText);
 - console.log(titleDomText1);// 百度一下
 - // 用js獲取節(jié)點
 - const titleDomText2 = await page1.evaluate(() => {
 - const titleDom = document.querySelector('title');
 - return titleDom.innerText;
 - });
 - console.log(titleDomText2);
 - }
 - main();
 
3.5 監(jiān)聽請求和響應(yīng)
下面就來監(jiān)聽一下百度中某一js腳本的請求和響應(yīng),request事件是監(jiān)聽請求,response事件是監(jiān)聽響應(yīng)。
- async function main() {
 - // 啟動chrome瀏覽器
 - const browser = await puppeteer.launch({
 - // 指定該瀏覽器的路徑
 - executablePath: chromiumPath,
 - // 是否為無頭瀏覽器模式,默認(rèn)為無頭瀏覽器模式
 - headless: false
 - });
 - // 在一個默認(rèn)的瀏覽器上下文中被創(chuàng)建一個新頁面
 - const page1 = await browser.newPage();
 - page1.on('request', request => {
 - if (request.url() === 'https://s.bdstatic.com/common/openjs/amd/eslx.js') {
 - console.log(request.resourceType());
 - console.log(request.method());
 - console.log(request.headers());
 - }
 - });
 - page1.on('response', response => {
 - if (response.url() === 'https://s.bdstatic.com/common/openjs/amd/eslx.js') {
 - console.log(response.status());
 - console.log(response.headers());
 - }
 - })
 - // 空白頁剛問該指定網(wǎng)址
 - await page1.goto('https://www.baidu.com');
 - }
 - main();
 
3.6 攔截某一請求
默認(rèn)情況下request事件只有只讀屬性,不能夠攔截請求,若想攔截該請求則需要通過page.setRequestInterception(value)啟動請求攔截器,然后利用request.abort, request.continue 和 request.respond 方法決定該請求的下一步操作。
- async function main() {
 - // 啟動chrome瀏覽器
 - const browser = await puppeteer.launch({
 - // 指定該瀏覽器的路徑
 - executablePath: chromiumPath,
 - // 是否為無頭瀏覽器模式,默認(rèn)為無頭瀏覽器模式
 - headless: false
 - });
 - // 在一個默認(rèn)的瀏覽器上下文中被創(chuàng)建一個新頁面
 - const page1 = await browser.newPage();
 - // 攔截請求開啟
 - await page1.setRequestInterception(true);// true開啟,false關(guān)閉
 - page1.on('request', request => {
 - if (request.url() === 'https://s.bdstatic.com/common/openjs/amd/eslx.js') {
 - // 終止該請求
 - request.abort();
 - console.log('該請求被終止?。?!');
 - }
 - else {
 - // 繼續(xù)該請求
 - request.continue();
 - }
 - });
 - // 空白頁訪問該指定網(wǎng)址
 - await page1.goto('https://www.baidu.com');
 - }
 - main();
 
3.7 截圖
截圖是一個很有用的功能,通過截取就可以保存一份快照,方便后期問題的排查。(注:在無頭模式下進行截圖,否則截的圖可能有問題)
- async function main() {
 - // 啟動瀏覽器,訪問頁面的操作
 - // 截屏操作,使用Page.screenshot函數(shù)
 - // 截取整個頁面:Page.screenshot函數(shù)默認(rèn)截取整個頁面,加上fullPage參數(shù)就是全屏截取
 - await page1.screenshot({
 - path: '../imgs/fullScreen.png',
 - fullPage: true
 - });
 - // 截取屏幕中一個區(qū)域的內(nèi)容
 - await page1.screenshot({
 - path: '../imgs/partScreen.jpg',
 - type: 'jpeg',
 - quality: 80,
 - clip: {
 - x: 0,
 - y: 0,
 - width: 375,
 - height: 300
 - }
 - });
 - browser.close();
 - }
 - main();
 
3.8 生成pdf
除了利用截圖保留快照外,還可以使用pdf保留快照。
- async function main() {
 - // 啟動瀏覽器,訪問頁面的操作
 - // 根據(jù)網(wǎng)頁內(nèi)容生成pdf文件,使用Page.pdf——注意:必須在無頭模式下才可以調(diào)用
 - await page1.pdf({
 - path: '../pdf/baidu.pdf'
 - });
 - browser.close();
 - }
 - main();
 
本文轉(zhuǎn)載自微信公眾號「執(zhí)鳶者」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系執(zhí)鳶者公眾號。

















 
 
 





 
 
 
 