大家好,我是若川。歡迎加我微信?ruochuan12,長期交流學習。今天推薦神器puppeteer,我猜有挺多人不知道。文章不長,看完有空也可以試玩。
我18年也寫過一篇puppeteer爬取生成pdf的文章,時間真快。前端使用puppeteer 爬蟲生成《React.js 小書》PDF并合并
點擊下方卡片關注我、加個星標,或者查看源碼等系列文章。學習源碼整體架構系列、年度總結、JS基礎系列
一、Puppeteer簡介
Puppeteer 是一個 Node 庫,它提供了一個高級 API 來通過 DevTools 協議控制 Chromium 或 Chrome,利用Puppeteer可以獲取頁面DOM節點、網絡請求和響應、程序化操作頁面行為、進行頁面的性能監控和優化、獲取頁面截圖和PDF等,利用該神器就可以操作Chrome瀏覽器玩出各種花樣。
二、Puppeteer核心組成結構
Puppeteer的結構也反映了瀏覽器的結構,其核心結構如下所示:
Browser:這是一個瀏覽器實例,可以擁有瀏覽器上下文,可通過 puppeteer.launch 或 puppeteer.connect 創建一個 Browser 對象。
BrowserContext:該實例定義了一個瀏覽器上下文,可擁有多個頁面,創建瀏覽器實例時默認會創建一個瀏覽器上下文(不能關閉),此外可以利用 browser.createIncognitoBrowserContext()創建一個匿名的瀏覽器上下文(不會與其它瀏覽器上下文共享cookie/cache).
Page:至少包含一個主框架,除了主框架外還有可能存在其它框架,例如iframe。
Frame:頁面中的框架,在每個時間點,頁面通過page.mainFrame()和frame.childFrames()方法暴露當前框架的細節。對于該框架中至少有一個執行上下文
ExecutionCOntext:表示一個JavaScript的執行上下文。
Worker:具有單個執行上下文,便于與 WebWorkers 交互。
三、基本使用和常用功能
該神器整體使用起來比較簡單,下面就開始我們的使用之路。
3.1 啟動Browser
核心函數就是異步調用puppeteer.launch()函數,根據相應的配置參數創建一個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,// 是否為無頭瀏覽器模式,默認為無頭瀏覽器模式headless: false});
}main();
3.2 訪問頁面
訪問頁面首先需要創建一個瀏覽器上下文,然后基于該上下文創建一個新的page,最后指定要訪問的網址。
async function main() {// 啟動chrome瀏覽器// ……// 在一個默認的瀏覽器上下文中被創建一個新頁面const page1 = await browser.newPage();// 空白頁訪問該指定網址await page1.goto('https://51yangsheng.com');// 創建一個匿名的瀏覽器上下文const browserContext = await browser.createIncognitoBrowserContext();// 在該上下文中創建一個新頁面const page2 = await browserContext.newPage();page2.goto('https://www.baidu.com');
}main();
3.3 設備模擬
經常需要不同類型的機型的瀏覽結果,此時就可以采用設備模擬實現,下面模擬一個iPhone X的設備的瀏覽器結果
async function main() {// 啟動瀏覽器// 設備模擬:模擬一個iPhone X// user agentawait 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節點
獲取DOM節點有兩種方式,一種方式是直接調用page所帶的原生函數,另一種是通過執行js代碼獲取。
async function main() {// 啟動chrome瀏覽器const browser = await puppeteer.launch({// 指定該瀏覽器的路徑executablePath: chromiumPath,// 是否為無頭瀏覽器模式,默認為無頭瀏覽器模式headless: false});// 在一個默認的瀏覽器上下文中被創建一個新頁面const page1 = await browser.newPage();// 空白頁訪問該指定網址await page1.goto('https://www.baidu.com');// 等待title節點出現await page1.waitForSelector('title');// 用page自帶的方法獲取節點const titleDomText1 = await page1.$eval('title', el => el.innerText);console.log(titleDomText1);// 百度一下// 用js獲取節點const titleDomText2 = await page1.evaluate(() => {const titleDom = document.querySelector('title');return titleDom.innerText;});console.log(titleDomText2);
}main();
3.5 監聽請求和響應
下面就來監聽一下百度中某一js腳本的請求和響應,request事件是監聽請求,response事件是監聽響應。
async function main() {// 啟動chrome瀏覽器const browser = await puppeteer.launch({// 指定該瀏覽器的路徑executablePath: chromiumPath,// 是否為無頭瀏覽器模式,默認為無頭瀏覽器模式headless: false});// 在一個默認的瀏覽器上下文中被創建一個新頁面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());}})// 空白頁剛問該指定網址await page1.goto('https://www.baidu.com');
}main();
3.6 攔截某一請求
默認情況下request事件只有只讀屬性,不能夠攔截請求,若想攔截該請求則需要通過page.setRequestInterception(value)啟動請求攔截器,然后利用request.abort, request.continue 和 request.respond 方法決定該請求的下一步操作。
async function main() {// 啟動chrome瀏覽器const browser = await puppeteer.launch({// 指定該瀏覽器的路徑executablePath: chromiumPath,// 是否為無頭瀏覽器模式,默認為無頭瀏覽器模式headless: false});// 在一個默認的瀏覽器上下文中被創建一個新頁面const page1 = await browser.newPage();// 攔截請求開啟await page1.setRequestInterception(true);// true開啟,false關閉page1.on('request', request => {if (request.url() === 'https://s.bdstatic.com/common/openjs/amd/eslx.js') {// 終止該請求request.abort();console.log('該請求被終止!!!');}else {// 繼續該請求request.continue();}});// 空白頁訪問該指定網址await page1.goto('https://www.baidu.com');
}main();
3.7 截圖
截圖是一個很有用的功能,通過截取就可以保存一份快照,方便后期問題的排查。(注:在無頭模式下進行截圖,否則截的圖可能有問題)
async function main() {// 啟動瀏覽器,訪問頁面的操作// 截屏操作,使用Page.screenshot函數// 截取整個頁面:Page.screenshot函數默認截取整個頁面,加上fullPage參數就是全屏截取await page1.screenshot({path: '../imgs/fullScreen.png',fullPage: true});// 截取屏幕中一個區域的內容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() {// 啟動瀏覽器,訪問頁面的操作// 根據網頁內容生成pdf文件,使用Page.pdf——注意:必須在無頭模式下才可以調用await page1.pdf({path: '../pdf/baidu.pdf'});browser.close();
}main();
最近組建了一個江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你進群。
·················?若川出品?·················
今日話題
一直漲粉很乏力,公眾號不寫原創,平時轉載文章,活下來艱難。想著破局,但平時工作又忙,寫原創文章艱難。只能暫時少接公眾號廣告了,能接廣告變現有時是更新的動力啊,以后的路難走啊。同時深知寫原創重要,但運營也很重要。原創、高質量和每天更新,這三點靠個人只能做到兩點。歡迎分享、收藏、點贊、在看我的公眾號文章~
一個愿景是幫助5年內前端人走向前列的公眾號
可加我個人微信 ruochuan12,長期交流學習
推薦閱讀
我在阿里招前端,我該怎么幫你?(現在還能加我進模擬面試群)
如何拿下阿里巴巴 P6 的前端 Offer
點擊上方卡片關注我、加個星標,或者查看源碼等系列文章。
學習源碼整體架構系列、年度總結、JS基礎系列