Hello World
Spiga

一次批量修改博客文章的经验(上):准备工作

2010-01-04 18:57 by 老赵, 6595 visits

这几天赋闲在家,除了看书和还债(如RSS订阅),终于把一直以来想做却拖着的事情完成了:批量去除博客文章段首的空格。这个过程并不难,只需要按部就班地去做就行了,一切资料都可以在互联网上搜索到。不过我还是打算记录一下,也是为了今后再做类似工作时有个参考,少走一些弯路。

前言

我是个略有些强迫症的人,希望很多东西可以统一。例如,几个月前我才在RSS订阅里输出了全文——那是因为博客园终于提供这个统一设置的选项了。其实在此之前就有很多朋友建议我开放全文,但我一直没有做。不是我追求PV,而是我只能做到所有的新文章输出全文,对于旧文章则必须一篇一篇地去修改——如果不修改,不就不统一了吗?但手动修改实在太繁琐,于是便一直没有去做。

同样的,在我以前的文章中,每段段首都是空两格的,但是现在感觉没有什么必要,于是最近的几十篇文章都顶格写了。这个“不统一”我便“容忍”了,因为我知道博客园提供了MetaWeblog API,这样我理论上可以写一段程序来批量修改之前的文章内容。只可惜,直到现在我才下决心这么做。

整个过程分几步完成,在此一一记录一下。

XML RPC与MetaWeblog

MetaWeblog是一个通用的博客内容修改接口,许多博客都实现了协议,因此我们可以使用Windows Live Writer这样的工具来写文章。网上关于这个协议最好的描述文档我认为是MSDN上的MetaWeblog API Reference——这其实是Windows Live Space服务所公开的接口。从理论上来说,博客园也应该实现完全相同的功能,但是实际使用上来看,还是有一些区别。由于任务的性质,这里我们自然以博客园为准,我也不再去追究到底谁是真正符合标准的做法了。

MetaWeblog API使用了基于XML RPC的调用方式。XML RPC使用HTTP来传输一段XML来表示一个远程调用,与SOAP不同,XML RPC非常简单,他的传输内容您一看就懂。我只是在开发过程中使用Fiddler简单查看了一下Windows Live Writer与博客园的通信,如果您感兴趣的话也可以仔细研究一下XML RPC。

在.NET上调用XML RPC服务可以利用开源的XML-RPC.NET类库来简化操作。虽然MSDN上提供了一段基于XML-RPC.NET的演示代码,但是您也可以看出其实这段代码无比粗略,而且我根本没跑通,差点让我认为XML-RPC.NET非常不成熟。但我看了XML-RPC.NET的文档之后才意识到,其实这个类库使用起来非常简单。因此,我在这里建议您忽略MSDN上的示例代码,而以XML-RPC.NET为准——甚至只要首页上的几行代码您就可以明白了。

当然,您也可以继续阅读这篇文章,用于完成简单的工作已经足够了。

使用MetaWeblog API修改博客园文章

使用XML-RPC.NET调用MetaWeblog API非常容易,我们只要根据API的样式来定义“类型”和“接口”就可以了。例如:

public class Post
{
    [XmlRpcMember("postid")]
    public int PostID;

    [XmlRpcMember("dateCreated")]
    public DateTime CreateTime;

    [XmlRpcMember("title")]
    public string Title;

    [XmlRpcMember("description")]
    public string Content;

    [XmlRpcMember("categories")]
    public string[] Categories;
}

public interface IMetaWeblogProxy : IXmlRpcProxy
{
    [XmlRpcMethod("metaWeblog.getPost")]
    Post GetPost(string postId, string userName, string password);

    [XmlRpcMethod("metaWeblog.editPost")]
    bool UpdatePost(string postId, string userName, string password, Post post, bool publish);
}

首先我们定义了一个Post类型,其中有多个字段,每个字段上标记了在XML RPC结构中的名称。此外,我们又定义了一个IMetaWeblogProxy接口类型,XML-PRC.NET会根据根据接口的签名自动与远程服务进行交互。这里有个问题,便是Post的PostID字段到底是哪个类型。MSDN上描述,与Windows Live Space的API使用的是字符串,而博客园使用的确实32位整数——如前文所述,我们这里以博客园为准。

调用接口很容易:

var client = XmlRpcProxyGen.Create<IMetaWeblogProxy>();
client.Url = "http://www.cnblogs.com/JeffreyZhao/services/metaweblog.aspx";

string userName = "JeffreyZhao";
string password = "...";
string postId = "1629216";

var post = client.GetPost(postId, userName, password);
post.Content += "<p>Hello World!</p>";
client.UpdatePost(postId, userName, password, post, true);

以上,便在ID为1629216的文章内容后添加一行“Hello World”字样了。

异步调用

F#并不是万能的,之前我们也看到说,由于F#缺少诸多语言特性,在XML构造方面的方便程度并不如C#。但是,我这里还是选择使用F#,关键的因素还是在于其异步支持实在是太方便了。我们的任务需要大量的网络IO操作,而其中高性能的关键还是在于利用异步IO操作。那么在F#中,我们又该如何异步访问MetaWeblog API呢?

其实XML-RPC.NET已经为我们提供了支持,例如我们可以这样定义上面GetPost和UpdatePost的“异步版本”:

type IMetaWeblogProxy =
    inherit IXmlRpcProxy

    [<XmlRpcBegin("metaWeblog.getPost")>]
    abstract BeginGetPost : string -> string -> string -> AsyncCallback -> obj -> IAsyncResult

    [<XmlRpcEnd("metaWeblog.getPost")>]
    abstract EndGetPost : IAsyncResult -> Post


    [<XmlRpcBegin("metaWeblog.editPost")>]
    abstract BeginUpdatePost : string -> string -> string -> Post -> bool -> AsyncCallback -> obj -> IAsyncResult

    [<XmlRpcEnd("metaWeblog.editPost")>]
    abstract EndUpdatePost : IAsyncResult -> bool

完整的代码(如Post类型的定义)您可以从文末的链接里获得。这里我们直接定义了Begin/End两个方法的签名,与C#版本相比省略了参数名,如果您希望保证可读性也可以在定义时补上。只要我们使用这种方法定义了接口,标记了方法,XML-RPC.NET便可以为我们生成一个代理对象。自然,我们也可以在F#中使用XmlRpcProxyGen.Create方法:

let createProxy() = XmlRpcProxyGen.Create<IMetaWeblogProxy>()

在实际使用的时候,我们可以按常规来扩展一下IMetaWeblogProxy接口:

type MetaWeblog.IMetaWeblogProxy with

    member p.GetPostAsync(postId, userName, password) =
        let beginGet (ac, o) = p.BeginGetPost postId userName password ac o
        Async.FromBeginEnd(beginGet, p.EndGetPost)

    member p.UpdatePostAsync(postId, userName, password, post, publish) =
        let beginUpdate (ac, o) = p.BeginUpdatePost postId userName password post publish ac o
        Async.FromBeginEnd(beginUpdate, p.EndUpdatePost)

于是之前的C#代码便可以改写成如下的F#异步工作流:

let workflow = async {
    let client = MetaWeblog.createProxy()
    client.Url <- "http://www.cnblogs.com/JeffreyZhao/services/metaweblog.aspx"

    let userName, password, postId = "JeffreyZhao", "...", "1629216"
    let! post = client.GetPostAsync(postId, userName, password)
    post.Content <- post.Content + "<p>Hello World!</p>"
    return! client.UpdatePostAsync(postId, userName, password, post, true)
}

当然,以上代码只是进行了“定义”,在实际运行时您还需要使用某种方法来执行这个异步工作流。

本文代码

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

16 条回复

  1. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2010-01-04 19:11:00

    沙发

  2. 麒麟
    *.*.*.*
    链接

    麒麟 2010-01-04 19:32:00

    板凳

  3. canbeing
    *.*.*.*
    链接

    canbeing 2010-01-04 19:45:00

    地板

  4. 辰
    *.*.*.*
    链接

    2010-01-04 19:57:00

    看了一下MetaWeblog 的wiki。

    作者是:Dave Winer

    顺便又看了一下profile,出现了:microsoft的关键字。

    During this period, Winer also collaborated with Microsoft and jointly developed the XML-RPC protocol


    所以,我大致可以断定,MetaWeblog和微软有着紧密的关联。

    再所以,我认为,MetaWeblog目前不是一个通用协议。

    不知道这样推理是否正确。
    如果MetaWeblog是个国际协议,除了microsoft,基本上所有blog都支持,那么我的blog系统开发这个protocol就有价值了。

  5. 邀月
    *.*.*.*
    链接

    邀月 2010-01-04 20:10:00

    路过。老赵赋闲,真羡慕!

  6. 老赵
    admin
    链接

    老赵 2010-01-04 20:20:00

    @辰
    XML-RPC和MetaWeblog是两个东西,MetaWeblog虽然我都找不到RFC但是基本上每个blog都支持?
    什么叫做除了Microsoft?如果你要做一个Blog,总归得支持MetaWeblog的,否则就残缺了,呵呵。
    事实上MetaWeblog是一个简单的有些粗糙的东西,说它是个协议还真抬举了它,嘿嘿。

  7. 老赵
    admin
    链接

    老赵 2010-01-04 20:21:00

    邀月:路过。老赵赋闲,真羡慕!


    又不是同时拿着钱,有啥羡慕的呀?

  8. franklin
    *.*.*.*
    链接

    franklin 2010-01-04 22:52:00

    jeffery,你接下来有什么打算呀

  9. 老赵
    admin
    链接

    老赵 2010-01-04 23:02:00

    @franklin
    哪方面打算?

  10. 在别处
    *.*.*.*
    链接

    在别处 2010-01-05 09:13:00

    恭喜老赵排名第一了!

  11. 王德水
    *.*.*.*
    链接

    王德水 2010-01-05 09:38:00

    我对老赵的钻研精神无比佩服,真希望老赵能进研究所,为国家的基础软件出份力。

    恭喜老赵排名第一。

  12. 雪涛
    *.*.*.*
    链接

    雪涛 2010-01-05 09:38:00

    博客园第一,恭喜。。

  13. lola
    *.*.*.*
    链接

    lola 2010-01-05 09:40:00

    因为你每段前几乎都有一个小标题,所以段首缩进2格反倒不好看了

  14. bravf
    *.*.*.*
    链接

    bravf 2010-01-05 14:20:00

    ..同学们都比我早。。我刚发现lz第一了。恭喜~吧。

  15. 老赵
    admin
    链接

    老赵 2010-01-05 14:44:00

    lola:因为你每段前几乎都有一个小标题,所以段首缩进2格反倒不好看了


    啥叫每段前有小标题亚?我说的段是指文章的自然段……

  16. 破浪
    *.*.*.*
    链接

    破浪 2010-01-05 18:49:00

    恭喜恭喜

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我