注意
新加入抓取 Microsoft Azure Architecture Center 文章的程序。程序调用方式已经变更,如果按照些说明文档操作抓取 Brendan Gregg 文章,请 git reset --hard d8cc3c2
。
简介
事情是这样的,我的一位朋友是 Brendan Gregg 的粉丝(啊,我也是),想把他的 blog 保存成 PDF,放到 kindle 上随时研读,群里讨论起来,就聊起来有没有一些好的办法能够把 130 篇文章由 HTML 转成 PDF。
简单想了下,要解决这个问题,有三个步骤:
- 拿到 130 篇 blog 文章的 URL
- 将每篇文章由 HTML 转换成 PDF
- 最后再将转换后的 PDF 合成一个大的 PDF 文件
重点在于前两步。
拿到所有 blog 文章的 URL
第一步,拿到一个网站或者多个网站的所有 URL,本质上是一个爬虫问题。 wget 是一个非常好用的下载工具,除了下载单个文件,wget 还可以下载一整个网站并将网站的链接转换成本地链接。
我们用下面的命令拿到 brendan gregg 网站的所有链接:
wget --spider -r http://www.brendangregg.com/blog/ 2>&1 | grep '^--' | awk '{ print $3 }' | grep -v '\.\(css\|js\|png\|gif\|jpg\|JPG\)$' > /tmp/urls.txt
我们发现 brendan gregg 网站的 blog 的 URL 非常有规律:
http://www.brendangregg.com/blog/2008-12-02/a-quarter-million-nfs-iops.html
http://www.brendangregg.com/blog/2008-12-15/up-to-2gbs-nfs.html
http://www.brendangregg.com/blog/2008-12-15/up-to-2gbs-nfs.html
http://www.brendangregg.com/blog/2009-01-09/1gbs-nfs-from-disk.html
http://www.brendangregg.com/blog/2009-01-09/1gbs-nfs-from-disk.html
于是我们用如下的命令过滤出所有 blog 文章的 URL:
cat /tmp/urls.txt | grep 'blog/2' | grep '.html$' | sort | uniq > blog.txt
到此,第一步完成。
将文章由 HTML 转换成 PDF
接下来,我们要将 130 篇文章全部由 HTML 转换成 PDF。
显然,手工做是不可以滴。批量转换必须用到 headless browser。
前几年 headless browser 的事实标准是 PhantomJS,不过后来 Chrome 团队放了个大招 puppeteer,基本上算宣告了 PhantomJS 寿终正寝。
我们可以用下面的代码将 HTML 网页转存成 PDF:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
await browser.close();
})();
略微封装一下,我们可以将 130 篇 blog 文章全部转换成 PDF。
需求注意的问题:
- JS 中写异步代码并不是特别舒服,如有可能,用
async
/await
的方式,写起来舒服很多 - Puppeteer 本质上是一个 chrome,页面多的话相当耗资源,因此 HTML 转存 PDF 的时候需要控制下频率,每次截图之后关闭 page,并
sleep(10000)
。我第一版的代码没有注意到这个问题并且为了调试同时 disable 了 headerless 选项 (const browser = await puppeteer.launch({headless: false}
),直接导致电脑内存耗光,出现了数十个 chromium 共存的感人画面:
完整的截图代码在这里。
运行:
yarn
mkdir output
node index.js
合并 PDF
大概花十几分钟的样子,我们可以拿到约 130 篇文章的 PDF,接下来我们将 130 篇 PDF 合并成一个大的 PDF 文件,这样可以方便在移动设备上管理和阅读。
合并 PDF 的工具相当多,专业的如 Adobe Acrobat,命令行的工具有 PDFtk。
Mac 上可以用 ghostscript
brew install ghostscript
合并 PDF 可以用类似下面的命令:
gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf pdf1.pdf pdf2.pdf
我们用 ls -t
命令列出所有的 PDF 文件,并按照文件的 modifed time 进行排序(man ls
命令
查看 -t
参数的含义)。
如此,我们得到下面的命令:
gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf `ls -t`
大约一两分钟的样子,130 个 PDF 合并成一个 640+ 页的 PDF。我提供两种版本下载:
思考
一呢,其实如果将 PDF 放到 kindle 这种小尺寸的设备上阅读的话,puppeteer 的截图参数还可以再改一下。我在程序里的设定是按照 A4 尺寸转换成 PDF,如果放到 kindle 上阅读,用 A5 的尺寸转 PDF 也许阅读效果会更好一点,这个时候,web 这种流式排版——流式排版这个名词是我自创的,嗯——在适配不同尺寸设备方面就显示出了巨大的优势。
二,如果做一个类似的 web 服务,会有人愿意买单么?
三,工具的强是无敌的……