WebAPIが提供されていないサイトのアプリを作っていると、スクレイピングしなければいけない事が多々あります。
SgmlReader for Silverlight/Windows Phone 7 - 酢ろぐ!で紹介したとおり、僕は neueccさん(@neuecc)がSilverlight/Windows Phone 7向けにポーティングしてくださったsgmlreader.slを使っています。
ただ、以下の様なHTMLの文書型宣言があると、HTMLパースに失敗してしまっていました。
|html| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ||<
どうしてもパースを通したかったので、forkして修正したコードを「https://bitbucket.org/ch3cooh393/sgmlreader.sl/」へあげておきました。一応、SgmlReaderの最新版とのマージを取っておきましたが、あまりテストしていないので動かないHTMLがあったらごめんなさい。
**使い方
https://bitbucket.org/ch3cooh393/sgmlreader.sl/downloadsへアクセスしてビルドします。ビルドしたSgmlReader.WP7.dllとSystem.Xml.Linq.dllを自分のプロジェクトへ参照追加します。
通信処理開始の開始のトリガーは適当にbutton1_Clickにハンドリングしておきます。このメソッドでは米国Amazonのランキング情報を取得しに行っています。
具体的には、WebClientクラスのOpenReadAsyncメソッドを使用して非同期でHTMLを取得しにいきます。読み込みが終わるとwc_OpenReadCompletedが呼ばれるので、StreamをSgmlReaderへ渡して、新しいXDocumentを作成します。
|cs| using System.Xml.Linq; using Sgml; using System.IO;
private void button1_Click(object sender, RoutedEventArgs e)
{
var wc = new WebClient();
wc.OpenReadCompleted += wc_OpenReadCompleted;
wc.OpenReadAsync(new Uri("http://www.amazon.com/gp/bestsellers/books"));
}
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
using (var reader = new StreamReader(e.Result, System.Text.Encoding.UTF8))
using (var sgmlReader = new SgmlReader { InputStream = reader })
{
sgmlReader.DocType = "HTML";
sgmlReader.CaseFolding = CaseFolding.ToLower;
// HTMLを元にXDocumentを作成。
var doc = XDocument.Load(sgmlReader);
}
}
||<
デバッグ実行してVSのウォッチで正しくデータがパース出来ているか確認してみてください。
**もっと現実に即した使い方
これで終わりなのですが、実際に本当にパース出来ているのか判らないので、ListBoxにスクレイピングした情報を表示してみましょう。簡単なヘルパークラスを作ります。
|cs| // Book.cs
namespace SgmlReaderTest { public class Book { public string Title { get; set; } public string PhotoUrl { get; set; } } } ||<
次に表示するListBoxのItemTemplateを定義します。ImageとTextBlockが横並びに配置しました。
|xml|
<ListBox.ItemTemplate> ||<</ListBox.ItemTemplate>
最後に欲しい要素を探してListBox.ItemSourceに設定します。
|cs| void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { if (e.Error != null) { return; }
using (var reader = new StreamReader(e.Result, System.Text.Encoding.UTF8))
using (var sgmlReader = new SgmlReader { InputStream = reader })
{
sgmlReader.DocType = "HTML";
sgmlReader.CaseFolding = CaseFolding.ToLower;
// HTMLを元にXDocumentを作成。
var doc = XDocument.Load(sgmlReader);
var q = doc.Descendants("div")
.Where(ul =>ul.Attribute("class") != null
&& ul.Attribute("class").Value == "zg_item zg_sparseListItem")
.Descendants("img")
.Select(el =>
{
var obj = new Book();
obj.PhotoUrl = el.Attribute("src").Value;
obj.Title = el.Attribute("title").Value;
return obj;
});
listBox1.ItemsSource = q.ToList();
}
} ||<
実行してみました。