Google Bookmarksとはてブの差分を出してみる。その1

Google Bookmarkとはてブ両方使ってるけど、全部はてブに移そうと思ったので(思いつきで特に理由もないけど)
差分を出してみることに。

F#の練習に丁度いいと思ったけど、フレームワークを使うのに慣れてないので、まずはC#でやってみる。

データを取得

C#ではてなブックマークのポストができなくて参ってる - ブログ執筆中を参考に。


public static string Dump(string username, string password)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://b.hatena.ne.jp/dump");
req.AddHeader(username,password,"GET");

var res = req.GetResponse();
var sr = new StreamReader(res.GetResponseStream());
var xml = sr.ReadToEnd();
return xml;
}

とりあえず、細かいことは気にせず適当。AddHeaderは割愛^^;

やり方がわかりませんorz
いろいろ追ったら混乱してきたので、今度やることにしてやめました。
とりあえず、手動で取得して次に進む><;

まず、googleからパースしてみる

はてブxmlだけど、googleはhtmlだから面倒だなー……と思いつつ正規表現で遊びながらウニウニやるのは意外に楽しい^^

google bookmarksのフォーマットはこんな感じです。


<DT><H3 ADD_DATE="1198640344791968">Wiiリモコン</H3>
<DL><p>
<DT><A ADD_DATE="1198640899163631" HREF="http://japanese.engadget.com/2007/12/22/wii-opera-sdk/">Wii インターネットチャンネル 非公式SDK公開 - Engadget Japanese</A>
<DT><A ADD_DATE="1198640546058701" HREF="http://www.nintendo.co.jp/wii/topics/interview/vol4/ailive.html">AiLive LiveMove 紹介ムービー - Wii</A>
<DT><A ADD_DATE="1198640795500908" HREF="http://japanese.engadget.com/2007/12/20/multi-touch-pen-tablet-using-wiimote/">Wiiリモコンで今度はマルチペンタブレット - Engadget Japanese</A>

ソースはこんな感じになりました。


var regex = new Regex("(<H3.+>(?<tag>.+)</H3>)|(<A +ADD_DATE=\"(?<utc>[0-9]{13}).+\" +HREF=\"(?<url>[^\"]+)\" *>(?<title>[^<>]+)</A>)", RegexOptions.Compiled);
var matchies = htmlTextLines
.Select(str => regex.Match(str))
.Where(m => m.Success);
return matchies.ScanSelect(
string.Empty,
(match, tag) =>
Tuple.Create(
new
{
Tag = tag = match.GetGroup("tag") ?? tag,
Date = match.GetGroup("utc").ParseLong().IsNotNull(l=>l.ToUtc()),
Url = match.GetGroup("url"),
Title = match.GetGroup("title"),
},
tag)
)
.Where(bm => bm.Url != null)
.GroupBy(bm=>bm.Url)
.Select(gp=>
new BookMarkBase.BmItem
{
Tags = gp.Select(bm=>bm.Tag).ToArray(),
AddDate = gp.First().Date ?? DateTime.MinValue,
Url = gp.Key,
Title = gp.First().Title
})
.ToArray();

短くしたいので、適当に拡張メソッドを作る。

  • GetGroups

Matchから結果取り出す、失敗したらnullにしてるだけ

  • ParseLong

TryParseして失敗したらnullにするだけ。だから結果はNullable

  • IsNotNull

nullじゃなかったら、結果を受け取って変換する。nullならnull返すから結果はNullable

  • ToUtc

longからUtc作ってる
意外だったんですが、フレームワークにないんですね><
あと、google bookmarkのADD_DATEがマイクロ秒(?)なのを知らずに何度も失敗しました。(この情報ってどこにあるんでしょう??)
TimeSpanを作るのに、ミリ秒の方が楽だったから13桁で切って手抜き……

ScanSelectってのを作ってみたが……いろいろ微妙

タグをどう扱うか

一行ずつ見るにはLinqのSelectじゃ扱えないので、F#のscanを真似することに。(そもそもscanが適切かはすごく微妙ですが^^;)

で、scanで、いいかなーと思ったけど、型を推論させるために、最初に匿名型を入れるのが意外に面倒……

結果の値と、次でも使う値(タグ)を分けることに

既に、scanの概念からズレてるのに、強引にこういうことするの良くないのか悩む^^;

分ける方法は、配列か、匿名型か……

配列は何となく嫌。匿名型にすると、推論の問題で逆戻り。

こうなってくると、やはりTupleが欲しい。

C# 3.0 Supplemental Library: Achiral - NyaRuRuが地球にいたころから頂きました

NyaRuRuさんのAchiralは本当にいろいろと参考になります^^
(あれもこれも真似しまくってます>< Ms-PLありがとうございます>NyaRuRuさん)

で、Tupleを生成するときも型を書きたくないので、Createメソッド作って推論させる。

やっぱTupleがあるし、F#の方がいいなー、なんて思ったり。。

ただ、C#で書いたクラスの呼び方はあまり好きじゃないけど・・・
F# at Microsoft Research - Microsoft Research
落とし所としては、問題ない(むしろよくできてる)んだろうけど、やっぱりちょっと違和感が・・・

はてブをパースしてみる。

xmlはこんな感じ。


<entry>
<title>応答: Speech Server 2007 を使用した音声応答ワークフロー</title>
<link rel="related" type="text/html" href="http://msdn2.microsoft.com/ja-jp/magazine/cc500549.aspx?pr=blog" />
<link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/yuji1982/20080424#bookmark-8354393" />
<issued>2008-04-24T19:37:39+09:00</issued>
<id>tag:hatena.ne.jp,2005:bookmark-yuji1982-8354393</id>
<summary type="text/plain">Speech Server 2007とWFでごにょごょ。まーやんないだろうけど・・・</summary>
<dc:subject>Speech Server 2007</dc:subject>
<dc:subject>WF</dc:subject>
</entry>
ソースはこれだけ


var doc = XDocument.Load(new StringReader(xml));
var ns = XNamespace.Get("http://purl.org/atom/ns#");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
return doc.Descendants(ns + "entry").Select(
el =>
new BmItem
{
Title = el.Element(ns + "title").Value,
Url = el.Element(ns + "link").Attribute("href").Value,
AddDate = DateTime.Parse(el.Element(ns + "issued").Value),
Tags = el.Elements(dc + "subject").Select(te=>te.Value).ToArray()
}
)
.ToArray();

XMLは楽でいい><

と言うより、

やっぱりLINQ凄い!そしてC#はいい言語><

あとやること

  • とりあえず差分出す→どうやって視覚化しようかな→WPFを忘れてしまってるので、練習に丁度いいかなー
  • F#で書いてみる
  • プログラムからGoogle Bookmarksを取得したい

飽きたらやめるかもしれないけど^^;