利用NODEJS+Puppeteer實現伺服器儲存網頁為圖片和PDF

花間酒發表於2018-01-25

本文適用於對docker,node有一定了解的童鞋

Puppeteer簡介

今年5月在github上建立的專案,屬於比較新的chromium無頭瀏覽器類庫。

Puppeteer在專案中的實際使用

基礎安裝

選用Puppeteer的主要原因有兩點,

  • 1:GOOGLE官方維護,活躍度很高,個人覺得前途光明。
  • 2:我們的產品在chrome上適配最好。目前最新版本是0.13.0,我們採用0.12.0版本,因0.13.0版本API做了一些變化無法滿足我們的需求。我們截圖時有如下兩個必須解決的場景
    • 網站需要等待當前儀表盤所有查詢都完成才可以進行截圖操作
    • 我們並不知道所有儀表盤發起的查詢會查詢多久結束

npm安裝puppeteer時會從google一個網站上下載chromium,因為牆的原因會下載失敗。我們採用的方式先設定環境變數

    set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
複製程式碼

阻止install的時候自動下載,然後手工下載chromium後通過docker build打包成一個基礎映象。我們在Dockerfile中From此映象,然後再做後續操作。

    npm instal puppeteer@0.12.0 --save
複製程式碼

現在可通過docker很快速的進行打包。最終打包後的image裡/usr/src/node/包含node程式碼及chromium目錄

基本操作

呼叫puppeteer

我們手動指定chromium目錄來執行。

   const browser = await puppteer.launch({
        args: ['--no-sandbox', '--disable-setuid-sandbox'],  // docker中執行需要加上這兩個args
        executablePath: 'chromium/chrome', // 基礎映象已將chromium複製到/usr/src/node/chromium目錄下
   });
複製程式碼

儲存圖片

通過URL開啟網站

   await page.goto(fullUrl, {
       waitUntil: 'networkidle',
       networkIdleTimeout: 15000,
       timeout: 240000
   });
複製程式碼

networkIdleTimeout: 15000引數代表當前頁面網路處於idle狀態至少15秒時導航完畢,避免匯出的截圖資料不全。
如果直接儲存整個頁面為圖片或PDF是很簡單的,有現成的API直接呼叫。但這次我們只將某一區域儲存為圖片,

    let rect = await page.evaluate(() => {
        const element = document.querySelector(
            '.class1'
        );  // 選擇包含指定class屬性的dom節點
        const { x, y, width, height } = element.getBoundingClientRect();
        return {
            left: x,
            top: y,
            width,
            height,
        };
    });
    await page.screenshot({
        path: imagePath,
        clip: {
            x: rect.x,
            y: rect.y,
            width: actualWidth,
            height: actualHeight
        }
    });
複製程式碼

可以在page.evaluate中操作頁面元素,所以可以獲取指定區域的長寬等資訊。這樣我們只需擷取那一區域即可。完整的API地址還是請參閱github官方API文件

儲存PDF

如上節所說,如果儲存整個頁面為PDF很簡單,因為我們只儲存某一區域,然而儲存pdf的API中沒有類似page.screenshot中clip引數,我的處理方式就是將上一步儲存的圖片轉為PDF即可。轉換方式很多,我採用pdfkit類庫實現。程式碼就不贅述,可以參考很多DEMO。

總結

因為我們通過docker+CICD+devops打包部署node服務,puppeteer在docker中也有一些坑,好在官方給出了一系列解決方案。我在實際使用中還是偶爾發生頁面載入失敗的情況,期望在未來版本會變得更加強大和穩定。

相關文章