首页 / 网页编程 / ASP.NET / 一次批量修改博客文章的经验(下):操作过程
        
            一次批量修改博客文章的经验(下):操作过程2011-03-26 博客园 Jeffrey Zhao获取所有文章ID首先,我们便要下载所有文章了,这又该怎么做呢?虽然MetaWeblog API提供了 getRecentPosts方法用来获取最近的文章,但是这个接口却并不好用。例如,它只能用来 获取最新的几篇文章内容,但对我来说,我想修改的其实是很久之前的文章。那么,难道 要我下载全部500多篇文章才行吗?后来我统计了一下,所有文章大小存成文本文件大约 有10M,一个请求下载 10M内容还是有些夸张的——而且还看不到进度。因此,我最后打 算“曲线救国”,先着手获得所有公开文章的ID,再通过getPost接口获得文章内容。MetaWeblog API并不提供获取所有文章ID的接口,但这并不影响我们从网页上直接进 行抓取。我们从博客园提供的“月份汇总”页面入手,即这样的一张页面。博客园的月份 汇总的URL非常有规律,获得它的HTML内容之后即可使用正则表达式来捕获文章ID了。您 可能会想,一篇文章可能会取别名(这样URL上就不显示ID了),而网页上各种URL也很多 ,有什么办法可以准确而方便地分析出文章ID吗?其实这个问题很简单,因为博客园为每 篇文章都放置了一个“编辑”链接,它的URL是.../EditPosts.aspx?postid=1633416,对 我们来说再方便不过了。于是下载和捕获文章ID的方法可谓手到擒来:type WebClient with
   member c.GetStringAsync(url) =
     async {
       let completeEvent = c.DownloadStringCompleted 
       do c.DownloadStringAsync(new Uri(url))
       let! args = Async.AwaitEvent(completeEvent)
       return args.Result
     }
let downloadPostIdsAsync (beginMonth : DateTime) (endMonth :  DateTime) =
   let downloadPostIdsAsync" (m : DateTime) =
     async {
       let webClient = new WebClient()
       let url = sprintf  "http://www.cnblogs.com/JeffreyZhao/archive/%i/%i.html" m.Year  m.Month
       let! html = webClient.GetStringAsync(url)
       let regex = @"EditPosts.aspx?postid=(d+)"
       return [ for m in Regex.Matches(html, regex) ->  m.Groups.Item(1).Value |> Int32.Parse ]
     }
   async {
     let! lists =
       Seq.initInfinite (fun i -> beginMonth.AddMonths(i)) 
       |> Seq.takeWhile (fun m -> m <= endMonth) 
       |> Seq.map downloadPostIdsAsync"
       |> Async.Parallel
     lists
     |> List.concat
     |> List.sort
     |> List.map (fun i -> i.ToString())
     |> fun lines -> File.WriteAllLines("postIds.txt",  lines)
   }下载文章ID的任务由downloadPostIdsAsync函数完成,它接受beginMonth和endMonth 两个DateTime参数来表示月份的区间,我们将从中获取所有的文章ID。 downloadPostIdsAsync函数会生成一个异步工作流,执行这个工作流便会将所有的文章 ID进行排序,并保存至postIds.txt文件中去,一行一个。获取单月的文章ID由内部的 downloadPostIdsAsync"这个辅助函数负责,它会构造出下载单个月份文章ID的异步工作 流,再由外部函数合并而成——换句话说,所有月份的文章将同时进行异步下载,提高效 率。我们可以在main方法中执行downloadPostIdsAsync函数,这样postIds.txt文件中便会 出现所有的文章ID了:System.Net.ServicePointManager.DefaultConnectionLimit <-  10
Blogging.downloadPostIdsAsync (new DateTime(2006, 9, 1)) (new  DateTime(2008, 12, 1))
|> Async.RunSynchronously由于WebClient基于WebRequest对象实现,而WebRequest受到ServicePointManager控 制,因此我们要设置其DefaultConnectionLimit属性来打开对单个域名的限制——并控制 对并发连接的数量进行限制,以免对服务器产生太大压力(当然我们其实工作量本不大, 且博客园也不会那么脆弱)。当然,这个限制也可以通过配置进行更改。