.NET TIPS

WebBrowserコントロールによりWebページからリンクや画像を抽出するには?[2.0のみ、C#、VB]

デジタルアドバンテージ 遠藤 孝信
2007/12/20

 WebページのHTMLからリンク文字列(およびそのURL)や画像のURLを抜き出したい場合、単純な方法としてはHTMLドキュメントをダウンロードし、正規表現などにより<A>タグや<IMG>タグを抜き出すことができる。しかしこの方法では、HTMLドキュメントの構造が複雑であった場合や、ページ作成者のタグの閉じ忘れなどまでに対応しようとすると非常に困難になる。

 そんな場合には、.NET Framework 2.0の標準コントロールであるWebBrowserコントロール(System.Windows.Forms名前空間)を利用するとよい。これはIEの描画エンジンをコントロール化したもので、対象となるWebページをこのコントロールに表示させた時点でIEの描画エンジンがそのHTMLドキュメントを解析しているため、それに含まれる各HTML要素には、後述する.NET Frameworkのクラスを使って簡単にアクセスできる。

 本来WebBrowserコントロールはWindowsアプリケーション上にWebページやHTMLで記述されたヘルプやドキュメントを表示させるためのものだが、GUIを持たないアプリケーションでもその機能は利用可能だ。本稿ではコンソール・アプリケーションからWebBrowserコントロールを使用し、指定されたWebページからリンク文字列を抜き出す方法を示す。

Webページからリンク文字列を抜き出すサンプル・プログラム

 WindowsアプリケーションでWebBrowserコントロールにより単にWebページを表示する場合には、Visual StudioでフォームにWebBrowserコントロールを配置しておき、そのNavigateメソッドを呼び出すだけでよい(「TIPS:WindowsアプリケーションでWebページを表示するには?」参照)。

 今回のケースでは、以下のような手順によりWebBrowserコントロールを使用し、Webページ内のHTML要素にアクセスする。

  1. WebBrowserコントロールをインスタンス化する
  2. NavigateメソッドによりWebページに移動する
  3. ページ取得が完了するのを待つ
  4. DocumentプロパティからHtmlDocumentオブジェクトを得る
  5. HtmlDocumentオブジェクトから各要素(HtmlElementオブジェクト)にアクセスする

 ポイントとなるのは、3のページ取得完了を待つ処理が必要となる点だ(WebBrowserコントロールではページの取得は非同期に行われるため、Navigateメソッドの呼び出し自体はすぐに完了する)。

 ここではまず、上記の手順を実装したサンプル・プログラムを示す。このプログラムはInsider.NETのトップ・ページに含まれるすべてのリンク文字列とそのURLを表示する。

// getlinks.cs

using System;
using System.ComponentModel;
using System.Windows.Forms;

public class NonDispBrowser: WebBrowser {

  bool done;

  // タイムアウト時間(10秒)
  TimeSpan timeout = new TimeSpan(0, 0, 10);

  protected override void OnDocumentCompleted(
                WebBrowserDocumentCompletedEventArgs e) {
    // ページにフレームが含まれる場合にはフレームごとに
    // このメソッドが実行されるため実際のURLを確認する
    if (e.Url == this.Url) {
      done = true;
    }
  }

  protected override void OnNewWindow(CancelEventArgs e) {
    // ポップアップ・ウィンドウをキャンセル
    e.Cancel = true;
  }

  public NonDispBrowser() {
    // スクリプト・エラーを表示しない
    this.ScriptErrorsSuppressed = true;
  }

  public bool NavigateAndWait(string url) {

    base.Navigate(url); // ページの移動

    done = false;
    DateTime start = DateTime.Now;

    while (done == false) {
      if (DateTime.Now - start > timeout) {
        // タイムアウト
        return false;
      }
      Application.DoEvents();
    }
    return true;
  }
}

class GetLinks {
  [STAThread]
  static void Main() {

    NonDispBrowser ndb = new NonDispBrowser();
    ndb.NavigateAndWait("http://www.atmarkit.co.jp/fdotnet/");

    HtmlDocument doc = ndb.Document;

    // リンク文字列とそのURLの列挙
    foreach (HtmlElement e in doc.GetElementsByTagName("A")) {

      string href = e.GetAttribute("href"); // HREF属性の値
      string text = e.InnerText; // リンク文字列

      if (!string.IsNullOrEmpty(href)
          && !string.IsNullOrEmpty(text)) {
        text = text.Replace("\r\n", ""); // 改行文字の削除
        Console.WriteLine(href);
        Console.WriteLine(text);
      }
    }
  }
}

// コンパイル方法:csc getlinks.cs
WebBrowserコントロールによりリンクを取得するC#のサンプル・プログラム(getlinks.cs)

' getlinks.vb

Imports System
Imports System.ComponentModel
Imports System.Windows.Forms

Public Class NonDispBrowser
  Inherits WebBrowser

  Dim done As Boolean

  ' タイムアウト時間(10秒)
  Dim timeout As New TimeSpan(0, 0, 10)

  Protected Overrides Sub OnDocumentCompleted( _
                ByVal e As WebBrowserDocumentCompletedEventArgs)
    ' ページにフレームが含まれる場合にはフレームごとに
    ' このメソッドが実行されるため実際のURLを確認する
    If e.Url = Me.Url Then
      done = True
    End If
  End Sub

  Protected Overrides Sub OnNewWindow(ByVal e As CancelEventArgs)
    ' ポップアップ・ウィンドウをキャンセル
    e.Cancel = True
  End Sub

  Public Sub New()
    ' スクリプト・エラーを表示しない
    Me.ScriptErrorsSuppressed = True
  End Sub

  Public Function NavigateAndWait(ByVal url As String) As Boolean

    MyBase.Navigate(url) ' ページの移動

    done = False
    Dim start As DateTime = DateTime.Now

    While done = False
      If DateTime.Now - start > timeout Then
        ' タイムアウト
        Return False
      End If
      Application.DoEvents()
    End While

    Return True
  End Function
End Class

Class GetLinks
  Shared Sub main()

    Dim ndb As New NonDispBrowser
    ndb.NavigateAndWait("http://www.atmarkit.co.jp/fdotnet/")

    Dim doc As HtmlDocument = ndb.Document

    ' リンク文字列とそのURLの列挙
    For Each e As HtmlElement In doc.GetElementsByTagName("A")

      Dim href As String = e.GetAttribute("HREF") ' HREF属性の値
      Dim text As String = e.InnerText ' リンク文字列

      If (Not String.IsNullOrEmpty(href)) _
            And (Not String.IsNullOrEmpty(text)) Then
        text = text.Replace(vbCrLf, "") ' 改行文字の削除
        Console.WriteLine(href)
        Console.WriteLine(text)
      End If
    Next

  End Sub
End Class

' コンパイル方法:vbc getlinks.vb
WebBrowserコントロールによりリンクを取得するVBのサンプル・プログラム(getlinks.vb)

 プログラムの実行結果は次のようになる。

http://www.atmarkit.co.jp/
@IT
http://tech.atmarkit.co.jp/
@IT テクノロジー
http://www.atmarkit.co.jp/im/
@IT情報マネジメント
http://monoist.atmarkit.co.jp/
@IT MONOist
http://jibun.atmarkit.co.jp/
@IT自分戦略研究所
http://www.atmarkit.co.jp/job/
……以下省略……
上記サンプル・プログラムの実行結果例

 このプログラムでは、WebBrowserコントロールをサブクラス化してNonDispBrowserクラスを作成している。NonDispBrowserクラスのNavigateAndWaitメソッドが、指定したWebページの取得を開始し、それが完了するまで待つメソッドである。

ページ取得完了を待つ処理

 NavigateAndWaitメソッドでは、WebBrowserコントロールより継承したNavigateメソッドを呼び出してページ取得を開始した後、doneフラグがtrueになるまでApplication.DoEventsメソッド呼び出しのループによりページ取得が完了するのを待つ。

 doneフラグをtrueにセットしているのは、ページ取得が完了したときにシステムにより呼び出されるOnDocumentCompletedメソッドである。

 ただし、対象としているWebページにフレームが含まれている場合には、フレーム内容の取得が完了した場合にもこのOnDocumentCompletedメソッドが呼び出される。このためプログラムでは、そのときに取得が完了したURL(コードではe.Url)と最終的に取得したいURL(WebBrowserコントロールのUrlプロパティの値)が一致した場合にのみdoneフラグをtrueにしている。

 しかしこの処理だけでは、ほかのURLにリダイレクトされるようなページの取得は正しく処理できない(WebBrowserコントロールのUrlプロパティの値がリダイレクト後のURLとなるため)。このため一定時間(上記のコードでは10秒間)内にdoneフラグがtrueにならない場合には強制的にループを抜けるようにしている。

ポップアップ・ウィンドウの抑制

 今回はGUIを持たないアプリケーションでの使用を目的としているので、WebBrowserコントロールが別ウィンドウを開こうとするのを阻止しなければならない。これは、新しいウィンドウが開く直前にシステムにより呼び出されるOnNewWindowメソッドにおいて、メソッドのパラメータで渡されるCancelEventArgsオブジェクト(System.ComponentModel名前空間)のCancelプロパティにtrueをセットすることで可能となる。

 また、デフォルトでは、対象となるWebページに実行エラーとなるスクリプト(JavaScriptのコードなど)が含まれている場合、スクリプト・エラーを示すダイアログが表示されてしまう。これを抑制するにはWebBrowseコントロールのScriptErrorsSuppressedプロパティをtrueにしておけばよい。

HtmlDocumentオブジェクトとHtmlElementオブジェクト

 Webページの取得が完了すれば、そのHTMLドキュメントにはDocumentプロパティからアクセスできる。

 HTMLドキュメントはHtmlDocumentオブジェクト(System.Windows.Forms名前空間)で表され、これはHTMLドキュメントに含まれる各HTML要素をHtmlElementオブジェクト(System.Windows.Forms名前空間)のコレクションとして保持している(このコレクションはHtmlElementCollectionクラス(System.Windows.Forms名前空間)により表される)。

 HtmlElementクラスには親要素や子要素(のコレクション)を示すParentプロパティやChildrenプロパティがあり、HTMLドキュメントは1つのツリー構造で表現されている。このためツリー構造をたどりながら任意の要素にアクセスすることもできるが、特定のタグを持つ要素だけを列挙する場合には、HtmlDocumentオブジェクトのGetElementsByTagNameメソッドを使うのが便利だ。

 GetElementsByTagNameメソッドのパラメータには「A」や「IMG」といったタグ名を文字列で指定して呼び出す(大文字小文字は区別されない)。これによりそのタグ名を持つHtmlElementオブジェクトのコレクションが取得でき、その各要素についてGetAttributeメソッドによりタグの属性値を、InnerTextプロパティによりそのタグ内に含まれるテキストを取得できる。End of Article

利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:Windowsフォーム 処理対象:WebBrowserコントロール
使用ライブラリ:WebBrowserコントロール(System.Windows.Forms名前空間)
使用ライブラリ:CancelEventArgsクラス(System.ComponentModel名前空間)
使用ライブラリ:HtmlDocumentクラス(System.Windows.Forms名前空間)
使用ライブラリ:HtmlElementクラス(System.Windows.Forms名前空間)
使用ライブラリ:HtmlElementCollectionクラス(System.Windows.Forms名前空間)
関連TIPS:WindowsアプリケーションでWebページを表示するには?

この記事と関連性の高い別の.NET TIPS
WebBrowserコントロールのコンテンツを文字列により設定するには?
WebBrowserコントロール内のテキストボックスに文字列をセットするには?
WindowsアプリケーションでWebページを表示するには?
WPFアプリケーションでWebページを表示するには?
WebBrowserコントロールで選択されている文字列をコピーするには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間