jonvi 发表于 2017-2-23 11:53:39

博客园博文备份小记

  夏天呆在空调房里,看看书,撸撸代码,写写文章,还是蛮爽的。
  最近花时间写了个博客园博文备份脚本,简单又好玩,与大家分享下。
  之前有玩过一段时间的 node,几乎是一直在做爬虫,纯粹是因为好玩嘛,然后看到张大大的 这篇文章,感觉 fs 模块也是值得一撸的,就想找个 demo 试试手,马上就想到了给博客园博文做备份。
  其实我本人一直有备份的好习惯,博客园博文备份的地址是 https://i.cnblogs.com/BlogBackup.aspx,但是备份结果是个 xml 的文件,这个用户体验就不好了吧,用过简书的备份,down 下来的就是原始的 markdown 文件或者 html 文件(如果用的是富文本编辑器的话),从 xml 提取元素生成 md/html 文件,不正好来练手下 fs 模块嘛!
  fs 模块其实就是 node 的文件系统模块(FileSystem),提供了对于文件一系列的操作 API,比如创建文件啊,创建路径啊,遍历文件,修改文件名啊,等等,反正你能想到的对于文件的操作,它都有,具体的 API 可以参考 官网,也可以看下 中文文档。
  我们先分析下 xml 文件。

  其实非常简单就能看出来,每一篇文章都在一个 <item> 标签中,里面有各种子元素,代表各种信息,比如 <title> 内包裹的就是文章><link> 是文章链接,<description> 就是正文内容啦,我们的目标就是把正文内容提取出来,保存为 markdown 文件就 ok 啦!等等,我以前写的还有不少需保存为 html 格式的,这就尴尬了,还需要引入相应的 css 才能保证在本地打开样式不出错,所以还没开始用 markdown 写文章的童鞋,强烈建议现在就开始学习 markdown!
  开始写代码啦。首先我们要读取 xml 文件,获取文件内容,我们用的是 fs 的 readFile API,回调中可以获取到读取的内容。对于读取到的 string,我们希望可以用类似 dom 选择器去选取,cheerio 又派上了用场。
fs.readFile(xmlSrc, {encoding: &quot;utf-8&quot;}, function(err, data) {  var $ = cheerio.load(data);
  });
  
})

  接下去我们遍历每一个 <item> 元素,然后提取其下的 <description> 元素中的内容,作为文章正文,我们把文章>  这个过程不复杂,但还是有几点需要注意下。


[*]<description> 并不完全就是文章正文,还需要过滤 <!--]--> 两个子串,很好找,在首尾,一眼就看出来了
[*]  因为我把文章标题当做了文件名,但是有些字符可以在标题中,但不能在文件名中(我是 windows 系统,不清楚别的系统是什么规则),用正则直接过滤了。
// 文章标题
  
var>  
// 过滤非法字符

  
title =>
[*]  之前说了,有 markdown 和 html 两个类型的文件,都在 <description> 的标签内,看了下似乎没有别的判断方式,只能根据标签实体内容判断,简单写了个判断,对于我的文章貌似够用了。
// 判断是 HTML 还是 markdown  
isHTML = (content.indexOf(&quot;style&quot;) !== -1 || content.indexOf(&quot;class&quot;) !== -1)
  && content.indexOf(&quot;![]&quot;) === -1;
  关于 html 格式的文件,如果要在本地正常打开,最好能备份相应的 css,但是由于对内容不造成影响,就没做这一步。

[*]  关于文件的存储路径。这个可以自由发挥,简书的做法是把所有博文都备份在了一级目录下,但是由于我个人的文章可能比较多,我把文章按月来分了,即年份命名的文件夹为二级目录,比如有 2016,2015 以及 2014 三个二级目录(对,我写博客两年左右,跨了三年了),然后每个文件夹下又有具体月份,比如 1 月的文章放一个文件夹,二月的放一个,出于程序员的习惯,我把它们按照 0-11 编号了。如何知道文章发表的日期?<pubDate> 标签内的数据分析下就 ok 了。对于判断路径是否存在,我们可以用 fs.existsSync,而创建路径,可以用 fs.mkdirSync,这些 API 的使用都非常的简单,可以看文档。

  最后还有一点要提的是,我把文章的图片也备份了,然后将正文中图片的 src 替换成了本地的地址,关于图片的下载,没有现成的 API,简单找了个函数,仅支持 HTTP 下的,对于博客园已经够用了。
// to download picture  
function download(url, savePath) {
  var req = http.get(url, function(res){
  var binImage = '';
  res.setEncoding('binary');
  

  res.on('data', function(chunk){
  binImage += chunk;
  });
  

  res.on('end', function(){
  if (!binImage) {
  console.log('image data is null');
  return null;
  }
  

  fs.writeFile(savePath, binImage, 'binary', function(err){
  if (err) throw err;
  console.log('It\'s saved!'); // 文件被保存
  });
  });
  });
  
}
  备份完后目录结构大概是这样的:
  

cnblogs|  --|2014
  --|9
  --|greasemonkey常见问题
  --|greasemonkey常见问题.html
  --|用php自动发邮件的简单实现
  --|用php自动发邮件的简单实现.html
  --|img
  --|10
  --|chrome扩展制作之hello world篇
  --|chrome扩展制作之hello world篇.html
  --|img
  

  ...
  

  --|2015
  

  ...
  

  --|2016
  

  ...
  

  详细的代码和使用方式可以参考 https://github.com/hanzichi/funny-node/tree/master/cnblogs-backup,有兴趣的也可以自己撸一个适合自己的。更深入的话,可以定时从 cnblogs 下载 xml 文件,定时备份,就更自动化了。
  ps:fs 模块真是太实用了,之前维护这个 repo https://github.com/hanzichi/leetcode 的时候,每做一道题都要更新下 README,实在苦不堪言,现在直接用 fs 写了个 gererate.js,简直爽爆!
  
页: [1]
查看完整版本: 博客园博文备份小记