随笔-145  评论-173  文章-70  trackbacks-0
最近准备把博客整体迁移到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<String, String>来保存每一个键值,然后根据键来分别读取信息呢?答案是否定的,因此并非所有的类型都是统一的,Key当然都是String类型,因为文档中已经定义好了,但是Value却可能不同,如Date对应的值是一个Date类型,而title对应的值是String类型,而他们都是隶属于Post对象的,如何来处理呢?
这里,我使用的是Map<String, Object>来存放每一个键值对,然后根据类型获取信息,代码如下:
        String title = getPostTitle(result);
        if (title != null){
            System.out.println(title);
            saveString += title + "<br/>"; // 文章标题,并以HTML形式存放
        }

        Date date = getPostDate(result);
        if (date != null) {
            String dateString = getPostTime(date);
            System.out.println(dateString);
            saveString += dateString + "<br/>";
        }

        String article = getPostArticle(result);
        if (article != null) {
            System.out.println(article);
            saveString += article + "<br/>";
        }
当然,这里用到了子函数实现,但实际的效果就是,根据API文档中的类型,在获得value的时候,将Object类型强制转换成需要的类型,比如,key为title的时候,知道值应该是String,因此就将值转换成String类型。Key位datedTime的时候,值的类型应该是Date类型,就将它转换成Date类型。这样,最终就可以完全读取出所有信息了。
而对于返回值为结构体数组的,同样用Object数组存放,然后读取每一个数组成员,强制转化成Map<String, Object>类型,保存一个结构体的所有键值对,然后根据结构体定义来逐个读取对应的值,当然,如果结构体全部都是String类型,就可以直接使用Map<String, String>来保存并读取了。

基本上来说,上面应该是这些程序中的主要难点了,解决之后,大部分问题也都可以完成,至于写博客啥的,原理都是相同的,既然所有的信息都能够读取,就只需要赋值相关的语句,执行一个对应的函数而已了。
当然,在写代码的过程中,发现重构非常重要,虽然代码量不大,但是从历史上来看,变化还是非常大,后期的代码以及比前面的耦合度要低很多,而且对于固定不变的内容等都放在类成员中,便于继承以及代码复用,有兴趣的朋友可以试试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

博客文章等保存为HTML文件,贴出HTML源码太大,就不帖了。

以上,就是基于MetaWeblog的博客信息读取和迁移工作的前期工作,也是最基本的工作,后期,主要将这些函数整合并利用,就可以实现所有信息的本地备份,批量写博,批量删除,批量导入等工作了。

最后,说说目前本工程的实现功能情况:
1. 根据一篇文章的博客ID,抓取对应的内容,标题和发布时间等信息(其余信息可自行添加,采用类似 的办法,因本人只对这写信息感兴趣) 
2. 获取所有的分类信息和Rss订阅信息
3. 获取博客信息
4. 获取指定数量的近期发布文章
5. 保存为HTML页面
基本上,抓取博客所提供的API函数功能都已经实现,接下来要做的是,利用这些函数,实现循环的遍历所有的博客ID,然后全部读取出来,并保存,这些工作应该就是一些功能的组合和接口设计了。

该工程已经发布到Github中,地址为:
https://github.com/deercoder/blogMover,欢迎大家拍砖,指导。


posted on 2012-06-10 00:07 deercoder 阅读(1865) 评论(0)  编辑 收藏 引用 所属分类: Java

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理