Kobarin's Development Blog

C#やASP.NET、公開APIなどについての記録です。

HtmlAgilityPackの基本

HTMLからスクレイピングでデータを抜き出す際に使用しました。
現時点でわかった使用例を示します。

HTML code example

<html>
<head></head>
<body>
  <h3>企業いろいろ</h3>
  <h4>コンビニ</h4>
  <ul>
    <li><a href="711.htm">セブン-イレブン</a></li>
    <li><a href="lawson.htm">ローソン</a></li>
    <li><a href="famima.htm">ファミリーマート</a></li>
  </ul>
  <h4>PC</h4>
  <ul>
    <li class="usa"><a href="hp.htm">HP</a></li>
    <li class="usa"><a href="dell.htm">Dell</a></li>
    <li><a href="epson.htm">Epson Direct</a></li>
  </ul>
  <h4>EC</h4>
  <ul>
    <li class="usa"><a href="amazon.htm">Amazon</a></li>
    <li><a href="rakuten.htm">楽天</a></li>
  </ul>
  <div class="migi">注意:順不同です</div>
  <div id="footer">copyright 2013 Kobarin</div>
</body>
</html>

前処理(対象によって変更して下さい)

string html = "";                  //HTMLコード
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.OptionAutoCloseOnEnd = false;  //最後に自動で閉じる(?)
doc.OptionCheckSyntax = false;     //文法チェック。
doc.OptionFixNestedTags = true;    //閉じタグが欠如している場合の処理
doc.LoadHtml(html);

取得コード

Exp1. id・classを指定しての要素を取得(単一取得)
HtmlAgilityPack.HtmlNode node1 = doc.DocumentNode.SelectSingleNode("div[@id='footer']");

Console.WriteLine(node1.InnerHtml);

結果

copyright 2013 Kobarin
Exp2. <li>タグの中身を全部取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

<a href="711.htm">セブン-イレブン</a>
<a href="lawson.htm">ローソン</a>
<a href="famima.htm">ファミリーマート</a>
<a href="hp.htm">HP</a>
<a href="dell.htm">Dell</a>
<a href="epson.htm">Epson Direct</a>
<a href="amazon.htm">Amazon</a>
<a href="rakuten.htm">楽天</a>
<a href="yahoo.htm">ヤフーショッピング</a>
Exp3. aタグの文字列部分を取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

セブン-イレブン
ローソン
ファミリーマート
HP
Dell
Epson Direct
Amazon
楽天
ヤフーショッピング
Exp4. 特定classのタグのみ取得
//<li class="usa">のタグを取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[@class='usa']");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

<a href="hp.htm">HP</a>
<a href="dell.htm">Dell</a>
<a href="amazon.htm">Amazon</a>
Exp5. 順番を指定して特定タグを取得
//body直下にある、2番目のdivタグ
HtmlAgilityPack.HtmlNode node = doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]");

Console.WriteLine(node.InnerHtml);

結果

copyright 2013 Kobarin
Exp6. aタグのhref内のURLを全て取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a");

foreach (HtmlAgilityPack.HtmlNode node in nodes) {
  string url = node.GetAttributeValue("href", "");
  Console.WriteLine(url);
}

結果

711.htm
lawson.htm
famima.htm
hp.htm
dell.htm
epson.htm
amazon.htm
rakuten.htm
yahoo.htm
Exp7. 全ul内の1番目のli内のリンク文字列だけ取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[1]/a");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

セブン-イレブン
HP
Amazon
Exp8. 全ul内の最後のli内のリンク文字列を取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[last()]/a");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

ファミリーマート
Epson Direct
楽天
Exp9. 各ul内で2番目以降にあり、且つclassが"usa"のli内にあるリンク文字列を取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[position() >= 2 and contains(@class, 'usa')]/a");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

Dell
Exp10. a内のテキストに'D'を含む内容を取得(※カンマを使います)
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a[contains(.,'D')]");

foreach(HtmlAgilityPack.HtmlNode node in nodes){
  Console.WriteLine(node.InnerHtml);
}

結果

Dell
Epson Direct

注意点

HTMLコードそのものにエラーがあると正常に処理されません。
例えば私が試した所では、閉じタグが抜けていたり、<div><p></div></p>のように互い違いになっていると抽出も出来ないため、ソースコードの段階で補正をしてやる必要があります。

参考

HtmlAgilityPackの記述は原則的にXPathに準拠するらしく、自分の場合はXPath Syntaxを参考にさせて頂きました。
また、HtmlAgilityPack Simulatorも公開しましたので、記述テスト等にお使い下さい。