基于MetaWeblog的博客信息抓取——C++博客迁移
作者: deercoder
最近准备把博客整体迁移到GitHub上面,但是由于本博客的内容较多,因此想找个办法进行迁移,无奈虽然C++博客提供了备份的功能,但是保存的XML文件不是标准的Wordpress的XML格式,因此还是得自己进行手动迁移,无奈过去记录的水文较多,弃之可惜,因此就想着如何进行博客迁移,如何抓取博客内容等一系列的问题。
正好,在网上搜索的时候,发现CSDN等网站提供MetaWeblog的标准接口用于第三方程序的接入,书写博客,发布,删除等一系列操作,而C++正好提供了相关接口,只需要登录到你的账户,在选项--配置中,选择允许Web Service以及MetaWeblog,并访问下面提供的MetaWeblog接口即可。
为此,好好研读了一下C++博客提供的接口,基本功能都已经提供了API接口说明,如writePost,getPost等等,对于其中用到的结构体,如Post等,也定义了相关的成员变量的组成,类型等等,因此,实际上是比较好做的。为此,我就开始动手,编写自己的博客迁移工具了。
首先确定使用的平台和技术,经过研究,决定使用Apache XML-RPC库,并使用Java实现。
其次,编写对应的函数和功能,初期开发讲究前期验证,为此先凌乱的写了几个基本函数,写死一些变量和定义,以及所需要的Key,然后利用自己博客的一篇文章进行验证,证明能够获取到对应的信息,最后才保存到HTML文件中,打开后发现所需要的基本内容都完好保存,因此可以进行后续工作。
后续,主要进行扩展功能的展开,包括对其他接口的使用,如获取近期所有博客,获取分类信息,获取博客信息等等,另外,经过重构后,将相关的工具函数都转移到一个类中实现,并尽量做到代码复用,这个过程经历了几次,渐渐发现最初的代码惨不忍睹,而重构之后,将不变的如标记字段,服务商提供地址等等,都固定在某个类中,一旦需要应用到其他平台,如CSDN博客,只需要做最小的修改即可,方便代码复用。
最后,进行了简单测试,并发布到Github中进行版本管理和备份,填写相关log,后续可能会继续做二次开发。
上面只是简单介绍使用的技术和开发流程,至于具体的细节实现,下面就几个重点来讲解:
1. 如何发起网络连接,并从服务器那边获取到所需要的博客信息?
利用Apache的XML-RPC库实现,非常简单,只需要进行简单配置即可,代码如下:
config = new XmlRpcClientConfigImpl();
client = new XmlRpcClient();
config.setServerURL(new URL(url));
client.setConfig(config); 这样,就完成了客户端的初始化和配置工作,此后,就可以直接利用此客户端来发现连接请求,获取对应的信息了。
List params = new ArrayList();
params.add("test");
params.add(USER_NAME);
params.add(USER_PASSWORD);
// 必須使用Object數組,List或者其他數組不行
Object[] arr = (Object[])client.execute(GET_BLOGS_METHOD, params);发起一次请求时,只需要先配置好参数,根据每一个API文档定义的函数参数,放入到一个数组中,然后执行对应的函数,函数名使用GET_BLOGS_METHOD之类的保存下来,即可获得对应的结果。
2. 如何处理获得的结果?
由于类型都是由API文档中定义的,比如一篇博文的信息定义为一个Post对象,而这个对象是它定义的类,包含有标题,时间等成员,成员的类型还可能不同,比如大部分是String类型,但是也有如Date时间这样的类型。如何获取呢?
对于一个Post对象,获得其实就是一个个键值对,比如Key为“title”,value为对应的值“我的博客文章”等等,因此,可以使用Java中的Map来保存一个个键值对,但是这里我们会遇到一个疑问,Post对象既然是按照一个个键值对来保存,如
{
"title", "我的博客标题"
"description", "我的博客正文"
"date", "博客发布时间"
}
那么,可否就直接用Map来保存每一个键值,然后根据键来分别读取信息呢?答案是否定的,因此并非所有的类型都是统一的,Key当然都是String类型,因为文档中已经定义好了,但是Value却可能不同,如Date对应的值是一个Date类型,而title对应的值是String类型,而他们都是隶属于Post对象的,如何来处理呢?
这里,我使用的是Map来存放每一个键值对,然后根据类型获取信息,代码如下:
String title = getPostTitle(result);
if (title != null){
System.out.println(title);
saveString += title + "
"; // 文章标题,并以HTML形式存放
}
Date date = getPostDate(result);
if (date != null) {
String dateString = getPostTime(date);
System.out.println(dateString);
saveString += dateString + "
";
}
String article = getPostArticle(result);
if (article != null) {
System.out.println(article);
saveString += article + "
";
}当然,这里用到了子函数实现,但实际的效果就是,根据API文档中的类型,在获得value的时候,将Object类型强制转换成需要的类型,比如,key为title的时候,知道值应该是String,因此就将值转换成String类型。Key位datedTime的时候,值的类型应该是Date类型,就将它转换成Date类型。这样,最终就可以完全读取出所有信息了。 www.zzzyk.com
而对于返回值为结构体数组的,同样用Object数组存放,然后读取每一个数组成员,强制转化成Map类型,保存一个结构体的所有键值对,然后根据结构体定义来逐个读取对应的值,当然,如果结构体全部都是String类型,就可以直接使用Map来保存并读取了。
基本上来说,上面应该是这些程序中的主要难点了,解决之后,大部分问题也都可以完成,至于写博客啥的,原理都是相同的,既然所有的信息都能够读取,就只需要赋值相关的语句,执行一个对应的函数而已了。
当然,在写代码的过程中,发现重构非常重要,虽然代码量不大,但是从历史上来看,变化还是非常大,后期的代码以及比前面的耦合度要低很多,而且对于固定不变的内容等都放在类成员中,便于继承以及代码复用,有兴趣的朋友可以试试CSDN等博客的相关功能。
附上最终实现的输出结构:
4765
http://www.cppblog.com/deercoder/
我的编程乐园
Category Description: ACM
Category HTML Url: http://www.cppblog.com/deercoder/Category/17069.aspx
Category RSS Url: http://www.cppblog.com/deercoder/rss.aspx?catid=17069
Category Title: ACM
Category ID: 17069
Category Description: Android
Category HTML Url: http://www.cppblog.com/deercoder/Category/17867.aspx
Category RSS Url: http://www.cppblog.com/deercoder/rss.aspx?catid=17867
Category Title: Android
Category ID: 17867
Category Description: C++
Category HTML Url: http://www.cppblog.com/deercoder/Category/13117.aspx
Category RSS Url: http://www.cppblog.com/deercoder/rss.aspx?catid=13117
Category Title: C++
Category ID: 13117
Category Description: CTeX和LateX
Category HTML Url: http://www.cppblog.com/deercoder/Category/13991.aspx
Category RSS Url: http://www.cppblog.com/deercoder/rss.aspx?catid=13991
Category Title: CTeX和LateX
Category ID: 13991
Category Description: Git
Category HTML Url: http://www.cppblog.com/deercoder/Category/18145.aspx
Category RSS Url: http://www.cppblog.com/deercoder/rss.aspx?catid=18145
Category Title: Git
Category ID: 18145
补充:软件开发 , C++ ,