首页 > 技术文章 > 博客原文备份小记——博客园

wanqi007 2016-08-06 19:45 原文

/博客园博文备份小记

 

   夏天呆在空调房里,看看书,撸撸代码,写写文章,还是蛮爽的。

   最近花时间写了个博客园博文备份脚本,简单又好玩,与大家分享下。

   之前有玩过一段时间的 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> 内包裹的就是文章 title,<link> 是文章链接,<description> 就是正文内容啦,我们的目标就是把正文内容提取出来,保存为 markdown 文件就 ok 啦!等等,我以前写的还有不少需保存为 html 格式的,这就尴尬了,还需要引入相应的 css 才能保证在本地打开样式不出错,所以还没开始用 markdown 写文章的童鞋,强烈建议现在就开始学习 markdown!

开始写代码啦。首先我们要读取 xml 文件,获取文件内容,我们用的是 fs 的 readFile API,回调中可以获取到读取的内容。对于读取到的 string,我们希望可以用类似 dom 选择器去选取,cheerio 又派上了用场。

fs.readFile(xmlSrc, {encoding: "utf-8"}, function(err, data) {
  var $ = cheerio.load(data);
  
  });
})

接下去我们遍历每一个 <item> 元素,然后提取其下的 <description> 元素中的内容,作为文章正文,我们把文章 title 当做文件名。

这个过程不复杂,但还是有几点需要注意下。

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

    // 文章标题
    var title = $(item).find("title").text();
    // 过滤非法字符
    title = title.replace(/(\/|\\|\:|\*|\?|\"|\<|\>|\|)/g, "");
  • 之前说了,有 markdown 和 html 两个类型的文件,都在 <description> 的标签内,看了下似乎没有别的判断方式,只能根据标签实体内容判断,简单写了个判断,对于我的文章貌似够用了。

    // 判断是 HTML 还是 markdown
    isHTML = (content.indexOf("style") !== -1 || content.indexOf("class") !== -1)
      && content.indexOf("![]") === -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,简直爽爆!

分类: Node.js
 
好文要顶 关注我 收藏该文  
 
 

推荐阅读