前言
你想一夜暴富嗎?你想一夜成名嗎?你想開蘭博基尼泡妞嗎?你想拿鈔票點菸嗎?你想成為世界主宰嗎?不,我不想,我只想把我喜歡的教程轉成PDF檔案,放到我的手機或者閱讀器中,什麼?你也想,那來吧,本文將介紹:
- 通過命令列將某網站的內容轉成PDF檔案
- 通過NodeJS爬蟲將某網路教程(例如阮一峰的JavaScript教程和ES6教程等)轉成PDF檔案
- 通過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檔案
已生成的免費網路教程PDF檔案:
如需調整大小、字型或者樣式,請fork原始碼自行生成。