1. 程式人生 > >使用node+puppeteer+express搭建截圖服務

使用node+puppeteer+express搭建截圖服務

### 使用node+puppeteer+express搭建截圖服務 >轉載請註明出處[https://www.cnblogs.com/funnyzpc/p/14222807.html](https://www.cnblogs.com/funnyzpc/p/14222807.html) #### 寫在之前 一開始我們的需求是開啟報表的某個頁面然後把圖截出來,然後呼叫企業微信傳送給業務群 這中間我嘗試了多種技術,比如`html2image`,`pdf2image`、`selenium`這些,這其中截圖 比體驗較好的也就`selenium`了,不過我們有些頁面載入的時間較長,selenium似乎對html互操作性 也不是很完美(通過Thread.sleep並不能完美的相容絕大多數報表),另外還有一個比較要命的 是Chromium渲染出來的頁面似乎也有不同程度的問題(就是不好看),當然後面一個偶然的機會在 某不知名網站看到有網友用`puppeteer`來實現截圖,遂~,一通騷操作就搭了一套出來(雖然最終方案並不是這個 ,當然這是後話哈~),這裡就拿出來說說哈~ #### 準備 由於整個系統是基於node+express的web服務,puppeteer只是node的一個plugin,所以需要做的準備大致有下 + 一臺linux伺服器,這裡實用centos + node安裝包(用於搭建node環境) + 字型檔案 #### 安裝node環境 + `wget https://nodejs.org/dist/v14.15.3/node-v14.15.3-linux-x64.tar.xz` + `tar --strip-components 1 -xvJf node-v* -C /usr/local` + `npm config set registry https://registry.npm.taobao.org` #### 安裝pm2(用於守護node服務) 【注意:安裝pm2前必須安裝npm,如果只是非正式環境可以不用安裝pm2】 + `npm install pm2 -g` + 其它操作請見[https://pm2.keymetrics.io](https://pm2.keymetrics.io) #### 安裝字型 【這個其實很重要,我也繞了彎,原本以為改改字型編碼就可以了,後來發現不是】 + step1: 將window字型複製到linux下 - windows: C:\Windows\Fonts - Linux: /usr/share/fonts/ + step2: 建立字型索引資訊並更新字型快取 - cd /usr/share/fonts/ - mkfontscale - mkfontdir - fc-cache #### 準備程式碼 + index.js ``` // 引入express module // 引入puppeteer module const express = require('express'), app = express(), puppeteer = require('puppeteer'); // 函式::頁面載入監控 const waitTillHTMLRendered = async (page, timeout = 30000) => { const checkDurationMsecs = 1000; const maxChecks = timeout / checkDurationMsecs; let lastHTMLSize = 0; let checkCounts = 1; let countStableSizeIterations = 0; const minStableSizeIterations = 3; while(checkCounts++ <= maxChecks){ let html = await page.content(); let currentHTMLSize = html.length; let bodyHTMLSize = await page.evaluate(() => document.body.innerHTML.length); console.log('last: ', lastHTMLSize, ' <> curr: ', currentHTMLSize, " body html size: ", bodyHTMLSize); if(lastHTMLSize != 0 && currentHTMLSize == lastHTMLSize) countStableSizeIterations++; else countStableSizeIterations = 0; //reset the counter if(countStableSizeIterations >= minStableSizeIterations) { console.log("Page rendered fully.."); break; } lastHTMLSize = currentHTMLSize; await page.waitFor(checkDurationMsecs); } }; //建立一個 `/screenshot` 的route app.get("/screenshot", async (request, response) => { try { const browser = await puppeteer.launch({ args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.setViewport({ width:!request.query.width?1600:Number(request.query.width), height:!request.query.height?900:Number(request.query.height) }); // 這裡執行登入操作(非公共頁面需要登入) if(request.query.login && request.query.login=="true"){ // wait until page load await page.goto('認證(登入)地址', { waitUntil: 'networkidle0' }); await page.type('#username', '登入使用者名稱'); await page.type('#password', '登入密碼'); // click and wait for navigation await Promise.all([ page.click('#loginBtn'), page.waitForNavigation({ waitUntil: 'networkidle0' }), ]); } await page.goto(request.query.url,{'timeout': 12000, 'waitUntil':'load'}); await waitTillHTMLRendered(page); const image = await page.screenshot({fullPage : true,margin: {top: '100px'}}); await browser.close(); response.set('Content-Type', 'image/png'); response.send(image); } catch (error) { console.log(error); } }); // listener 監聽 3000埠 var listener = app.listen(3000, function () { console.log('Your appliction is listening on port ' + listener.address().port); }); ``` + package.json ``` { "name": "funnyzpc", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ``` #### 依賴安裝 + `npm i --save puppeteer express` [注意:如果安裝失敗 請檢查是否更改為taobao源] #### 啟動及管理 + 直接使用node啟動服務 - `node index.js` + 使用pm2啟動(如果安裝了pm2) - 啟動:`pm2 start index.js` - 程序:`pm2 list` - 刪除:`pm2 delete 應用ID` #### 使用 由於以上程式碼已經對截圖的載入做過處理的,所以無需在使用執行緒睡眠 同時代碼也對寬度(width)和高度(height)做了處理,所以具體訪問地址如下 `http://127.0.0.1:3000/screenshot/?login=[是否登入true or false]&width=[頁面寬度]&height=[頁面高度]&url=[截圖地址]` #### 最後 雖然我們我們使用`puppeteer`能應對絕大多數報表,後來發現`puppeteer`對多元件圖表存在渲染問題,所以就要求 提供商提供匯出圖片功能(使用者頁面匯出非api),所以最終一套就是 http模擬登入+呼叫截圖介面+圖片生成監控+推送圖片 好了,關於截圖就分享到這裡了,各位元旦節快樂哈~