久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合

站長(zhǎng)資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

淺析Node中怎么利用Puppeteer庫(kù)生成海報(bào)(實(shí)現(xiàn)方案分享)

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

淺析Node中怎么利用Puppeteer庫(kù)生成海報(bào)(實(shí)現(xiàn)方案分享)

之前文章寫了一下前幾天因?yàn)槭褂昧?html2canvas 碰到了很多兼容性問(wèn)題,差點(diǎn)提桶跑路。然后經(jīng)過(guò)評(píng)論區(qū)大佬們指導(dǎo),發(fā)現(xiàn)了一個(gè)操作簡(jiǎn)單,復(fù)用性高的海報(bào)生成方案—— Node+Puppeteer生成海報(bào)

主要的設(shè)計(jì)思路為:訪問(wèn)生成海報(bào)的接口,接口通過(guò)Puppeteer去訪問(wèn)傳入的地址,將對(duì)應(yīng)的元素截圖返回。

Puppeteer 生成海報(bào)相對(duì)于 Canvas 生成的優(yōu)勢(shì)有哪些:

  • 沒(méi)有瀏覽器兼容,平臺(tái)兼容等問(wèn)題。
  • 代碼復(fù)用性高,h5、小程序、app的生成海報(bào)服務(wù)都可以使用。
  • 優(yōu)化操作空間更大。因?yàn)楦某闪私涌谏珊?bào)的形式,可以使用各種服務(wù)端的方式去優(yōu)化響應(yīng)速度,比如:加服務(wù)器、加緩存

puppeteer介紹

Puppeteer 是一個(gè) Nodejs 庫(kù),它提供了一個(gè)高級(jí) API 來(lái)通過(guò) DevTools 協(xié)議控制 Chromium 或 Chrome。Puppeteer 默認(rèn)以 headless 模式運(yùn)行即“無(wú)頭”模式,但是可以通過(guò)修改配置 headless:false 運(yùn)行“有頭”模式。 在瀏覽器中手動(dòng)執(zhí)行的絕大多數(shù)操作都可以使用 Puppeteer 來(lái)完成! 下面是一些示例:

  • 生成頁(yè)面 PDF或者截圖。
  • 抓取 SPA(單頁(yè)應(yīng)用)并生成預(yù)渲染內(nèi)容(即“SSR”(服務(wù)器端渲染))。
  • 自動(dòng)提交表單,進(jìn)行 UI 測(cè)試,鍵盤輸入等。
  • 創(chuàng)建一個(gè)時(shí)時(shí)更新的自動(dòng)化測(cè)試環(huán)境。 使用最新的 JavaScript 和瀏覽器功能直接在最新版本的Chrome中執(zhí)行測(cè)試。
  • 捕獲網(wǎng)站的 timeline trace,用來(lái)幫助分析性能問(wèn)題。
  • 測(cè)試瀏覽器擴(kuò)展。

方案實(shí)現(xiàn)

1. 寫一個(gè)簡(jiǎn)單的接口

Express 是一個(gè)簡(jiǎn)潔而靈活的 node.js Web應(yīng)用框架。使用express寫一個(gè)簡(jiǎn)單的node服務(wù),定義一個(gè)接口,接收截圖所需的配置項(xiàng)傳遞給puppeteer。

const express = require('express') const createError = require("http-errors") const app = express() // 中間件--json化入?yún)?app.use(express.json()) app.post('/api/getShareImg', (req, res) => {     // 業(yè)務(wù)邏輯 }) // 錯(cuò)誤攔截 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) }) // 啟動(dòng)服務(wù)監(jiān)聽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. 創(chuàng)建一個(gè)截圖模塊

打開一個(gè)瀏覽器 => 打開一個(gè)標(biāo)簽頁(yè) => 截圖 => 關(guān)閉瀏覽器

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]):?jiǎn)?dòng)一個(gè)瀏覽器
  • browser.newPage():創(chuàng)建一個(gè)標(biāo)簽頁(yè)
  • page.goto(url[, options]):導(dǎo)航到某個(gè)頁(yè)面
  • page.setViewport(viewport):制定打開頁(yè)面的窗口
  • page.$(selector):元素選擇
  • elementHandle.screenshot([options]):截圖。其中encoding屬性可以指定返回值是base64或Buffer
  • browser.close():關(guān)閉瀏覽器及標(biāo)簽頁(yè)

3. 優(yōu)化

1. 請(qǐng)求時(shí)間優(yōu)化

page.goto(url[, options]) 方法的配置項(xiàng) waitUntil 表示什么狀態(tài)下算執(zhí)行完畢, 默認(rèn)是load事件觸發(fā)時(shí)。事件包括:

 await page.goto(url, {      waitUntil: [          'load', //頁(yè)面“l(fā)oad” 事件觸發(fā)          'domcontentloaded', //頁(yè)面 “DOMcontentloaded” 事件觸發(fā)          'networkidle0', //在 500ms 內(nèi)沒(méi)有任何網(wǎng)絡(luò)連接          'networkidle2' //在 500ms 內(nèi)網(wǎng)絡(luò)連接個(gè)數(shù)不超過(guò) 2 個(gè)      ]  });

如果使用 networkidle0 的方案等待頁(yè)面完成,會(huì)發(fā)現(xiàn)接口的響應(yīng)時(shí)間會(huì)比較長(zhǎng), 因?yàn)?networkidle0 需要等待500ms,真實(shí)業(yè)務(wù)場(chǎng)景下很多情況下不需要等待,所以可以封裝一個(gè)延時(shí)器,可以自定義等待時(shí)間。比如我們的海報(bào)頁(yè)只是渲染一個(gè)背景圖跟一個(gè)二維碼圖片,頁(yè)面觸發(fā) load 時(shí)已經(jīng)加載完成了,不需要等待時(shí)間,可以傳入0跳過(guò)等待時(shí)間。

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

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

 await page.waitForSelector("#end")

類似的方法共包括:

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

2. 啟動(dòng)項(xiàng)優(yōu)化

Chromium啟動(dòng)時(shí)還會(huì)開啟很多不需要的功能,可以通過(guò)參數(shù)禁用某些啟動(dòng)項(xiàng)。

    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. 復(fù)用瀏覽器

因?yàn)槊看谓涌诒徽{(diào)用都啟動(dòng)了一個(gè)瀏覽器,截圖之后關(guān)閉了這個(gè)瀏覽器,造成了資源的浪費(fèi),并且啟動(dòng)瀏覽器也需要耗費(fèi)時(shí)間。并且同時(shí)啟動(dòng)的瀏覽器過(guò)多,程序還會(huì)拋出異常。所以使用了連接池:?jiǎn)?dòng)多個(gè)瀏覽器,在其中一個(gè)瀏覽器下創(chuàng)建標(biāo)簽頁(yè)打開頁(yè)面,截圖完成后只關(guān)閉標(biāo)簽頁(yè),保留瀏覽器。下一次請(qǐng)求過(guò)來(lái)時(shí)直接創(chuàng)建標(biāo)簽頁(yè),達(dá)到復(fù)用瀏覽器的目的。當(dāng)瀏覽器使用次數(shù)達(dá)到一定數(shù)目或者一段時(shí)間內(nèi)沒(méi)有被使用時(shí)就關(guān)閉這個(gè)瀏覽器。 有大佬已經(jīng)對(duì)generic-pool這個(gè)連接池進(jìn)行了處理,我就直接拿來(lái)用了。

const initPuppeteerPool = () => {  if (global.pp) global.pp.drain().then(() => global.pp.clear())  const opt = {    max: 4,//最多產(chǎn)生多少個(gè)puppeteer實(shí)例 。    min: 1,//保證池中最少有多少個(gè)puppeteer實(shí)例存活    testOnBorrow: true,// 在將實(shí)例提供給用戶之前,池應(yīng)該驗(yàn)證這些實(shí)例。    autostart: false,//是不是需要在池初始化時(shí)初始化實(shí)例    idleTimeoutMillis: 1000 * 60 * 60,//如果一個(gè)實(shí)例60分鐘都沒(méi)訪問(wèn)就關(guān)掉他    evictionRunIntervalMillis: 1000 * 60 * 3,//每3分鐘檢查一次實(shí)例的訪問(wèn)狀態(tài)    maxUses: 2048,//自定義的屬性:每一個(gè) 實(shí)例 最大可重用次數(shù)。    validator: () => Promise.resolve(true)  }  const factory = {    create: () =>      puppeteer.launch({        //啟動(dòng)參數(shù)參考第二條      }).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)  // 重寫了原有池的消費(fèi)實(shí)例的方法。添加一個(gè)實(shí)例使用次數(shù)的增加  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 => {          // 不管業(yè)務(wù)方使用實(shí)例成功與后都表示一下實(shí)例消費(fèi)完成          pool.release(resource)          return result        },        err => {          pool.release(resource)          throw err        }      )  }  return pool; } global.pp = initPuppeteerPool()

4. 優(yōu)化接口防止圖片重復(fù)生成

用同一組參數(shù)重復(fù)調(diào)用時(shí)每次都會(huì)開啟一個(gè)瀏覽器進(jìn)程去截圖,可以使用緩存機(jī)制優(yōu)化重復(fù)的請(qǐng)求。可以通過(guò)傳入唯一的key作為標(biāo)識(shí)位(比如用戶id+活動(dòng)id),將圖片base64存入redis或者寫入內(nèi)存中。當(dāng)接口被請(qǐng)求時(shí)先查看緩存里是否已經(jīng)生成過(guò),如果生成過(guò)就直接從緩存取。否則就走生成海報(bào)的流程。

結(jié)尾

這個(gè)方案目前已經(jīng)開始在項(xiàng)目里試運(yùn)行了,這對(duì)于我一個(gè)前端開發(fā)來(lái)說(shuō)簡(jiǎn)直太友好了,再也不用在小程序里一步一步去繪制canvas,不用考慮資源跨域,也不用考慮微信瀏覽器、各種自帶瀏覽器的兼容問(wèn)題。省下了時(shí)間可以讓我寫這篇文章。其次,我比較擔(dān)心的還是性能問(wèn)題,因?yàn)橹挥性诜窒淼膭?dòng)作才會(huì)觸發(fā),并發(fā)較小,目前使用還未暴露出性能的問(wèn)題,有了解的大佬們可以指導(dǎo)我一下可以進(jìn)一步優(yōu)化或者預(yù)防的點(diǎn)。

代碼

完整代碼查看:github

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

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合
欧美freesex黑人又粗又大| 视频一区二区国产| 亚洲一区二区三区无吗| 91久久国产| 日韩网站在线| 鲁大师成人一区二区三区 | 亚洲人成亚洲精品| 国产综合婷婷| 国产一区日韩欧美| 欧美日韩国产亚洲一区| 夜夜精品视频| 蜜臀久久久久久久| 欧美日本久久| 精品亚洲免a| 日韩一区自拍| 亚洲精品2区| 久久av一区二区三区| 蜜臀91精品一区二区三区| 亚洲精品在线二区| 国产欧美亚洲一区| 国产精品福利在线观看播放| 久久久成人网| 成人久久久久| 欧美日韩国产一区精品一区| 亚洲3区在线| 国产精选一区| 日本蜜桃在线观看视频| 伊人久久大香线蕉av不卡| 久久精品免费一区二区三区 | 欧美一级精品| 亚洲精品在线a| 精品亚洲二区| 日韩视频久久| 日本亚洲欧美天堂免费| 欧美国产极品| 极品日韩av| 日韩欧美中文在线观看| 国产乱子精品一区二区在线观看 | 亚洲日本久久| 精品视频在线你懂得| 99久久婷婷这里只有精品| 亚洲影视一区二区三区| 国产精品毛片aⅴ一区二区三区| 粉嫩av一区二区三区四区五区| 欧美激情另类| 国产亚洲综合精品| 国产精品地址| 1024精品久久久久久久久| 欧美另类中文字幕| 久久一区亚洲| 女主播福利一区| 欧美一区二区三区久久精品| 岛国av在线播放| 亚洲欧美视频一区二区三区| 国产精品a级| 亚洲一区国产| 精品一区二区三区中文字幕视频 | 国产在线一区不卡| 亚洲一区欧美二区| 国产精品欧美三级在线观看| 日韩精品一区二区三区免费观影 | 国产日韩亚洲欧美精品| 久久亚洲精品中文字幕| 亚洲欧洲一区二区天堂久久| 国产精品白浆| 国产精品日本欧美一区二区三区| 国产精品色在线网站| 制服诱惑一区二区| 精品国产精品国产偷麻豆| 爽好久久久欧美精品| 亚洲五月综合| 精精国产xxxx视频在线野外| 国产亚洲高清视频| 麻豆精品久久| 日韩在线一二三区| 国产精品13p| 91p九色成人| 色婷婷色综合| 日韩高清一区二区| 精品一区亚洲| 精品国产成人| 国产亚洲字幕| 亚洲免费毛片| 91高清一区| 黄色在线网站噜噜噜| 97se亚洲| 蜜桃视频免费观看一区| 亚洲精品一级二级| 久久午夜影院| 欧美亚洲二区| 亚洲午夜免费| 91久久久久| 日韩深夜视频| 另类小说一区二区三区| 蜜桃久久久久久| 欧美高清一区| 92国产精品| 日本久久精品| 久久久久亚洲精品中文字幕| 国产探花在线精品| 婷婷综合国产| 喷白浆一区二区| 伊人精品在线| 亚洲国产影院| 亚洲成av在线| 高清一区二区| 成人污污视频| 7777精品| 国产美女撒尿一区二区| 亚洲日本欧美| 亚洲精品护士| 天堂成人免费av电影一区| 久久久久久免费视频| 中文在线а√天堂| 色婷婷综合网| 日本美女一区| 亚洲成人av观看| 久久视频一区| 1000部精品久久久久久久久| 国产一区日韩欧美| 伊人久久亚洲热| 亚洲中午字幕| 一本一道久久a久久精品蜜桃| 久久中文字幕二区| 亚洲一级二级| 欧美日韩激情| 99在线精品免费视频九九视| 日韩视频不卡| 综合激情视频| 日韩av一区二区三区| 97成人在线| 免费在线日韩av| 国产一区二区三区四区二区| 色爱综合网欧美| 色婷婷狠狠五月综合天色拍| 久久三级福利| 国产视频一区三区| 国产精品社区| 日本欧美一区| 国产福利资源一区| 日韩av在线播放网址| 成人高清一区| 亚洲国产影院| 日本欧美在线看| 日本久久一区| 精品亚洲自拍| 国产精品亚洲人成在99www| 在线国产一区二区| 亚州av乱码久久精品蜜桃| 伊人久久大香线蕉av超碰演员| 国产精品免费看| 久久亚洲欧美| 亚洲麻豆一区| 国产精品美女午夜爽爽| 福利视频一区| 免费观看久久av| 国产视频久久| 国产精一区二区| 欧产日产国产精品视频| 999精品一区| 欧美va亚洲va日韩∨a综合色| 久久亚洲图片| 久久99视频| 香蕉久久精品| 亚州精品视频| 91亚洲国产| 羞羞答答国产精品www一本| 亚洲天堂日韩在线| 免费在线成人| 亚洲二区在线| 久久精品99国产国产精| 精品国产免费人成网站| 欧美1区免费| 亚洲欧美日韩在线观看a三区| 国产亚洲一卡2卡3卡4卡新区| 久久男人天堂| 蜜臀a∨国产成人精品| 麻豆91小视频| 午夜欧美精品久久久久久久| 91精品福利观看| 日韩不卡视频在线观看| 日韩精品一区二区三区免费视频 | 成人av动漫在线观看| 亚洲精品字幕| 欧美日韩视频网站| 日韩精品一区二区三区中文 | 日韩亚洲一区在线| 日本亚洲三级在线| 日韩精品欧美激情一区二区| 日韩一区精品字幕| 日韩av一二三| 久久精品亚洲人成影院| 日韩精品中文字幕一区二区| 水蜜桃精品av一区二区| 国产免费成人| 国产不卡精品| 日韩三级视频| 久久国产影院| 国产精品综合| 国产精品毛片一区二区三区|