1. 程式人生 > >福利!NodeJs爬取網路教程並生成PDF檔案,以阮一峰JavaScript教程和ES6教程為例(附原始碼和PDF檔案)

福利!NodeJs爬取網路教程並生成PDF檔案,以阮一峰JavaScript教程和ES6教程為例(附原始碼和PDF檔案)

前言

你想一夜暴富嗎?你想一夜成名嗎?你想開蘭博基尼泡妞嗎?你想拿鈔票點菸嗎?你想成為世界主宰嗎?那麼,趕緊往下看吧,雖然它不能達成前面所說的任 何一個夢想,但是,你將獲得:

  1. 通過命令列將某網站的內容轉成PDF檔案
  2. 通過NodeJS爬蟲將某網路教程(例如阮一峰的JavaScript教程和ES6教程等)轉成PDF檔案
  3. 通過NodeJS或者VScode外掛將Markdown檔案轉成PDF檔案

依賴模組

cheerio是nodejs的抓取頁面模組,為伺服器特別定製的,快速、靈活、實施的jQuery核心實現。適合各種Web爬蟲程式

Request是Node.js中的一個模組,目標是用最可能簡單的方式,在Node.js發起HTTP請求。此外也支援最新的HTTPS協議

一個通過網址將網頁轉成PDF的命令列工具,NodeJS版本要大於8.6.0,如果出現安裝失敗,請翻牆後再安裝

將markdown檔案轉成PDF檔案

命令列匯出PDF檔案

percollate是一位外國友人做的一個命令列工具,是對puppeteer做了一層封裝,暴露出常用的API, 我們來看下文件中的例子

一個頁面

percollate pdf --output some.pdf https://example.com

多個頁面

percollate pdf --output some.pdf https://example.com/page1 https://example.com/page2

操作很簡單是不是,哇哦,我們是不是可以美滋滋的將自己喜歡的文章轉成PDF在手機或者電腦上看了,嗯,沒錯是的,不過這也就能玩個文章,如果想拿下整個網站的所有文章就心有餘而力不足了,比如下面的這個

當然,不排除有些比較有毅力的同學,把所有url都拿到,然後拼到命令列中,就像我曾經在工作中見過某同事在專案做完後,一行一行的去刪console.log(),為的是線上版本的控制檯不出現列印的資訊,得說下我們使用webpack打包的,這在打包的時候新增一個配置就能解決的問題,我們幹嘛要費那老鼻子的勁,拒絕假努力,我們來點有效率的。

NodeJs爬蟲將整個網站生成PDF檔案

首先我們來瞅一眼阮大神的Javascript教程的網站,地址為 wangdoc.com/javascript/…, 我們能看到網頁右邊為所有章節的導航,開啟Chrome開發者工具,我們能看到

紅框圈出的地方即為每個章節對應的地址,到此,你應該能想到接下里我們要幹什麼了吧,就是通過爬蟲拿到所有的章節地址,文章中的地址為相對路徑,我們再拼上域名,就成了可以訪問的地址了,至於怎麼去抓取所有的文章地址,在這裡略去,比較簡單,相信你看一眼就能懂,原始碼在下面喲。

那麼我們想在NodeJs中使用percollate,該如何操作呢?前面已經說了percollate是個命令列工具,文件上並沒有告訴我們怎麼在Node環境中怎麼呼叫,難道我們要放棄,直接用puppeteer或者phantomjs去擼嗎?怎麼可能?秉著你是NodeJs的包,肯定能在NodeJs環境跑的宗旨,我把percollate打印出來瞅瞅

喲,percollate物件下有一個pdf的方法,這不就是我們想要的嗎?來一起去瞅瞅原始碼,看看這個方法要怎麼使用,找到專案下面的index.js檔案,別問我為什麼要看這個檔案,程式設計師都知道的

我們在頁面搜尋pdf,最終能找到

當然我們也可以通過列印percollate.pdf檢視函式的內容

ok,我們現在知道pdf方法接收兩個引數,一個是urls,一個是options,通過名字基本就能推測出這兩個引數的內容,urls為要轉成pdf的網址的陣列,options是對轉成的pdf檔案的一些配置,事實確如我們所料,現在我們可以愉快的生成pdf檔案了,美滋滋!實際上你執行之後得到的pdf檔案是這個樣子的

what?為什麼是html?出現這種情況我的第一反應是需要新增某些配置,但文件都是關於命令列操作的,又沒有相關的解釋,我了個擦,咋辦?還能咋辦,看原始碼和Issues, 以我五級的英語水平在Issues中發現這樣一個標題

這位老哥想在瀏覽器中用percollate,喲,老哥可以呀,我Node裡面還沒跑起來,你就想在瀏覽器中跑了,點進來看看,竟然真有所發現

他把上面那位老哥想做的事給做出來了,起一個服務,然後通過web api去生成PDF檔案,來看看原始碼,然後我找到下面一段程式碼

原來我們是少執行了一個percollate.configure的方法,加上之後,執行一波

這下真的美滋滋了,當然這樣生成的PDF檔案使用的是預設配置,如果你想生成適配你手機或者閱讀器的PDF檔案,就需要新增你的自定義配置了,

 percollate.pdf(urlList, {
      output: "阮一峰JavaScript教程.pdf",
      css: "@page { size: A6 landscape } html { font-size: 18pt } "
    });
複製程式碼

關於css屬性的文件,點選檢視

完整程式碼

新建一個util.js,增加一個用於傳送請求的方法:

const request = require("request");

function parseBody(url) {
  return new Promise((resolve, reject) => {
    request(url, (error, res, body) => {
      if (!error && res.statusCode === 200) {
        resolve(body);
      } else {
        reject("獲取頁面失敗" + error);
      }
    });
  });
}

module.exports = {
  parseBody
};
複製程式碼

新建config檔案,新增配置

// 阮一峰JS教程
const javaScriptCourse = {
  url: "https://wangdoc.com/javascript", // 要爬取的網站地址
  name: "阮一峰JavaScript教程.pdf", // 匯出的檔名字
  wrapEle: ".menu-list", //  導航父元素的class
  css: "@page { size: A6 landscape } html { font-size: 18pt } ", // 生成pdf的大小和字型
  getUrlList(body, ele, url) {
    // 從返回的html中獲取章節地址
    let urlList = [];
    $(body)
      .find(ele)
      .eq(0)
      .find("li a")
      .each((i, v) => {
        const pathStr = $(v).attr("href");
        const path = pathStr.slice(pathStr.indexOf("/"));
        urlList.push(url + path);
      });
    return urlList;
  }
};
複製程式碼

新建index.js為專案的入口檔案,引入相關依賴

const request = require("./util"),
  percollate = require("percollate"),
  markdownpdf = require("markdown-pdf"),
  fs = require("fs"),
  { javaScriptCourse, es6Course, baseOpt } = require("./config");

const getHtml = url => {
  return request.parseBody(url);
};

const getJSCourse = () => {
  const { url, name, wrapEle, getUrlList, css } = javaScriptCourse;

  getHtml(url).then(res => {
    const urlList = getUrlList(res, wrapEle, url);
    percollate.configure();
    percollate.pdf(urlList, {
      output: name,
      css
    });
  });
};

// 生成pdf檔案
getJSCourse()
複製程式碼

以上是全部程式碼,總共不超過80行,執行之後,我們能看到終端列印的日誌

成功之後,在專案的目錄下就能看到生成的pdf檔案

將Markdown檔案生成PDF

這個以阮一峰大神ES6教程為例,地址為:es6.ruanyifeng.com ,開啟網站後,我們發現,網站是通過介面動態生成內容的,網站請求返回的內容都為Markdown,

我們略過抓取文章地址的過程,詳情可在文章附上的原始碼中檢視,執行的轉化程式碼為

percollate.configure();
    percollate.pdf(urlList, {
      output: name,
      css: baseOpt.css,
      sandbox: true // 設定為false,動態生成的內容抓取不到
    });
複製程式碼

生成的PDF檔案如下,沒有轉成我們希望的樣子,內容為原始的Markdown語法

到此,我沒有再研究 percollate新增某個配置之後,是否就可以完美的將Markdown轉成PDF檔案,因為我知道Node有一個包markdown-pdf可以將Markdown轉成PDF檔案,還知道VScode有一個外掛也可以將Markdown轉成PDF檔案,這樣的話,我們首先要生成一個包含所有內容的Markdown檔案,Node的fs模組可以很容易的完成這件事情,生成Markdown檔案以後,再使用上面講述的兩種方法將Markdown轉成PDF即可,程式碼如下

 const urlList = getUrlList(res, wrapEle, url);
    
    const reqList = [];
    urlList.forEach(v => {
      console.log("請求地址---", v);
      reqList.push(getHtml(v));
    });
    console.log("開始發出請求...");

    Promise.all(reqList)
      .then(arrRes => {
        console.log("所有請求都成功了---");
        const md = arrRes.join(" ");
        // console.log(md);
        const optPath =
          "/Users/apple/Documents/my/LearningLog/NodeJs/網頁生成pdf/";

        fs.writeFileSync(`${name}.md`, md, function(err) {
          if (err) {
            return console.error(err);
          }
          console.log("資料寫入成功!");
        });
        console.log("開始生成pdf檔案...");
        markdownpdf({
          paperFormat: "A6"
          // paperOrientation: "landscape"
        })
          .from(`${optPath}${name}.md`)
          .to(`${optPath}${name}.pdf`, function() {
            console.log("生成pdf檔案成功");
          });
      })
      .catch(err => {
        console.log("請求報錯---", err);
      });
複製程式碼

關於使用VScode將Markdown檔案轉為PDF的方法,我這裡就不贅述了,參考markdown-preview-enhanced

寫在最後

本文的所有程式碼以及生成的PDF檔案都在下面的地址,後續會更新更多的大佬免費教程的PDF檔案

點此檢視原始碼,喜歡的Star或者Fork喲

PS: 書中自有黃金屋,書中自有顏如玉,為了前面提到的夢想,趕緊擼起來!