c#によるIE自動操作のサンプルコード(初心者向け)

IEを自動制御するC#のアプリケーションを作成してみましょう。

ブラウザを自動で操作するにはSeleniumという仕組みも使えますが、今回はCOMを使った操作を行います。

COMというと少し難しく聞こえますが、要は特別な仕組みでなく、Windowsの機能のみで作ってしまおうということです。

ExcelなどのVBAマクロでもほぼ同じようなやり方で出来ると思います。

サンプルとして作るのは以下のようなアプリです。

  • 検索したいキーワードを入力してボタンを押すと、自動でInternet Explorer(ブラウザ)が起動する
  • ブラウザでは入力したキーワードで検索される。
  • 結果が表示されたら5秒後に自動でブラウザが閉じる。

プロジェクトを作成

はじめにプロジェクトを作成します。

フリーの開発環境Microsoft Visual Studio 2019を使います。

「新しいプロジェクトの作成」⇒「Windowsフォームアプリケーション」を選択します。

プロジェクトを作成すると、下記のようなフォームが表示されるので、

TextBoxとButtonを配置します。

ブラウザを自動で開いてサイトを表示するソースと設定が必要な参照

シンプルにブラウザを開いて、指定したサイトを表示させるには、以下のコードでOKです。

ブラウザを操作するハンドラ(インスタンス)を以下のように宣言し、Navigate()するだけです。

SHDocVw.InternetExplorer objIE = new SHDocVw.InternetExplorer(); //オブジェクトを作成
objIE.Navigate("about:blank");          //空ページの表示
objIE.Visible = true;                   //IEを表示

さて、このハンドラを使ってIEの自動操作していくわけですが、それにあたって、いくつか参照が必要なので以下のように追加していきます。

SHDocVwの参照追加

SHDocVwのクラスを使えるように、「参照」から「参照の追加」でCOMの「Microsoft Internet Controls」にチェックを入れ、追加します。

mshtml.HTMLDocument用の参照追加

また、Webページを表示した後、要素を取得するのに「mshtml.HTMLDocument」を使うので、COMの「Microsoft HTML Object Library」を参照に追加します。

UrlEncodeのための参照追加

System.Web.HttpUtility.UrlEncodeを使うには、アセンブリの「System.Web」を参照で追加します。

Yahooに対して検索を行うには、

のようなURLを投げる必要がありますが、検索キーワードの部分が日本語などのマルチバイトの文字の場合、そのままだとエラーになります。

この場合、URLEncodeしてリクエストを投げる必要があるためです。

要素の取得の方法

さて、さっそく自動操作していきましょう。
まず、以下のようにInternetExplorerをインスタンス化します。

その後、Navigate()メソッドで、targetUrlのサイトを読み込みます。

さて、さっそく自動操作していきましょう。
まず、以下のようにInternetExplorerをインスタンス化します。

その後、Navigate()メソッドで、targetUrlのサイトを読み込みます。

Navigte後、ページがロードされたら、そのロードの結果、解析されたHTML文書がDocumentに入っていますので

var ObjHtml = (mshtml.HTMLDocument)objIE.Document;

string gStrRes = "";

var bodys = ObjHtml.getElementsByTagName("body");

foreach (mshtml.IHTMLElement element in bodys)
{
    if (element != null)
    {
        gStrRes = element.outerHTML;
    }
}

のようにすると、サイト全体をテキストで取得することができます。

objIEにはその結果が入っていますので、一度Navigateでサイトを読み込んでしまえば、あとは、その中の要素をgetElementsByXXXまたはgetElementByXXXで取得することができます。

例えば、getElementById()で要素を取得し、その内容を取得するには以下のようにします。

mshtml.IHTMLElement elm = ObjHtml.getElementById("js-tracked_mod_1");
Debug.WriteLine("elm=" + elm.outerHTML);

一方、getElementsByXXX という形式の関数はElementsとあるように要素が複数返ります。

body要素は1つしかないので、上記のやり方でbody要素、つまりページ全体のテキストを取得できます。

これをデバッグなどで出力すると、どんなHTMLソースが返却されたかがわかるので、

どんな要素名で取得すればいいか?

などが分かりやすいです。

注意点としては、実際に返却されるHTMLソースと、上記のソースは若干異なることがあります。

それは、返却されたHTML文書をドライバが解釈して、ツリー構造にした結果が「mshtml.IHTMLElement」だからです。

そのため、期待される要素名で取得できない場合は、デバッグで出力させてみることをお勧めします。

クラス名で要素を取得するTips

タグやIdで取得するメソッドはありますが、クラス名で取得するメソッドは存在しません。

そのため、特定のクラス名で取得したい場合は、

var rankurls = ObjHtml.getElementsByTagName("a");

foreach (mshtml.IHTMLElement element in rankurls)
{
    //Debug.WriteLine("foreach" + (String)(element.getAttribute("className")));
    if ((String)(element.getAttribute("className")) == "sw-Card__titleInner")
    {
        String siteUrl = element.getAttribute("href");
        Debug.WriteLine("URL=" + siteUrl);
    }
}

のように、タグで取得し、そのタグの要素のクラス名を指定することで取得すると良いです。

上記の例だと、aタグで取得し、その要素のクラス名が「sw-Card__titleInner」のものを抜き出しています。

すると、

<a class=”sw-Card__titleInner” href=”http://search.yahoo.co.jp/xxxxx” >南青山 <b>まめ</b></h3>
<br><div class=”sw-Cite”><cite>www.<b>mamemame</b>.info/</cite></div></a>

のような要素のhrefの属性が取得できるというわけです。

ページの読み込み完了まで待つ方法

ネットワークの状態によっては、すぐにレスポンスが返ってくるかわかりませんし、ページのロードに時間がかかってしまう場合もあります。

そのため、ページが全部読み込まれるまで、完了を待つようにすると良いです。

while (objIE.Busy || objIE.ReadyState != SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
{
    //無処理
    System.Windows.Forms.Application.DoEvents();
    System.Threading.Thread.Sleep(100);
}

サンプルソース全文

以下がサンプルソースです。

このまま貼り付けると動く状態になっています。

using System;
using System.Windows.Forms;
using System.Diagnostics;

namespace IEAutoSample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SHDocVw.InternetExplorer objIE = new SHDocVw.InternetExplorer(); //オブジェクトを作成
            objIE.Navigate("about:blank");          //空ページの表示
            objIE.Visible = true;                   //IEを表示


            String encodedKeyword = System.Web.HttpUtility.UrlEncode(this.textBox1.Text, System.Text.Encoding.UTF8);

            String targetUrl = "https://search.yahoo.co.jp/search?b=0&p=" + encodedKeyword;

            objIE.Navigate(targetUrl);

            //読み込み完了まで待つ
            while (objIE.Busy || objIE.ReadyState != SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
            {
                //無処理
                System.Windows.Forms.Application.DoEvents();
                System.Threading.Thread.Sleep(100);
            }

            var ObjHtml = (mshtml.HTMLDocument)objIE.Document;

            var bodys = ObjHtml.getElementsByTagName("body");
            foreach (mshtml.IHTMLElement element in bodys)
            {
                if (element != null)
                {
                    Debug.WriteLine(element.outerHTML);
                }
            }

            System.Threading.Thread.Sleep(5000);

            if (objIE != null) {
                objIE.Quit();
            }

        }
    }
}

ブラウザが固まらないようにする別スレッド化

あと、ブラウザ操作を繰り返すときに、GUIが固まってしまう現象を回避するため、別スレッドで操作するようにしても良いです。

これは少し中級者向けなので、最後に添えます。

そのためには、backgroundWorkerのコンポーネントを追加し、

  • backgroundWorker1_ProgressChanged
  • backgroundWorker1_RunWorkerCompleted
  • backgroundWorker1_DoWork

というメソッドを作成したうえで、イベント発生時に呼ばれるように設定します。