作者: 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.2cto.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