关于javascript:在线博客转PDF电子书-JS爬虫初探

25次阅读

共计 3236 个字符,预计需要花费 9 分钟才能阅读完成。

最近在看一位大佬写的源码解析博客,平时上下班用手机看不太得劲,然而平板又没有网卡,所以就想搞个离线 pdf 版,不便通勤工夫学习浏览。

所以,问题来了: 怎么把在线网页内容转成 pdf?

这位大佬的博客是用 gitbook 写的,我先上网搜了下工具,发现大多是将本人本地 gitbook 转 pdf,只有一个开源工具是用 python 爬取的在线 gitbook,然而一看 issues,中文乱码、空白页、看不到图等等问题都没解决,遂放弃……

通过我不懈的搜寻,终于找到了一个能够间接把网页保留成 pdf 工具:phantomjs

phantomjs 是一个无界面的,可脚本编程的 WebKit 浏览器引擎,也就是俗称的“无头浏览器”,罕用于爬取网页信息。

下载地址:https://phantomjs.org/downloa…

我用的是 win 零碎,下载后在 bin 目录下找到.exe 文件,而后新建如下 js 脚本:

// html2pdf.js
var page = require('webpage').create();
var system = require('system');

if (system.args.length === 1) {console.log('Usage: loadspeed.js <some URL>');
    // 这行代码很重要。但凡完结必须调用。否则 phantomjs 不会进行
    phantom.exit();}
page.settings.loadImages = true;  // 加载图片
page.settings.resourceTimeout = 30000;// 超过 10 秒放弃加载
// 截图设置,//page.viewportSize = {
//  width: 1000,
//  height: 3000
//};
var address = system.args[1];
var name = system.args[2];
page.open(address, function (status) {function checkReadyState() {// 期待加载实现将页面生成 pdf
        setTimeout(function () {var readyState = page.evaluate(function () {return document.readyState;});

            if ("complete" === readyState) {page.paperSize = { width: '297mm', height: '500mm', orientation: 'portrait', border: '1cm'};
                var timestamp = Date.parse(new Date());
                var pdfname = name;
                var outpathstr = "C:/Users/Desktop/pdfs/" + pdfname + ".pdf"; // 输入门路
                page.render(outpathstr);
                console.log("生成胜利");
                console.log("$" + outpathstr + "$");
                phantom.exit();} else {checkReadyState();
            }
        }, 1000);
    }
    checkReadyState();});

控制台 cd 进入 bin 目录,命令行执行:
phantomjs html2pdf.js http://xxx.xx.xxx(博客所在地址)
即可将该网页转成一个 pdf。

这时候问题又来了:

  1. phantomjs 不能主动截取下一页;
  2. 每一个网页都只能生成一个 pdf,最初还要找工具把所有 pdf 合并成一个;
  3. 保留的实际上是网页的截图,对于侧边栏、顶部栏、底部等我不须要的内容也会保留到页面中,不能很好地适配。

思考和察看得出解决办法:

  • 这个博客的网址除了域名对立外并没有其余法则,只能手动保护一个 url 列表,而后通过脚本遍从来解决第 1 个问题;
  • pdf 合并工具网上有现成的,第 2 个问题也能解决;
  • phantomjs 是能够对 dom 进行操作的,然而有个问题,页面里如果有异步申请的资源,比方图片,就须要提早截图工夫,否则会呈现很多空白区域,具体解决办法能够参见这篇博客:应用 phantomjs 操作 DOM 并对页面进行截图须要留神的几个问题。

问题尽管是能解决,然而过于麻烦,而且这个 dom 操作并不能在截图的时候去掉多余内容。

通过下面一系列骚操作,我受到了启发,从而有了一个全新的思路:

通过 dom 操作,把整个博客的内容都爬到一个 html 文件中,再把这个 html 文件转成 pdf。

话不多说,间接开撸。

为了避开浏览器同源网络策略,我基于之前搭建的 node+express 本地服务,并引入插件 cheerio(用于 dom 操作)、html-pdf(用于将网页转成 pdf)来实现。

首先,察看须要爬取的 dom 元素的特点:
我须要爬取的内容如图所示

这部分的内容能够通过.theme-default-content 款式获取到:

https.get(url, function (res) {
    var str = "";
    // 绑定办法,获取网页数据
    res.on("data", function (chunk) {str += chunk;})
    // 数据获取结束
    res.on("end", function () {
      // 沿用 JQuery 格调,定义 $
      var $ = cheerio.load(str);
      // 获取的数据数组
      var arr = $(".theme-default-content");
      var main = "";
      if (arr && arr.length > 0) {main = arr[0]
      }
    })

通过这段代码失去的 main 就是咱们要获取的主体 dom。

其次,察看图片资源的 url:

这里用的是相对路径,所以须要对图片门路进行解决:

// 将下面失去的 main 转为字符串便于解决
main = $.html(main)
// 对图片门路进行补全
main = main.replace(/src=\"\/img/g,"src=\""+ prefixUrl +"/img")

察看下一页的 url 地址,都是在一个款式名为 nextspan标签内:

获取下一页内容的代码如下:

var $ = cheerio.load(str);
var arr = $(".next");
var nextData = "";
if (arr && arr.length > 0) {nextData = arr[0]
    if (nextData && nextData.children && nextData.children[0] && nextData.children[0].attribs) {// 下一页地址:prefixUrl + nextData.children[0].attribs.href
  } else {// 没有下一页}
}

最初还须要把 html 转成 pdf:

function htmlTopdf() {var html = fs.readFileSync(path.resolve(__dirname, htmlFileName), 'utf8');
  var options = {format: 'A4'};
  pdf.create(html, options).toFile(path.resolve(__dirname, pdfFileName), function (err, res) {if (err) return console.log("error message", err);
    console.log(res);
  });
}

总结一下 实现思路

  1. 通过 dom 操作抓取到主体内容,并对其中的图片等资源进行解决,而后保留到 html 文件中;
  2. 找到下一页的 url,将下一页的主体内容持续拼到 html 文件后;
  3. 最初将 html 转成 pdf 保留。

下面的代码不是通用的,然而如果要抓取其余网页的话,思路根本都是这三步。

Demo 已开源:https://github.com/youzouzou/node-crawler/blob/main/routes/index.js

npm install后关上http://localhost:3009/ 即可生成 pdf。

正文完
 0