網頁生成PDF文件應用

生成PDF的需求多麼?

你沒有碰到過要將網頁轉換爲PDF文件的需求? 圖片呢?
如何保存一個網頁的快照? 截圖? 頁面過長怎辦,系統自帶的截圖工具無法滾屏
海外網頁被牆,經過在香港服部署一PDF生成服務能夠免除無謂的瀏覽其它垃圾/非法網址。
公司管理層須要在公司集成辦公系統上查看其它系統的資料,好比hr系統上員工簡歷,營銷系統上的某報表...html

需求仍是很多的。前端

使用歷史

我的前後使用過 wkhtmltopdf , phantomjs ,puppeteer。
wkhtmltopdf 使用起來最簡單,功能比較單一,看名字就知道了,在windows下直接就是一個exe,可在本身的項目中fork/startProcess 開一個新進程,並傳入網頁和保存的路徑地址,經過 wkhtmltopdf -h 能夠查看各參數和說明。若是你要生成的頁面是集團內部服務,那麼使用它是最方便的。node

phantomjs ,一樣經過名字就知道它和js 結合的很是緊密。它使用webkit內核,同pupp,用戶在可在頁面內注入js,像在瀏覽器上操做頁面和元素同樣操做它,但它已中止更新了。linux

puppeteer 是google 提供的 node 庫用來操做chrome,有些人稱之爲無頭瀏覽器,在linux上使用會配套下載chromium。功能上徹底能夠做爲phantomjs 替代品,正由於它操做的實際是chrome瀏覽器,對於分析頁面數據,模擬和破解前端邏輯很是給力。web

除此以外,其實還有不少工具,網上能搜出一大把,這裏就很少說了。chrome

使用puppeteer實現windows

'use strict'
 
const Service = require("egg").Service;
const puppeteer = require('puppeteer');

class pupp extends Service {
    async Create(tourl, savePath,ops) {
        ops = ops || {};
        //console.log("pupp.js ", tourl, savePath,ops);
        const browser = await puppeteer.launch({
                args : [
                    '--no-sandbox',
                    '--disable-setuid-sandbox',
                    '--disable-gpu',
                    '--disable-dev-shm-usage',
                    '--no-first-run',
                    '--no-zygote',
                    //'--single-process'
                ],
                ignoreHTTPSErrors:true
            });
        const page = await browser.newPage();
        // console.log("before go to:");
        await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36");
        /*
        await page.setExtraHTTPHeaders({
        "accept-language":"zh-CN,zh;q=0.9,en;q=0.8",
        "cookie":"csrfToken=V04e-JSpCtM_BEr_VH2-73N9",
        "cache-control":"max-age=0"
        });*/
         
        if(ops.cookies)
        {
          console.log("setcookies return :",await page.setCookie(...ops.cookies));
          await page.waitFor(200);
        }
        await page.goto(tourl, {
            waitUntil : 'networkidle2',
            timeout : 60000
        });
           
        if(ops.navigation)
          await page.waitForNavigation({waitUntil:'networkidle2'});//沒有這個,weibo 相關頁面會是空白
         
        //await page.screenshot({path: 'screenshot.png'});
        //否則會有href 顯示在頁面
        page.emulateMedia('screen');
 
        let scrollEnable = true;
        let scrollStep = 1000; //每次滾動的步長
        let scrollCount = 0;
        while (scrollEnable) {
            if (scrollCount++ > 5) //最多容許滾動5次
            {
                console.error("too many scroll, jumpout ", tourl);
                scrollEnable = false;
                break;
            }
 
            scrollEnable = await page.evaluate((scrollStep) => {
                        let scrollTop = document.scrollingElement.scrollTop;
                        document.scrollingElement.scrollTop = scrollTop + scrollStep;
                        return document.body.clientHeight > scrollTop + scrollStep ? true : false
                    }, scrollStep);
            await page.waitFor(200)
        }
        page.waitFor(2000);
 
        await page.pdf({
            path : savePath,
            width : "1280px",
            height : "1960px",
            printBackground:true
        });
        await browser.close();
 
    }
    /**
    將cookie string 轉換爲cookie object 的數組
    */
    cvtCookie(ck_line)
    {
        let arr = fs.readFileSync("./old_cookies.txt").toString().split(";");
        let objcks = arr.map(aline=>{
          let ind = aline.indexOf("=");
          return {name:aline.substr(0,ind),value:aline.substr(ind+1)};
        });
    }
}
 
//延時
function waitMs(ms) {
    if (typeof ms != "number")
        return Promise.resolve(0);
    return new Promise((resolve, rej) => {
            setTimeout(() => {
                    resolve(1);
                }, ms);
        });
}
 
module.exports = pupp;

以上是生成pdf文件的核心代碼,使用的是egg框架,不熟悉並不影響閱讀。有以下幾點可留意一下數組

1:有些註釋掉的代碼沒有去掉,有些代碼也能夠註釋,取決於你真實的測試狀況,許多網頁的渲染方式不同,有些還混合着多種js異步渲染。瀏覽器

2:文中的一些具體參數都是通過反覆屢次驗證效果後添加或調整過的,官網上並不會有這些內容,有時一個默認參數就會讓效果迥異。服務器

3:代碼中有段cookie設置,這是由於不少網頁用戶不登錄不能看,因此須要前置的用戶登錄,並獲取登錄成功後的cookie,進而在此步使用。須要留意模擬登錄後的cookie(甚至手動在瀏覽器登錄後保存cookie)使用的瀏覽器請求頭應儘可能保持一致,以避免服務端將它區分爲兩個不一樣用戶。

4:文中有個滾動頁面的邏輯,由於有些頁面僅顯示一屏,當用戶滾動鼠標到文末時纔會繼續加載,就像一些手機app,最多往下拉五次,拉太多沒多大意義還浪費時間。

一點廢話

puppeteer用做生成pdf有些大材小用了,用做分析和操做頁面元素獲取數據都十分方便的。我的認爲它不太適合大批量的爬取數據,效率較低。

另外,要獲取別人的頁面就繞不開須要登錄和驗證碼,若是不是過於頻繁的請求,如上文所述,直接用戶手動在瀏覽器輸入賬密,而後再將瀏覽器的cookie保存下來,交由上面代碼處理就夠了。但若是是將服務部署在服務器,這種辦法就失去了做用。筆者就用過一個網上下載的新浪微博登錄庫,並使用一種很「挫」的方式填充驗證碼,工做良好,有興趣的同窗能夠留言向我瞭解。

下一篇,將講述使用 selinum 獲取股票數據

相關文章
相關標籤/搜索