VBAの勉強を始めてみた

色々試しています。

指定位置(セル)に瞬間移動する方法

エクセルで「列がAAAで行が30000のセルを、表示して下さい」と言われたら、何秒以内に表示できるでしょうか?私がやってみたところ・・・・・・A1セルを表示した状態から、通常のスクロールのみで1分以上かかりました。
時間がかかることと、動く数字やアルファベットを目で追うのは、ストレスや眼精疲労の原因になります。

私はストレスや眼精疲労がこの世で一番嫌いです(@_@;)

なので、今回は指定位置(セル)に瞬間移動する方法を紹介しましょう。

 

標準機能で瞬間移動する

これはとてもカンタンです。
F5キーを押して表示されたボックスに、瞬間移動したい位置を入力してOKします。(A1形式で入力します)

f:id:kouten0430:20180914145359j:plain

 

一度移動した所へは、履歴から選んで瞬間移動することもできます。

f:id:kouten0430:20180914142707j:plain

 

ただし、エクセルを閉じると履歴はリセットされます。 

この標準機能での瞬間移動は、指定位置(セル)を表示し、かつ選択状態にします。


マクロで瞬間移動する

「表示はさせたいけど、セルの選択はしなくていい・・・・・・(>_<)」

という人は、VBAScrollRowプロパティScrollColumnプロパティを使ってみて下さい。ScrollRowプロパティでは指定行をウィンドウの上端に表示させることができ、ScrollColumnプロパティでは指定列をウィンドウの左端に表示させることができます。
この二つを組み合わせて、指定位置を左上に表示させます。

ActiveWindow.ScrollRow = 行番号
ActiveWindow.ScrollColumn = 列番号

 

セルの選択状態は変えず指定位置を左上に表示させるだけなので、表示変更後に任意のセルを Shift + 左クリックすることで範囲選択することもできます。 

 

表示させる位置を、行番号と列番号で指定するサンプルコードはこちら

Sub 指定位置に瞬間移動()
    Dim y As Variant
    Dim x As Variant
    Dim flag As Boolean
    
    y = InputBox("表示する「行」を数値で入力" & vbCrLf & "(行はこのままで良い場合、空白 or キャンセル)")
    
    If y = "" Then
        y = ActiveWindow.ScrollRow
    ElseIf y > 1048576 Then
        y = 1048576
        flag = True
    ElseIf y < 1 Then
        y = 1
    End If
    
    x = InputBox("表示する「列」を数値で入力" & vbCrLf & "(列はこのままで良い場合、空白 or キャンセル)")
    
    If x = "" Then
        x = ActiveWindow.ScrollColumn
    ElseIf x > 16384 Then
        x = 16384
        flag = True
    ElseIf x < 1 Then
        x = 1
    End If

    ActiveWindow.ScrollRow = y
    ActiveWindow.ScrollColumn = x
    If flag Then MsgBox "いしのなかにいる!"
    
End Sub

 

表示させる位置を、行番号と列のアルファベットで指定するサンプルコードはこちら

Sub 指定位置に瞬間移動アルファベットで指定版()
    Dim y As Variant
    Dim x As String
    Dim flag As Boolean
    
    y = InputBox("表示する「行」を数値で入力" & vbCrLf & "(行はこのままで良い場合、空白 or キャンセル)")
    
    If y = "" Then
        y = ActiveWindow.ScrollRow
    ElseIf y > 1048576 Then
        y = 1048576
        flag = True
    ElseIf y < 1 Then
        y = 1
    End If
    
retry:
    x = InputBox("表示する「列」をアルファベットで入力" & vbCrLf & "(列はこのままで良い場合、空白 or キャンセル)")
    
    If x <> "" Then
        x = StrConv(x, vbNarrow)
        x = StrConv(x, vbUpperCase)
        If x Like "*[!A-Z]*" Then
            MsgBox "列はアルファベットのみ入力可"
            GoTo retry
        End If
        On Error GoTo ErrorHandler
        ActiveWindow.ScrollColumn = Range(x & "1").Column
        On Error GoTo 0
    End If

    ActiveWindow.ScrollRow = y
    If flag Then MsgBox "いしのなかにいる!"
    
    Exit Sub
    
ErrorHandler:
    x = "XFD"
    flag = True
    Resume
    
End Sub


は変わりますが・・・・・・、ウィザードリィというゲームで瞬間移動する際に座標を間違えると、通路以外の場所に飛んでしまい

f:id:kouten0430:20180914142403j:plain

 

というメッセージが表示され、パーティが全滅します。

子供の頃、なけなしの小遣いでファミコン版「ウィザードリィIII ダイヤモンドの騎士」を買ったけれど、これに心を折られ、ソフトを中古屋に瞬間移動させた記憶があります←(ぇ)

 

全然関係ない話ですけどね。

 

A1セルに瞬間移動する

表示を、A1セルに戻すサンプルコードはこちら

Sub A1に瞬間移動()
    ActiveWindow.ScrollRow = 1
    ActiveWindow.ScrollColumn = 1
    
End Sub

 

※コードの使用方法

  • SubからEnd Subまでをコピーし、標準モジュール等に貼り付けて使用して下さい。なお、マクロで実行した処理は「元に戻す」ことができません。実行前に一旦保存しやり直しのできる状態にしておいて下さい。標準モジュールにコードを貼り付けてマクロを使用する方法はこちら

VBAでIEを操る(フレームについて)

今回は、フレームについてです。
社内システムでは未だに使われていたりするので、いちおう書いときましょう。

 

フレームとは?

複数個に分割した画面に、それぞれ別のページを表示する機能です。下の例では、3つのHTMLファイルが用意されます。

f:id:kouten0430:20180909000454j:plain

・構成.html(画面表示なし)

  • ユーザーは、この構成用HTMLファイルを読み込むことになります。分割の縦横サイズの設定、分割画面に表示するHTMLファイルのURLなどが記されています。

・メニュー.html(画面左)

・内容.html(画面右)

 

フレーム構造をもったページを操作する

この場合、IE直下のdocumentプロパティでは、フレーム以下のDOMまでは取得できません。

  • 誤:

f:id:kouten0430:20180909001221j:plain

 

frameタグから、さらにdocumentプロパティを参照する必要があります。

  • 正:

f:id:kouten0430:20180909000832j:plain

 

構成.htmlが「親」だとしたら、メニュー.htmlと内容.htmlが「子」です。
もし「孫」「ひ孫」があっても、同じようにそれぞれdocumentプロパティを参照する必要があります。

 

 フレーム以下の要素を取得する場合の記述

一行で記述するならこうです。

  • Set myElem = ie.document.frames(0).document.getElementsByTagName("p")(0)

※取得したいフレーム、要素がそれぞれインデックス0である場合

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したり、リンクやボタンをクリックしたり、とか色々やってみたいと思います。