読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

Excel VBAのマクロで,IEを自動操作しよう (DOMセレクタ関数をVBAで自作)

vba ブラウザ IEの自動操作


JavaScriptと同じように,VBAでもDOM操作が可能。


下記のようなマクロで,IEを操作できる。

Sub Googleで検索()

    ' IEを立ち上げて Google を開く
    Dim ie As Object
    Set ie = new_ie("http://www.google.co.jp")

    ' 検索キーワードを入力
    type_val ie, "q", "ホゲラッチョ"
    
    ' 検索ボタンクリック
    submit_click ie, "btnG"
    
    ' 検索結果の 1 件目のタイトルを表示
    MsgBox domselec(ie, Array( _
        "id", "res", _
        "tag", "li", 0, _
        "tag", "h3", 0 _
    )).innerText
    
    ' IEを閉じる
    ie.Quit
    Set ie = Nothing

End Sub

これは,独自の関数(後述)をいろいろ使って

  • IEを立ち上げ,
  • Googleで特定のキーワードで検索を行ない,
  • 検索結果の1件目のサイト名を表示する。

というコード。

(※「domselec」というDOMセレクタ関数を自作している。
jQueryの$()やprototype.jsの$$()の簡易版と思えばよい。)



下記で,この方法を解説する。


素の関数を使う場合

WebページのHTMLにアクセスするための基本的な関数は,VBAにデフォルトで備わっている。


それらを使って,冒頭のコードを工夫しないで書くと下記のようになる。

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Sub IE操作()

    ' IE起動
    Set ie = CreateObject("InternetExplorer.Application")
    ie.Navigate "http://www.google.co.jp/"
    ie.Visible = True
    waitIE ie
    
    ' 検索キーワードを入力
    ie.Document.getElementById("q").Value = "ホゲラッチョ"
        ' IEのgetElementByIdはnameも参照する
    Sleep 100
    
    ' 検索ボタンクリック
    ie.Document.all("btnG").Click
    waitIE ie
    
    ' 1件目のサイトのタイトルを表示
    MsgBox ie.Document.getElementById("res") _
        .getElementsByTagName("li")(0) _
        .getElementsByTagName("h3")(0) _
        .innerText
    
    ' 制御を破棄
    ie.Quit
    Set ie = Nothing

End Sub


' IEがビジー状態の間待ちます
Sub waitIE(ie)
    
    ' http://www.excel.studio-kazu.jp/kw/20070219032632.html
    ' http://www.ken3.org/cgi-bin/group/vba_ie.asp#Document_ReadyState_Busy
    Do While ie.Busy = True Or ie.readystate <> 4
        DoEvents
    Loop
    
    Sleep 100
    
End Sub

JavaScriptと同じように

  • getElementById
  • getElementsByTagName
  • getELementsByName

などの関数が使える。

また,要素に対して

  • .Value
  • .Click

のようにして値のアクセスとか操作を実行できる点もJavaScriptと同じ。


もっと楽にコーディングしたい

前項のコードをもっと簡潔に書くために,IE自動操作用の関数ライブラリを作ろう。

その場合,冒頭のコードと同じように,下のように書くことができる。

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Sub IE操作改()

    Dim ie As Object
    Set ie = new_ie("http://www.google.co.jp")

    ' 検索キーワードを入力
    type_val ie, "q", "ホゲラッチョ"
    
    ' 検索ボタンクリック
    submit_click ie, "btnG"
    
    ' 1件目のサイトのタイトルを表示
    MsgBox domselec(ie, Array( _
        "id", "res", _
        "tag", "li", 0, _
        "tag", "h3", 0 _
    )).innerText
    
    ' 終了
    ie.Quit
    Set ie = Nothing
End Sub



' IEがビジー状態の間待ちます
Sub waitIE(ie)
    Do While ie.Busy = True Or ie.readystate <> 4
        DoEvents
    Loop
    
    Sleep 100
End Sub


' 新規IE作成
Function new_ie(home_url)

    Dim ie As Object
    Set ie = CreateObject("InternetExplorer.Application")
    
    ' 初期ページを開く
    goto_url ie, home_url
    ie.Visible = True

    Set new_ie = ie

End Function


' URL移動
Sub goto_url(ie, url)
    ie.Navigate url
    waitIE ie
End Sub


' $
Function gid(ie, dom_id)
    ' 注:IEのgetElementByIdはnameも参照する
    Set gid = ie.Document.getElementById(dom_id)
End Function


' getElementsByTagName
Function gtn(parent, tag_name)
    Set gtn = parent.getElementsByTagName(tag_name)
End Function


' 入力します
Sub type_val(ie, dom_id, val)
    gid(ie, dom_id).Value = val
    Sleep 100
End Sub


' 送信ボタンやリンクをクリック
Sub submit_click(ie, dom_id)
    gid(ie, dom_id).Click
    waitIE ie
End Sub


' 簡易DOMセレクタ
Function domselec(ie, arr)
    Dim parent_obj As Object
    Dim child_obj As Object
    Set parent_obj = ie.Document
    
    ' 条件配列内で階層を深めていく
    cur = 0
    continue_flag = True
    Do While continue_flag = True
        
        ' 適用メソッドの種類を判定
        If arr(cur) = "id" Then
            
            ' getElementById
            dom_id = arr(cur + 1)
            Set child_obj = parent_obj.getElementById(dom_id)
            
            ' 条件配列内のカーソルを進める
            cur = cur + 2
        
        ElseIf arr(cur) = "tag" Then
            
            ' getElementsByTagName
            tag_name = arr(cur + 1)
            index_num = arr(cur + 2)
            Set child_obj = parent_obj.getElementsByTagName(tag_name)(index_num)
            
            ' 条件配列内のカーソルを進める
            cur = cur + 3
        
        End If
        
        ' 取得したオブジェクトを次の階層の親オブジェクトとする
        Set parent_obj = child_obj
        
        ' 条件配列の終端まで来たか
        If cur > UBound(arr) Then
            continue_flag = False
        End If
        
    Loop
    
    Set domselec = parent_obj
    
End Function

こうすれば,トップの IE操作改() のメソッド中には,目的機能に特化したコードのみを記述すればよくなる。


解説:

  • オブジェクトの代入は,Setを付けないと実行時エラーになる。

応用

IE限定なので,開発時にはテストには利用しづらい。
(※Webアプリケーションをブラウザ上で自動テストするには,Exceleniumを使うとよいだろう。)


しかし,「ブラウザ経由の面倒なタスク」を繰り返しこなす目的には,非常に役立つ。


特に,ブラウザをExcelと連携させたい場合。

  • (1)Excel上に大量のデータが書いてあって,
  • (2)その大量のデータをもとに,Webサイトを操作したい

という時,VBAで解決できる。

あるいは,「Web上に大量のデータがあって,それをExcelに保存したい(=Webスクレイピング)」という用途にもぴったりだ。


(1)は,「シート上のデータをセルごとに読み込む」という事だから,VBAの定番だ。

(2)は,JavaScriptの得意分野。

だから,今回のようにVBAをJavaScriptのように使う事ができるようにしておけばよいというわけだ。

補足

他の役立つ関数。

' 要素をクリックします
Sub ie_click(ie, dom_id)
    gid(ie, dom_id).Click
    Sleep 100
End Sub


' チェックボックスの状態をセットします
Sub set_check_state(ie, dom_id, checked_flag)
    ' 希望通りのチェック状態でなければクリック
    If Not(gid(ie, dom_id).Checked = checked_flag) Then
        ie_click ie, dom_id
    End if
End Sub


' セレクトボックスを文言ベースで選択します
Sub select_by_label(ie, dom_id, label)
    If Len(label) < 1 Then
      Exit Sub
    End If
    
    Set opts = gid(ie, dom_id).Options
    For i = 0 To opts.Length - 1
        ' textが同じか
        If opts(i).innerText = label Then
            opts(i).Selected = True
            Exit Sub
        End if
    Next i
    
End Sub


' ラジオボタンを値ベースで選択します
Sub select_radio_by_val(ie, post_name, value)
    If Len(value) < 1 Then
        Exit Sub
    End If
    
    Set radios = ie.Document.getElementsByName(post_name)
    For i = 0 To radios.Length - 1
        If radios(i).Value = CStr(value) Then
            radios(i).Click
            
            Sleep 100
        End If
    Next i

End Sub

追記

2012年12月現在,IEで閲覧した際のGoogleトップページの仕様が変わっている。

そのため,ここで取り上げたコードは下記のように書きなおす必要がある。

' 検索キーワードを入力
type_val ie, "lst-ib", "ホゲラッチョ"

' 検索ボタンクリック
submit_click ie, "btnG"



関連エントリー:

ブラウザのビジー状態を判定するための,より良い方法 (WSHでIEを自動操作する際,COMのアプリケーションイベントを利用する)
http://language-and-engineering.hatenablog.jp/entry/20100410/p1


ドキュメント作成を楽にするための,Excel VBA 頻出8パターン
http://language-and-engineering.hatenablog.jp/entry/20090401/p1


JScript / VBScript (WSH)で,IEを自動操作しよう
http://language-and-engineering.hatenablog.jp/entry/20090713/p1


[IEの自動操作]記事一覧
http://d.hatena.ne.jp/language_and_en...