接續上篇準備工作,本文實作以 F# 非同步工作流 (`async`) 結合 `WebClient` 平行下載博客園所有公開文章 ID,再透過 MetaWeblog API 批次取得/更新內容,完成 500+ 篇文章自動化修改。
博客園「月份歸檔」頁面 URL 規則固定:`https://www.cnblogs.com/{user}/archive/{yyyy}/{mm}.html`。每頁包含「編輯」連結 `EditPosts.aspx?postid=12345`,以 Regex 擷取 ID。
type WebClient with
member c.GetStringAsync(url) =
async {
let completeEvent = c.DownloadStringCompleted
do c.DownloadStringAsync(Uri(url))
let! args = Async.AwaitEvent(completeEvent)
return args.Result
}
let downloadPostIdsAsync (beginMonth: DateTime) (endMonth: DateTime) =
let downloadMonth (m: DateTime) = async {
let wc = new WebClient()
let url = sprintf "https://www.cnblogs.com/JeffreyZhao/archive/%i/%i.html" m.Year m.Month
let! html = wc.GetStringAsync(url)
let regex = @"EditPosts\.aspx\?postid=(\d+)"
return [ for m in Regex.Matches(html, regex) -> m.Groups.[1].Value |> Int32.Parse ]
}
async {
let! lists =
Seq.initInfinite (fun i -> beginMonth.AddMonths(i))
|> Seq.takeWhile (fun m -> m <= endMonth)
|> Seq.map downloadMonth
|> Async.Parallel
lists
|> List.concat
|> List.sort
|> List.map string
|> fun lines -> File.WriteAllLines("postIds.txt", lines)
}
type Post = {
[] PostID: int
[] CreateTime: DateTime
[] Title: string
[] Content: string
[] Categories: string[]
}
type IMetaWeblogProxy = inherit IXmlRpcProxy
[]
abstract GetPost: postId:string * userName:string * password:string -> Post
[]
abstract UpdatePost: postId:string * userName:string * password:string * post:Post * publish:bool -> bool
使用 `XmlRpcProxyGen.Create