gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区

站長資訊網
最全最豐富的資訊網站

淺析Node中怎么利用Puppeteer庫生成海報(實現方案分享)

怎么利用Node生成海報?下面本篇文章給大家介紹一下使用Node+Puppeteer生成海報的方法,希望對大家有所幫助!

淺析Node中怎么利用Puppeteer庫生成海報(實現方案分享)

之前文章寫了一下前幾天因為使用了 html2canvas 碰到了很多兼容性問題,差點提桶跑路。然后經過評論區大佬們指導,發現了一個操作簡單,復用性高的海報生成方案—— Node+Puppeteer生成海報

主要的設計思路為:訪問生成海報的接口,接口通過Puppeteer去訪問傳入的地址,將對應的元素截圖返回。

Puppeteer 生成海報相對于 Canvas 生成的優勢有哪些:

  • 沒有瀏覽器兼容,平臺兼容等問題。
  • 代碼復用性高,h5、小程序、app的生成海報服務都可以使用。
  • 優化操作空間更大。因為改成了接口生成海報的形式,可以使用各種服務端的方式去優化響應速度,比如:加服務器、加緩存

puppeteer介紹

Puppeteer 是一個 Nodejs 庫,它提供了一個高級 API 來通過 DevTools 協議控制 Chromium 或 Chrome。Puppeteer 默認以 headless 模式運行即“無頭”模式,但是可以通過修改配置 headless:false 運行“有頭”模式。 在瀏覽器中手動執行的絕大多數操作都可以使用 Puppeteer 來完成! 下面是一些示例:

  • 生成頁面 PDF或者截圖。
  • 抓取 SPA(單頁應用)并生成預渲染內容(即“SSR”(服務器端渲染))。
  • 自動提交表單,進行 UI 測試,鍵盤輸入等。
  • 創建一個時時更新的自動化測試環境。 使用最新的 JavaScript 和瀏覽器功能直接在最新版本的Chrome中執行測試。
  • 捕獲網站的 timeline trace,用來幫助分析性能問題。
  • 測試瀏覽器擴展。

方案實現

1. 寫一個簡單的接口

Express 是一個簡潔而靈活的 node.js Web應用框架。使用express寫一個簡單的node服務,定義一個接口,接收截圖所需的配置項傳遞給puppeteer。

const express = require('express') const createError = require("http-errors") const app = express() // 中間件--json化入參 app.use(express.json()) app.post('/api/getShareImg', (req, res) => {     // 業務邏輯 }) // 錯誤攔截 app.use(function(req, res, next) {     next(createError(404)); }); app.use(function(err, req, res, next) {     let result = {         code: 0,         msg: err.message,         err: err.stack     }     res.status(err.status || 500).json(result) }) // 啟動服務監聽7000端口 const server = app.listen(7000, '0.0.0.0', () => {     const host = server.address().address;     const port = server.address().port;     console.log('app start listening at http://%s:%s', host, port); });

2. 創建一個截圖模塊

打開一個瀏覽器 => 打開一個標簽頁 => 截圖 => 關閉瀏覽器

const puppeteer = require("puppeteer");  module.exports = async (opt) => {     try {         const browser = await puppeteer.launch();         const page = await browser.newPage();         await page.goto(opt.url, {             waitUntil: ['networkidle0']         });         await page.setViewport({             width: opt.width,             height: opt.height,         });         const ele = await page.$(opt.ele);         const base64 = await ele.screenshot({             fullPage: false,             omitBackground: true,             encoding: 'base64'         });         await browser.close();         return 'data:image/png;base64,'+ base64     } catch (error) {         throw error     } };
  • puppeteer.launch([options]):啟動一個瀏覽器
  • browser.newPage():創建一個標簽頁
  • page.goto(url[, options]):導航到某個頁面
  • page.setViewport(viewport):制定打開頁面的窗口
  • page.$(selector):元素選擇
  • elementHandle.screenshot([options]):截圖。其中encoding屬性可以指定返回值是base64或Buffer
  • browser.close():關閉瀏覽器及標簽頁

3. 優化

1. 請求時間優化

page.goto(url[, options]) 方法的配置項 waitUntil 表示什么狀態下算執行完畢, 默認是load事件觸發時。事件包括:

 await page.goto(url, {      waitUntil: [          'load', //頁面“load” 事件觸發          'domcontentloaded', //頁面 “DOMcontentloaded” 事件觸發          'networkidle0', //在 500ms 內沒有任何網絡連接          'networkidle2' //在 500ms 內網絡連接個數不超過 2 個      ]  });

如果使用 networkidle0 的方案等待頁面完成,會發現接口的響應時間會比較長, 因為 networkidle0 需要等待500ms,真實業務場景下很多情況下不需要等待,所以可以封裝一個延時器,可以自定義等待時間。比如我們的海報頁只是渲染一個背景圖跟一個二維碼圖片,頁面觸發 load 時已經加載完成了,不需要等待時間,可以傳入0跳過等待時間。

 const waitTime = (n) => new Promise((r) => setTimeout(r, n));  //省略部分代碼  await page.goto(opt.url);  await waitTime(opt.waitTime || 0);

如果這種方式不能滿足,需要頁面在某個時機通知puppeteer結束,還可以使用 page.waitForSelector(selector[, options]) 等待頁面某個指定的元素出現。比如:頁面執行完某個操作時,插入一個 id="end" 的元素,puppereer 等待這個元素出現。

 await page.waitForSelector("#end")

類似的方法共包括:

  • page.waitForXPath(xpath[, options]):等待 xPath 對應的元素出現在頁面中。
  • page.waitForSelector(selector[, options]):等待指定的選擇器匹配的元素出現在頁面中,如果調用此方法時已經有匹配的元素,那么此方法立即返回。
  • page.waitForResponse(urlOrPredicate[, options]):等待指定的響應結束。
  • page.waitForRequest(urlOrPredicate[, options]):等待指定的響應出現。
  • page.waitForFunction(pageFunction[, options[, …args]]):等待某個方法執行。
  • page.waitFor(selectorOrFunctionOrTimeout[, options[, …args]]):此方法相當于上面幾個方法的選擇器,根據第一個參數的不同結果不同,比如:傳入一個string類型,會判斷是不是xpath或者selector,此時相當于waitForXPath或waitForSelector。

2. 啟動項優化

Chromium啟動時還會開啟很多不需要的功能,可以通過參數禁用某些啟動項。

    const browser = await puppeteer.launch({         headless: true,         slowMo: 0,         args: [             '--no-zygote',             '--no-sandbox',             '--disable-gpu',             '--no-first-run',             '--single-process',             '--disable-extensions',             "--disable-xss-auditor",             '--disable-dev-shm-usage',             '--disable-popup-blocking',             '--disable-setuid-sandbox',             '--disable-accelerated-2d-canvas',             '--enable-features=NetworkService',         ]     });

3. 復用瀏覽器

因為每次接口被調用都啟動了一個瀏覽器,截圖之后關閉了這個瀏覽器,造成了資源的浪費,并且啟動瀏覽器也需要耗費時間。并且同時啟動的瀏覽器過多,程序還會拋出異常。所以使用了連接池:啟動多個瀏覽器,在其中一個瀏覽器下創建標簽頁打開頁面,截圖完成后只關閉標簽頁,保留瀏覽器。下一次請求過來時直接創建標簽頁,達到復用瀏覽器的目的。當瀏覽器使用次數達到一定數目或者一段時間內沒有被使用時就關閉這個瀏覽器。 有大佬已經對generic-pool這個連接池進行了處理,我就直接拿來用了。

const initPuppeteerPool = () => {  if (global.pp) global.pp.drain().then(() => global.pp.clear())  const opt = {    max: 4,//最多產生多少個puppeteer實例 。    min: 1,//保證池中最少有多少個puppeteer實例存活    testOnBorrow: true,// 在將實例提供給用戶之前,池應該驗證這些實例。    autostart: false,//是不是需要在池初始化時初始化實例    idleTimeoutMillis: 1000 * 60 * 60,//如果一個實例60分鐘都沒訪問就關掉他    evictionRunIntervalMillis: 1000 * 60 * 3,//每3分鐘檢查一次實例的訪問狀態    maxUses: 2048,//自定義的屬性:每一個 實例 最大可重用次數。    validator: () => Promise.resolve(true)  }  const factory = {    create: () =>      puppeteer.launch({        //啟動參數參考第二條      }).then(instance => {        instance.useCount = 0;        return instance;      }),    destroy: instance => {      instance.close()    },    validate: instance => {      return opt.validator(instance).then(valid => Promise.resolve(valid && (opt.maxUses <= 0 || instance.useCount < opt.maxUses)));    }  };  const pool = genericPool.createPool(factory, opt)  const genericAcquire = pool.acquire.bind(pool)  // 重寫了原有池的消費實例的方法。添加一個實例使用次數的增加  pool.acquire = () =>    genericAcquire().then(instance => {      instance.useCount += 1      return instance    })   pool.use = fn => {    let resource    return pool      .acquire()      .then(r => {        resource = r        return resource      })      .then(fn)      .then(        result => {          // 不管業務方使用實例成功與后都表示一下實例消費完成          pool.release(resource)          return result        },        err => {          pool.release(resource)          throw err        }      )  }  return pool; } global.pp = initPuppeteerPool()

4. 優化接口防止圖片重復生成

用同一組參數重復調用時每次都會開啟一個瀏覽器進程去截圖,可以使用緩存機制優化重復的請求。可以通過傳入唯一的key作為標識位(比如用戶id+活動id),將圖片base64存入redis或者寫入內存中。當接口被請求時先查看緩存里是否已經生成過,如果生成過就直接從緩存取。否則就走生成海報的流程。

結尾

這個方案目前已經開始在項目里試運行了,這對于我一個前端開發來說簡直太友好了,再也不用在小程序里一步一步去繪制canvas,不用考慮資源跨域,也不用考慮微信瀏覽器、各種自帶瀏覽器的兼容問題。省下了時間可以讓我寫這篇文章。其次,我比較擔心的還是性能問題,因為只有在分享的動作才會觸發,并發較小,目前使用還未暴露出性能的問題,有了解的大佬們可以指導我一下可以進一步優化或者預防的點。

代碼

完整代碼查看:github

https://github.com/yuwuwu/markdown-code/tree/master/puppeteer%E6%88%AA%E5%9B%BE

贊(0)
分享到: 更多 (0)
?
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区
国模吧无码一区二区三区| 波多野结衣之无限发射| 三日本三级少妇三级99| 特级黄色片视频| 老司机午夜免费福利视频| 妞干网在线播放| 手机在线看福利| 国产视频一区二区视频| 日韩精品一区二区免费| 日本高清一区二区视频| 亚洲第一综合网站| 黄色一级一级片| 久久综合色视频| 在线观看av免费观看| 欧美二区在线视频| 亚洲爆乳无码精品aaa片蜜桃| 免费成人午夜视频| aaa免费在线观看| 中文字幕天天干| 男人亚洲天堂网| 国产视频九色蝌蚪| 国产精品无码av在线播放| 在线观看成人免费| 国产又黄又爽免费视频| 国产性生交xxxxx免费| 国产日产欧美视频| 日日摸日日碰夜夜爽无码| 九九久久九九久久| 亚洲男人天堂2021| 手机看片日韩国产| 日本高清xxxx| 99精品在线免费视频| 日本香蕉视频在线观看| 免费一级特黄毛片| 国产无套内射久久久国产| 丰满爆乳一区二区三区| 久草精品在线播放| 亚洲精品www.| 成人av在线播放观看| 成人性免费视频| 能看的毛片网站| 成年丰满熟妇午夜免费视频| 国产爆乳无码一区二区麻豆| 日韩欧美在线播放视频| 9l视频白拍9色9l视频| www婷婷av久久久影片| 超碰网在线观看| 久久天天东北熟女毛茸茸| 国产97在线 | 亚洲| 在线观看免费黄网站| 九色自拍视频在线观看| 日韩av片专区| 国产又黄又猛又粗| 日本成人在线不卡| 一区二区成人网| 中文字幕免费高清在线| a级网站在线观看| 日本新janpanese乱熟| 成人性做爰片免费视频| 男人天堂网视频| 五月婷婷狠狠操| 久久久国产欧美| aaa毛片在线观看| 青青草国产精品视频| 亚洲制服中文字幕| 午夜精品久久久内射近拍高清| 国产探花在线观看视频| 欧美成人黑人猛交| 久久综合九色综合88i| 成年人深夜视频| 日日噜噜噜夜夜爽爽| av在线免费观看国产| 亚洲欧美天堂在线| 中文字幕中文在线| 亚洲一级片网站| 亚洲视频一二三四| 超碰中文字幕在线观看| 在线不卡一区二区三区| 日本人69视频| 激情图片qvod| 日本a级片在线播放| 蜜臀精品一区二区| 国产成人精品视频免费看| 色91精品久久久久久久久| 国内自拍第二页| 国产精品嫩草影院8vv8 | 欧美日韩亚洲一| 国产欧美日韩网站| 手机看片福利盒子久久| 亚洲国产成人精品无码区99| 毛片av在线播放| www.四虎成人| 国产av不卡一区二区| 少妇性饥渴无码a区免费| 成年人免费大片| 午夜啪啪福利视频| 超碰在线97免费| 国产一区二区片| 亚洲一区二区偷拍| 欧洲xxxxx| 欧美 另类 交| 国产素人在线观看| 国产日本欧美在线| 欧美日韩中文在线视频| 日本一本中文字幕| 亚洲天堂2018av| 蜜臀av.com| 欧美日韩福利在线| 岛国av在线免费| 91黄色在线看| 妞干网在线播放| 少妇黄色一级片| av免费在线播放网站| 777777av| 国产三级中文字幕| 日本一本二本在线观看| 激情成人在线观看| 成人免费观看视频在线观看| 白嫩少妇丰满一区二区| 欧美啪啪免费视频| 69sex久久精品国产麻豆| 欧美日韩福利在线| 中文字幕 91| 日韩精品手机在线观看| 日韩免费视频播放| 免费观看黄色的网站| 欧美性受xxxx黒人xyx性爽| 在线免费看v片| 欧美日韩在线一| 不卡的在线视频| 激情五月婷婷六月| 人妻少妇精品久久| 国产成人黄色网址| avav在线播放| 青青草免费在线视频观看| 亚洲男人天堂av在线| 青青草国产免费| 日本天堂免费a| a级网站在线观看| 欧美在线一区视频| 欧美 另类 交| 欧美男女交配视频| 国产日产欧美一区二区| 激情 小说 亚洲 图片: 伦| 麻豆中文字幕在线观看| 中国一级黄色录像| 日本手机在线视频| 美女黄色免费看| 视频一区二区视频| 亚洲 高清 成人 动漫| 日本美女视频一区| 国产午夜大地久久| 美女扒开大腿让男人桶| mm131午夜| 日本一道本久久| 青青青在线观看视频| 免费不卡av在线| 黄色片一级视频| www.日本xxxx| 青青在线视频免费| 黄色一级大片在线观看| 色一情一区二区| 亚洲另类第一页| 中国女人做爰视频| 搞av.com| 久久精品国产露脸对白| 大地资源网在线观看免费官网| 亚洲最大综合网| www.九色.com| 欧美成人黄色网址| 成人性做爰片免费视频| 成人免费在线网| 久久久久久久久久久99| 精品久久久久久中文字幕2017| 向日葵污视频在线观看| 99999精品| 91高清国产视频| 男人日女人逼逼| 久久综合久久网| 特黄特黄一级片| 日本黄大片一区二区三区| 国产美女视频免费| 国产3p在线播放| 国模杨依粉嫩蝴蝶150p| 国内少妇毛片视频| 日韩av黄色网址| 麻豆av免费在线| 免费观看黄色的网站| 成人性视频欧美一区二区三区| 欧美人成在线观看| 国产高清不卡无码视频| 亚洲精品成人在线播放| 日韩av.com| 一区二区三区四区久久| 亚洲av无日韩毛片久久| 国产精品69页| 亚洲一区二区三区四区精品| 欧美午夜精品理论片| 手机看片一级片| 免费极品av一视觉盛宴|