VBAの勉強を始めてみた

色々試しています。

VBAでIEを操る(ページ移動後にDOMや要素を再取得しなかったらどうなるのか?)

今回は、変数に取得したDOMや要素が、ページ移動後、どんな挙動になるか調べてみます。
ページを移動した後、DOMツリー構造が変わるので、再取得する必要があるだろう・・・・・・と予想できます。が、実際はどうなのか、書籍やネットから腑に落ちる情報を見つけられなかったので、実験してみます。

目次

 

実験に使うのは、以下のHTML文書です。

 

ページ移動前

f:id:kouten0430:20180906164812j:plain

↑このページのソース

<html>
<body>
<p id="ichibanjukusita">りんご</p>
<p>りんご</p>
<p>りんご</p>
<p>りんご</p>
<p>りんご</p>
</body>
</html> 

 

ページ移動後

f:id:kouten0430:20180906164905j:plain

↑このページのソース

<html>
<body>
<p id="ichibanjukusita">みかん</p>
<p>みかん</p>
<p>みかん</p>
<p>みかん</p>
<p>みかん</p>
</body>
</html>

 

この二つのHTML文書を使い、ページ移動前と移動後で、DOMと要素がどうなるのかローカルウィンドウで観察します。

結論から言うと

  • メソッドで取得する要素は、ページ移動後に再取得する必要がある
  • プロパティで取得するDOMは、ページ移動後に再取得する必要なし

 

この結論を得るために、過剰ですが、8パターンの実験をしてみました。

 

要素ひとつを変数に取得し、ページ移動後、再取得せずに使用した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myElem As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myElem = ie.document.getElementsByTagName("p")(0)
    
    ページ移動前 = myElem.innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    ページ移動後 = myElem.innerText
    
End Sub

 

りんごの木になっている「りんご」を取りに登った、変数の myElem君。

f:id:kouten0430:20180906165300j:plain

 

「りんご」を取ったあとに「みかん」も欲しくなったので、みかんの木に移動したけれど、みかんの木の登りかた(メソッド)が分からなかったので

f:id:kouten0430:20180906165449j:plain

 

「みかん」を手に入れることはできなかった・・・・・・。

 

要素ひとつを変数に取得し、ページ移動後、再取得した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myElem As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myElem = ie.document.getElementsByTagName("p")(0)
    
    ページ移動前 = myElem.innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    Set myElem = ie.document.getElementsByTagName("p")(0)
    
    ページ移動後 = myElem.innerText
    
End Sub

 

またまた、りんごの木になっている「りんご」を取りに登った myElem君。

「りんご」を取ったあとに「みかん」も欲しくなったので、みかんの木に移動し、みかんの木の登りかた(メソッド)を知っていたので

f:id:kouten0430:20180906165637j:plain

 

「みかん」も手に入れることができた。

 

同じ要素すべてを変数に取得し、ページ移動後、再取得せずに使用した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myCollection As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myCollection = ie.document.getElementsByTagName("p")
    
    ページ移動前 = myCollection(0).innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    ページ移動後 = myCollection(0).innerText
    
End Sub

 

myCollection君は大きなハンマーでりんごの木を叩き、「りんご」をすべて落としてから、「りんご」をひとつ入手した。

f:id:kouten0430:20180906165958j:plain

 

しばらくして「みかん」も欲しくなったので、みかんの木に移動したけれど、myCollection君はハンマーを持ってこなかったので、

f:id:kouten0430:20180906170012j:plain

 

「みかん」を手に入れることはできなかった・・・・・・。

 

同じ要素すべてを変数に取得し、ページ移動後、再取得した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myCollection As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myCollection = ie.document.getElementsByTagName("p")
    
    ページ移動前 = myCollection(0).innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    Set myCollection = ie.document.getElementsByTagName("p")
    
    ページ移動後 = myCollection(0).innerText
    
End Sub

 

myCollection君は「りんご」を取ったあと、みかんの木がある場所にハンマーを持ってきたので、「みかん」をすべて落とし、目的の「みかん」を入手することに成功した。

f:id:kouten0430:20180906170217j:plain

 

IDで指定した要素を変数に取得し、ページ移動後、再取得せずに使用した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myId As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myId = ie.document.getElementById("ichibanjukusita")
    
    ページ移動前 = myId.innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    ページ移動後 = myId.innerText
    
End Sub

 

myId君は、目印のついている実を打ち落とすことができる魔法の銃を持っています。まず、「りんご」を打ち落とし、

f:id:kouten0430:20180906170407j:plain

 

次にみかんの木に移動しました。・・・・・・が、目印を忘れてしまったため、

f:id:kouten0430:20180906170424j:plain

 

「みかん」を打ち落とすことができませんでした・・・・・・。

 

IDで指定した要素を変数に取得し、ページ移動後、再取得した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myId As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myId = ie.document.getElementById("ichibanjukusita")
    
    ページ移動前 = myId.innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    Set myId = ie.document.getElementById("ichibanjukusita")
    
    ページ移動後 = myId.innerText
    
End Sub

 

myId君は「りんご」を取ったあと、みかんの木に移動しました。今度は、目印の"ichibanjukusita"を思い出したので、

f:id:kouten0430:20180906170558j:plain

 

「みかん」も打ち落とすことができました。

 

documentプロパティでDOMを変数に取得し、ページ移動後、再取得せずに使用した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myDoc As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myDoc = ie.document
    
    ページ移動前 = myDoc.getElementsByTagName("p")(0).innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    ページ移動後 = myDoc.getElementsByTagName("p")(0).innerText
    
End Sub

 

myDocさんは、みんなに木の実の取り方を教えてくれる先生です。
myDocさんは、一つのから望遠鏡を使って、いろんな場所にある木のかたちを見ることができます。しかし、一回望遠鏡を使ってしまえば、あとは望遠鏡なしでも、いろんな場所にある木のかたちをライブで見ることができます。

f:id:kouten0430:20180906170740j:plain

 

これは、望遠鏡の使用で発動する myDocさんの特殊能力です。

 

documentプロパティでDOMを変数に取得し、ページ移動後、再取得した場合

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim myDoc As Object
    Dim ページ移動前 As String
    Dim ページ移動後 As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    Set myDoc = ie.document
    
    ページ移動前 = myDoc.getElementsByTagName("p")(0).innerText
    
    ie.navigate "C:\Users\みかん.html"
    
    Set myDoc = ie.document
    
    ページ移動後 = myDoc.getElementsByTagName("p")(0).innerText
    
End Sub

 

myDocさんは一回望遠鏡を使ってしまえば、あとは望遠鏡なしでもいろんな場所にある木のかたちをライブで見ることができます。だからその都度、望遠鏡を使っていろんな場所にある木を見ることができるのは当然ですね。

f:id:kouten0430:20180906170941j:plain

 

まとめ

8パターンの実験結果は、下記のようになりました。

方法 りんご入手 みかん入手
要素1個を変数に取得し、ページ移動後、変数をそのまま利用 OK NG
要素1個を変数に取得し、ページ移動後、変数に再取得して利用 OK OK
要素のすべてを変数に取得し、ページ移動後、変数をそのまま利用 OK NG
要素のすべてを変数に取得し、ページ移動後、変数に再取得して利用 OK OK
IDから要素1個を変数に取得し、ページ移動後、変数をそのまま利用 OK NG
IDから要素1個を変数に取得し、ページ移動後、変数に再取得して利用 OK OK
documentプロパティでDOMを変数に取得し、ページ移動後、変数をそのまま利用 OK OK
documentプロパティでDOMを変数に取得し、ページ移動後、変数に再取得して利用 OK OK

 
冒頭で結論を先に言ってしまったので繰り返しになりますが、

ページを移動したら

  • 要素は再取得する必要がある
  • DOM は(変数に入っていても)再取得しなくてよい。常に最新の状態である

 

ということが、実験で分かりました。

 

VBAでIEを操る(ポップアップや通知バーをSendKeysで操作する)

VBAIEを操作している途中、ポップアップや通知バーが出てきたらどうすればいいでしょうか?
今回は、その辺に焦点をあててみたいと思います。

ポップアップや通知バーが出たら、選択肢を選んで次へ進んだり、ファイルを保存したりする・・・・・・
これをオブジェクトとして操作する方法もあるようですが、正直、ちょっと煩雑です。

もともと、楽がしたくてIEの自動操作を行っているのに、そのコードを書くために長い時間を割いたり、結局上手くいかなくて何回も試行錯誤したり・・・・・・。
そのうち、本来の目的以上に時間を浪費してしまう矛盾に陥ってしまいます。まぁ、コードの技術は上がりますが(@_@;)

うー・・・ん。

まあ、その都度悩むのはやめにして、ポップアップや通知バーの操作はもれなく SendKeysメソッド で記述する作戦にしようと思います。

SendKeys とはなんぞや?

ということですが、これは、アクティブなアプリケーションにキーストロークを送信することができるメソッド・・・・・・
もうちょっと噛み砕いて言うと、人間が行うキーボード入力を再現することができるメソッド、とゆーことになります。

  • SendKeys 引数

引数には、送信するキーストロークを文字列式で指定します。

例えば、IEでファイルメニューを開くなら、Alt + F なので、下記のようにします。

  • SendKeys "%f"

 % はAltキーを押しながら、ということを表す特殊文字です。

キー操作をどのように表現するかは、MSDN 又はその他のサイトを参照下さいませ。

 

上記をふまえて、通知バーでファイル保存する操作を再現してみたいと思います。

と、その前に、IEがそもそも画面の前面にないと、キーストロークが他のアプリケーションに対して送信されるので、下記のWin APISetForegroundWindow関数IEを最前面に表示して、アクティブにしておきます。(下記は、Win32 API での例)

Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long

    SetForegroundWindow (ie.hWnd)

太字の行は、モジュールの先頭(宣言エリア)に記述します。

 

さて・・・・・・、話を戻しましょう。

ファイルを保存するリンクやボタンをクリックすると、下記のような通知バー(ポップアップの場合もある)が表示されます。

f:id:kouten0430:20180901155050j:plain

 

画像に表示されているように、「保存」にはショートカットキー"S"が割り当てられているのがわかります。ショートカットキーはAltキーとの同時押しなので、

  • SendKeys "%s", True

とすることで、名前を指定せずに保存できます。(第二引数の True は、キーストロークが渡るまで処理を中断することを表します)

ショートカットキーが効かないページや、名前を付けて保存したいような場合は、Alt + N で通知バーにフォーカスを当ててから、TABキーとEnterで操作することもできます。

まずは、Alt + N で通知バーにフォーカスを当てます。

  • SendKeys "%n", True

f:id:kouten0430:20180901155126j:plain

 

最初に、「実行」が選択されていることがわかります。次に、TABキーで移動し「保存」を選択します。

  • SendKeys "{TAB}", True

f:id:kouten0430:20180901155202j:plain

 

「名前を付けて保存」は、この状態で下方向を押すと現れ、さらに「保存」の下なので、下方向を2回送信します。

  • SendKeys "{DOWN 2}", True

f:id:kouten0430:20180901155228j:plain

 

あとは、Enterで名前を付けて保存します。

  • SendKeys "{ENTER}", True

このあと表示されるダイアログボックスで、SendKeys のみでファイル名を打ち込むことも可能です。(SendKeysの引数には変数を使うこともできるので、連番などを付加することも可能)

一見、メンドくさそうですが、キー操作を模擬するだけのことなのでカンタンです。

※ファイルを保存するには、URLを直接指定する専用のメソッドもありますが、CGIなどで都度ファイルが生成されるようなものはURLが特定できないので、今回のような SendKeys が有効だと思います。

VBAでIEを操る(処理・読み込みを待機する)

今回はIEの操作を行った後に発生する処理および読み込みを待って、次の操作を行うといったことに焦点を当てて話を進めていきたいと思います。
前回も書きましたが、読み込み完了を待たずに次の操作を行っても空振りする場合があるので、何らかの形で処理を待機させる必要があります。
処理を待機するには次の2通りが考えられると思います。

  • IEの状態を監視する
  • 時間的なウエイトを挿入する

 

IEの状態を監視する、については、書籍やネットでよく紹介されるのは下記のようなものです。

    Do While ie.Busy Or ie.readyState < READYSTATE_COMPLETE
        DoEvents
    Loop

 

ie.Busy は、名前の通り ieのBusy状態を取得するプロパティです。Busy(処理中)であれば Trueを、それ以外には Falseを返します。
ie.readyStateは、ドキュメントの読み込み状態を取得するプロパティです。読み込みの段階によって、0~4の数値が返されます(4で完了)。READYSTATE_COMPLETE は、4 を表す定数です。

この2つを組み合わせて、「Busy状態」または「読み込み段階が4未満」であれば待機する、というのが上記コードの内容です。

しかし・・・・・・世界に存在するあまたのWebページは千差万別であり、必ずしも上記コードで待機できる保障はありません_| ̄|○

 

そういう時は、時間的なウエイトを挿入するで解決します。Win64/32 API関数の Sleep を使って、単純に指定時間、処理を待機させます。

3秒待機するなら、下記のようにします。引数はミリ秒で指定します。(下記は、Win32 API での例)

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

        Sleep 3000

 

太字の箇所は、使用するWin32 API関数のありかをVBAに教えるためのもので、モジュールの先頭(宣言エリア)記述します。
Privateで宣言された関数はモジュール内でのみ使用可能で、Publicまたは省略で宣言された関数は他のモジュールでも使用可能になります。(この辺はまた別の機会に・・・・・・)

ただし、時間的なウエイトで待つというのは不確定要素なので、回線が込み合っていたり・サーバーが重くなっていたり・自身のPCのが重くなっていても確実に待機させるのなら、たっぷり時間を指定する必要があります。

 IEの状態を監視するのと、時間的なウエイトを挿入するのを組み合わせるのももちろんアリです。


どうしても、プログラムに最短で処理をさせたいのであれば、ページ毎に最適な待機方法をあみだす・・・・・・という手もあるのですが、IEの時代も終わりが見えてきているのに、それに時間をかけるのは私はナンセンスだと思います。

私はたっぷりと Sleepを入れて、プログラムが終わるまでは割り切って、コーヒーブレイクにするか、PC以外の仕事をするようにしてます。
一律 Sleepを思考停止ととらえるか、時間的コストダウンととらえるかは、人それぞれだと思います(@_@;)


前回紹介したコードに、Sleepをかませてループさせるものはこちら

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

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    Dim i As Integer
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    For i = 1 To 5
        ie.document.getElementsByClassName("hatena-star-add-button")(0).Click
        Sleep 1000
    Next i
    
End Sub

 

VBAでIEを操る(取得した要素を操作:☆ボタンをn回クリックする)

今回は、VBAから、テキストボックスに文字列を入力したり、セレクトボックスの項目を選択したり、チェックボックスラジオボタンをON・OFFしたり、リンクやボタンをクリックしたりしてみたいと思います。

前回、要素の取得方法について説明したので、今回は変数 myElem に操作対象の要素が取得されているものとして話を進めていきたいと思います。

目次

 

操作頻度が高い要素一覧

とりあえず、IE操作で使用頻度が高いと思われる要素、そのデータ型、プロパティ・メソッド(Value、checked、Click)をまとめてみました。

タグ名 Type Value checked Click データ型
INPUT text HTMLInputElement
INPUT radio HTMLInputElement
INPUT checkbox HTMLInputElement
INPUT button HTMLInputElement
INPUT submit HTMLInputElement
INPUT password HTMLInputElement
SELECT   HTMLSelectElement
TEXTAREA   HTMLTextAreaElement
a   HTMLAnchorElement

※「-」は、その要素では何も起こらない・エラーになる・設定する必要がない という意味で「-」としました。

 

要素ごとのコード記述例

各要素ごとに、コード記述例を軽く紹介します。 


テキストボックス

タグ名:INPUT
Type:text

 

テキストボックスに文字列を入力する場合

myElem.Value ="文字列"

 

テキストボックスに入力された文字列を取得する場合
myElem.Value

 


ラジオボタン

タグ名:INPUT
Type:radio

 

ラジオボタンをオン・オフする場合
myElem.Checked = True
myElem.Checked = False

 

ラジオボタンのオン・オフ状態を取得する場合
myElem.Checked

 

左クリックと同等の操作をする場合
myElem.Click

 


チェックボックス

タグ名:INPUT
Type:checkbox

 

チェックボックスをオン・オフする場合
myElem.Checked = True
myElem.Checked = False

 

チェックボックスのオン・オフ状態を取得する場合
myElem.Checked

 

左クリックと同等の操作をする場合
myElem.Click

 


タグ名:INPUT
Type:button

 

左クリックと同等の操作をする場合
myElem.Click

 


タグ名:INPUT
Type:submit

 

左クリックと同等の操作をする場合
myElem.Click

 


パスワード入力欄

タグ名:INPUT
Type:password

 

パスワード入力欄に文字列を入力する場合
myElem.Value ="文字列"

 

パスワード入力欄に入力された文字列を取得する場合
myElem.Value

 


セレクトボックス

タグ名:SELECT

 

セレクトボックスのリストから項目を選択する場合
myElem.Value ="5"

※下記のような、<SELECT>タグの内側にある<option>タグでリスト化された値(value)以外は選択できないので注意
<SELECT>
<option value="0"></option>
<option value="1">てすと1</option>
<option value="2">てすと2</option>
<option value="3">てすと3</option>
<option value="4">てすと4</option>
<option value="5">てすと5</option>
</SELECT>

 

セレクトボックスで、現在選択されている値を取得する場合
myElem(myElem.selectedIndex).Value

 


テキストエリア

f:id:kouten0430:20180818141252j:plain

タグ名:TEXTAREA

 

テキストエリアに文字列を入力する場合
myElem.Value ="文字列"

 

テキストエリアに入力された文字列を取得する場合
myElem.Value

 


ハイパーリンク

タグ名:a

 

ハイパーリンクをクリックする場合
myElem.Click

 


 

 

☆ボタンをクリックするコード 

 さて、ここまでを踏まえ、今回もブログ記事を表示したIEで少し遊んでみましょう。
VBAからClickメソッドを使って、ブログ記事の☆ボタン(いわゆるはてなスター)をクリックさせます。

ブラウザで記事を表示し、☆マークの上で右クリック → 要素の検証 などから、☆ボタンに Class属性"hatena-star-add-button" が付与されていることが確認できるので、これを使って要素を取得します。
タグ名は無視。

 

IEで、はてなブログ記事を表示した状態で下記コードを実行すると、☆(黄色)ボタンを1回クリックします。

Sub test()
    Dim ie As Object
    Dim sh As Object
    Dim win As Object
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    ie.document.getElementsByClassName("hatena-star-add-button")(0).Click

End Sub

 

1回じゃ足りん! 1万回押したる!!というなら、赤色の行を1万回ループすれば可能でございます。
☆ボタンクリック1万回は例え話ですが、そんな、気が遠くなるような繰り返し作業でも、マクロなら実行して後は寝て待つだけです。

 

補足:

実際は、☆ボタンをクリックした後、僅かに「読み込み」が発生します。
超高速でループしたところで、何回か空振りする運命なので、そうならないよう「読み込み」完了を待って次のクリックをさせる必要があります。
その辺については次回以降に。

VBAでIEを操る(DOMから要素を取得)

今回は、取得したIEからDOMを取得し、さらにそれぞれの要素を取得したいと思います。

DOMって何?

という声が聞こえたり、聞こえなかったり・・・・・・。

えーっと、私もあんまり詳しいわけじゃないですが、JavaScriptの書籍などから得た知識によると・・・・・・
Document Object Modelの略で、HTML文書をオブジェクトのように扱う仕組みのことだそうです。

前回、IEをオブジェクトとして取得するコードを掲載しましたが、IEには document というプロパティがあり、このプロパティから、HTML文書のオブジェクト化されたものを取得することができます。

こんな感じに図示してみました。

f:id:kouten0430:20180816115743j:plain

 

厳密には、このHTMLをオブジェクト化したものをDOMと呼ぶのか、これをオブジェクトのように扱う概念・仕様のことを DOM と呼ぶのか実はよく知らない(@_@;)

さて、このようにDOMを取得するのですが、少し勘違いしそうなのは、通常のオブジェクトツリーのように.(ピリオド)で区切って


ie.document.html.body.h1.プロパティ(またはメソッド)

 

というような記述には、残念ながらできません。

メンドウですが、要素、言い換えれば「タグ」を documentプロパティに用意された、getElementBy~(または、getElementsBy~) メソッド で、オブジェクトとして再取得する必要があります。(Element〔要素〕をget〔取得〕する)

f:id:kouten0430:20180816123316j:plain

 

たとえば、HTML文書中にこんなタグがあったとしたら・・・・・・、

 

<p id="test1" class="test2" name="test3">てすと</p>

 

このタグを取得する方法は下表の通りです。

メソッド 引数 使用例
getElementById id属性値で指定 ie.document.getElementById("test1")
getElementsByClassName Class属性値で指定 ie.document.getElementsByClassName("test2")
getElementsByName Name属性値で指定 ie.document.getElementsByName("test3")
getElementsByTagName タグ名で指定 ie.document.getElementsByTagName("p")

 

idはHTML文書中で唯一無二なので単数形(Element)。他は同じ文書中に複数存在し得るので複数形(Elements)です。

タグ名でpタグを取得するのなら、こんなイメージ。

f:id:kouten0430:20180816120552j:plain

 

id以外は複数形でコレクション扱いされるため、対象の要素を絞り込むためには、インデックスを指定するか、For Each ~Next などでひとつずつ評価します。
classや、nameの値が文書中に一つしかないことが分かっていれば、次のように決め打ちで取得できます。(コレクションのインデックス番号は 0 から始まることに注意)

 

Set p = ie.document.getElementsByClassName("test2")(0)
Set p = ie.document.getElementsByName("test3")(0)

 

pタグに挟まれた文字列 "てすと" で評価するなら、次のように For Each ~Next から取得できます。

For Each tmp In ie.document.getElementsByTagName("p")
    If tmp.innerText = "てすと" Then
        Set p = tmp
        Exit For
    End If
Next

 

さてさて、オブジェクトとして参照できたなら、あとはいつも通り

p.プロパティ
p.メソッド

のように、記述することができます。

オブジェクトにどのようなプロパティやメソッドが用意されているかは、インテリセンス(自動補完システム)で確認することができます。

DOMのインテリセンスをオンにするには、下記を参照設定します。

Microsoft HTML Object Library

 

ここまでをふまえ、当ブログ記事を表示したIEでちょっとした操作を試してみます。

2018-08-11の記事タイトルを変更してみる・・・・・・
ソースの表示などで、記事タイトルが<a>タグで挟まれていること、CSSを適用するためにClass属性が付加されていることに着目します。

f:id:kouten0430:20180816121720j:plain

f:id:kouten0430:20180816121731j:plain

 

ClassNameで決め打ちするなら次のようにします

Sub test()
    Dim ie As InternetExplorer
    Dim sh As Object
    Dim win As Object
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    ie.document.getElementsByClassName("entry-title-link bookmark")(0).innerText = "記事タイトルを変更してみた"

End Sub

 

aタグのコレクションを取得し、記事タイトルの文字列で評価するなら次のようにします

Sub test()
    Dim ie As InternetExplorer
    Dim sh As Object
    Dim win As Object
    Dim a As HTMLAnchorElement
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next
    
    For Each a In ie.document.getElementsByTagName("a")
        If a.innerText = "VBAでIEを操る(起動済みIEをシェルとして取得)" Then
            a.innerText = "記事タイトルを変更してみた"
            Exit For
        End If
    Next a
    
End Sub

 

 どちらも、結果、同じようになります。

 

変更前

f:id:kouten0430:20180816122447j:plain

 

変更後

f:id:kouten0430:20180816122500j:plain

 

でも・・・・・・、記事タイトルを変更してみたところで何のメリットもないっす(@_@;)←ぇ

 

次回以降、VBAから、テキストボックスに文字列を入力したり、セレクトボックスの項目を選択したり、チェックボックスラジオボタンをON・OFFしたり、リンクやボタンをクリックしたり、とか色々やってみたいと思います。

VBAでIEを操る(起動済みIEをシェルとして取得)

前回は、IEを起動させると同時にIEをオブジェクトとして取得しました。
しかし、CreateObjectで起動したIEは何も表示していないIEです。Webページを表示するには、下記のように navigate メソッドでURLを指定します。 

  • ie.navigate "URLをここに記載"

 

ではでは、起動済みの、IEをオブジェクトとして取得するにはどうすればいいでしょうか?

結論からいうと、前回の CreateObject("InternetExplorer.Application") を、CreateObject("Shell.Application") に変えて取得します。

以下、さらっと読んでもらい、とりあえず雰囲気をわかってもらえればOKです。(私もエラそうに語れるほど、詳しくはない・・・・・・)

起動済みのIEを取得するには、起動しているシェルをコレクションとして取得し、さらにシェルのコレクションの中からIEを取り出す必要があります。

シェルとはなぁに?

ってことですが、人間様は普段、OSの核(カーネル)に直接触れることができないので、外皮(シェル)の上から間接的に触れて(アクセスして)います。

f:id:kouten0430:20180811132918j:plain

その、間接的な役割である、シェル(shell32.dll)の機能を使っているプログラム

例えば、ファイルエクスプローラー、コントロールパネル、Internet Explorerなどの中から現在起動中のものを、コレクションとして取得します。

んで、コレクションの中からIEを探し出して取得するために、For Each ~Next でまわして評価するのです。

Nameプロパティで、IEか否か評価するなら次のようにします。

Sub test()
    Dim ie As InternetExplorer
    Dim sh As Object
    Dim win As Object
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        If win.Name = "Internet Explorer" Then
            Set ie = win
            Exit For
        End If
    Next

End Sub

 

ついでに、ローカルウィンドウで確認したところ、ファイルエクスプローラーやコントロールパネルのNameプロパティの値は、"エクスプローラー"でした。

 

しかし、上記のコードだとIEが複数起動している場合、どのIEが取得されるか分からないので、目的の(操作したいWebページを表示中の)IEを取得するなら、Titleプロパティ(要するにHTMLのTitleタグで書かれている部分)で判定します。

Titleプロパティで評価するなら次のようにします。

Sub test()
    Dim ie As InternetExplorer
    Dim sh As Object
    Dim win As Object
    Dim DocumentTitle As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        DocumentTitle = ""
        On Error Resume Next
        DocumentTitle = win.document.title
        On Error GoTo 0
        If DocumentTitle = "VBAの勉強を始めてみた" Then
            Set ie = win
            Exit For
        End If
    Next
End Sub

 

ファイルエクスプローラーやコントロールパネルは document.title プロパティをもっておらず、実行時エラーとなるため On Error Resume Next ~ On Error GoTo 0 で挟み、無視するようにしています。(無視されると、変数 DocumentTitle の中身は空白になります)

上記のコードで、当ブログのトップページを表示中のIEを取得します。

この状態で、ちょっとした操作をしてみましょう。

Sub test()
    Dim ie As InternetExplorer
    Dim sh As Object
    Dim win As Object
    Dim DocumentTitle As String
    
    Set sh = CreateObject("Shell.Application")
    
    For Each win In sh.Windows
        DocumentTitle = ""
        On Error Resume Next
        DocumentTitle = win.document.title
        On Error GoTo 0
        If DocumentTitle = "VBAの勉強を始めてみた" Then
            Set ie = win
            Exit For
        End If
    Next
    
    ie.document.getElementsByTagName("TITLE")(0).innerText = "タイトルを変更してみた"

End Sub

 

上記のコードを実行すると、下のようにタイトルが変更されます。

f:id:kouten0430:20180811133912j:plain

 

f:id:kouten0430:20180811134130j:plain

 

(ローカルのIE上で変更されているだけなので、サーバー上のデータにはもちろん影響ありません)

 

この辺については次回以降に。

 

補足:タイトルの変更だけなら document.titleプロパティ の値の変更だけでもできる

上記では汎用性がある、getElementsBy・・・・・・の形で変更していますが、タイトルだけなら、単純に、ie.document.title = "タイトルを変更してみた" でも変更できます。

 

補足:起動済みのIEを取得して操作するほうが、オートメーションエラーにならない?

今回のように、シェルから起動済みIEを取得したIEで操作したほうが、オートメーションエラーに悩まされずに済むようです。
というのも、CreateObject したIEでWebページを操作しようとしたところ、何回か「オートメーションエラー」が発生して困ったため(発生するページ、しないページ、さまざまですが)いろいろ調べてみたところ、原因としては、CreateObject した時の何も表示していないIEのセキュリティレベルと、navigateメソッドでの移動後でセキュリティレベルが変わってしまうことがあるためのようです。(というのをどこかのサイトで見た)

 

本記事は、以下の書籍

book.impress.co.jp

 及び、他の様々なサイト様の情報をパクって参考にしています。ご不明な点は書籍または他のサイト様を合わせて参考にして下さい。

VBAでIEを操る(IEをオブジェクトとして取得)

IEInternet Explorer)の後継としてEdgeが登場していますが、IEもまだまだ現役なハズ。私は個人で Chrome を主に使っていますが、会社では IE を使っています(使わされています)。

なので、IEを使った社内システムにおいて、入力作業などを半自動化、あわよくば全自動化したいと思っている今日この頃なのであります。

と、いうことで、今回はVBAIEを操る前の「IEをオブジェクトとして取得する」というところまでをやってみたいと思います。

ゆっくり進行ですが、よろしくお願いします。

IEをオブジェクトとして取得し、VBAで操作する」というと、裏技的でアウトローな響きがありますが、そんなことはなく、Excelであれ、Wordであれ、オブジェクトとして参照し、VBAで操作するという考え方は一緒です。操作対象がExcelからIEになっただけと考えましょう。

IEをうんぬんする前に、VBAとオブジェクトの関係を、私なりに図示してみました。

f:id:kouten0430:20180804125637j:plain

VBAは「ExcelVBA」と呼ばれることが多く、混同してしまいがちですが、VBAVBAExcelExcelなのです。VBAExcelのオブジェクトを参照し、操り人形しているだけです。
外部ライブラリ群に関しては、プログラムの補佐というイメージですが、補佐機能を発動するためにVBAが操っている、というニュアンスの違いだけで他のオブジェクトと同類です。(違っていたらゴメンなさい)

f:id:kouten0430:20180804125922j:plain

さてさて、

傀儡師(プログラム)の指先と、人形(オブジェクト)を、糸(参照)で繋ぐ作業をコードで記述すると Set myApp = CreateObject("XXX") のようになりますが、XXXの部分に図中のアプリケーションを当てはめると以下の通りです。

  • Set myApp = CreateObject("Excel.Application")
  • Set myApp = CreateObject("Word.Application")
  • Set myApp = CreateObject("Access.Application")
  • Set myApp = CreateObject("PowerPoint.Application")
  • Set myApp = CreateObject("InternetExplorer.Application")

 

あれ?いつも、VBAExcelを操作しているけど、Set myApp = CreateObject("Excel.Application")なんて、記述したことないよ。という声が聞こえてきそうですが・・・・・・

確かに、ExcelVBAでは、Excelに付属しているVBAなので、Excelに関してわざわざ CreateObject しなくてもよく、WordVBA、AccessVBA、PowerPointVBAなども同様です。
親切な反面、VBAとオブジェクトの関係をわかりづらくしているような気もしますね(@_@;)

 

さて、今回の本題であるIEの制御ですが、IEVBAは付属していないので、Excel、Word、AccessPowerPointのいずれかに付属しているVBAで、CreateObjectしてやります。

あわせて、参照設定をしておけばインテリセンス(自動補完システム)が働くので便利です。
参照設定を行う際は、以下の項目にチェックを入れます。

 

ここまでを踏まえて、IEをオブジェクトとして取得するコードを記述してみましょう。

Sub test()
    Dim myApp As Object
    Set myApp = CreateObject("InternetExplorer.Application")
    myApp.Visible = True
End Sub


上記のコードを実行すると、何も表示していない、空っぽのIEが立ち上がったはずです。
myApp.Visible = True で画面表示させています。これをしなければ、非表示でバックグラウンドで立ち上がった状態になります。

このコードを実行後、VBEでオブジェクトブラウザーを開くと、IEオブジェクトのメンバがずらりと並んでいることを確認することができます。
(事前に参照設定してあることが前提ですが)

しかし、これだけでは、IEをオブジェクトとして取得しただけなので、プロパティやメソッドの種類や使用方法を知らなければ何にもできません。
その辺については次回以降に。