指定位置(セル)に瞬間移動する方法
エクセルで「列がAAAで行が30000のセルを、表示して下さい」と言われたら、何秒以内に表示できるでしょうか?私がやってみたところ・・・・・・A1セルを表示した状態から、通常のスクロールのみで1分以上かかりました。
時間がかかることと、動く数字やアルファベットを目で追うのは、ストレスや眼精疲労の原因になります。
私はストレスや眼精疲労がこの世で一番嫌いです(@_@;)
なので、今回は指定位置(セル)に瞬間移動する方法を紹介しましょう。
標準機能で瞬間移動する
これはとてもカンタンです。
F5キーを押して表示されたボックスに、瞬間移動したい位置を入力してOKします。(A1形式で入力します)
一度移動した所へは、履歴から選んで瞬間移動することもできます。
ただし、エクセルを閉じると履歴はリセットされます。
この標準機能での瞬間移動は、指定位置(セル)を表示し、かつ選択状態にします。
マクロで瞬間移動する
「表示はさせたいけど、セルの選択はしなくていい・・・・・・(>_<)」
という人は、VBAでScrollRowプロパティと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
話は変わりますが・・・・・・、ウィザードリィというゲームで瞬間移動する際に座標を間違えると、通路以外の場所に飛んでしまい
というメッセージが表示され、パーティが全滅します。
子供の頃、なけなしの小遣いでファミコン版「ウィザードリィIII ダイヤモンドの騎士」を買ったけれど、これに心を折られ、ソフトを中古屋に瞬間移動させた記憶があります←(ぇ)
全然関係ない話ですけどね。
A1セルに瞬間移動する
表示を、A1セルに戻すサンプルコードはこちら
Sub A1に瞬間移動() ActiveWindow.ScrollRow = 1 ActiveWindow.ScrollColumn = 1 End Sub
※コードの使用方法
- SubからEnd Subまでをコピーし、標準モジュール等に貼り付けて使用して下さい。なお、マクロで実行した処理は「元に戻す」ことができません。実行前に一旦保存しやり直しのできる状態にしておいて下さい。標準モジュールにコードを貼り付けてマクロを使用する方法はこちら。
VBAでIEを操る(フレームについて)
今回は、フレームについてです。
社内システムでは未だに使われていたりするので、いちおう書いときましょう。
フレームとは?
複数個に分割した画面に、それぞれ別のページを表示する機能です。下の例では、3つのHTMLファイルが用意されます。
・構成.html(画面表示なし)
- ユーザーは、この構成用HTMLファイルを読み込むことになります。分割の縦横サイズの設定、分割画面に表示するHTMLファイルのURLなどが記されています。
・メニュー.html(画面左)
・内容.html(画面右)
フレーム構造をもったページを操作する
この場合、IE直下のdocumentプロパティでは、フレーム以下のDOMまでは取得できません。
- 誤:
frameタグから、さらにdocumentプロパティを参照する必要があります。
- 正:
構成.htmlが「親」だとしたら、メニュー.htmlと内容.htmlが「子」です。
もし「孫」「ひ孫」があっても、同じようにそれぞれdocumentプロパティを参照する必要があります。
フレーム以下の要素を取得する場合の記述
一行で記述するならこうです。
- Set myElem = ie.document.frames(0).document.getElementsByTagName("p")(0)
※取得したいフレーム、要素がそれぞれインデックス0である場合
VBAでIEを操る(ページ移動後にDOMや要素を再取得しなかったらどうなるのか?)
今回は、変数に取得したDOMや要素が、ページ移動後、どんな挙動になるか調べてみます。
ページを移動した後、DOMツリー構造が変わるので、再取得する必要があるだろう・・・・・・と予想できます。が、実際はどうなのか、書籍やネットから腑に落ちる情報を見つけられなかったので、実験してみます。
目次
- 要素ひとつを変数に取得し、ページ移動後、再取得せずに使用した場合
- 要素ひとつを変数に取得し、ページ移動後、再取得した場合
- 同じ要素すべてを変数に取得し、ページ移動後、再取得せずに使用した場合
- 同じ要素すべてを変数に取得し、ページ移動後、再取得した場合
- IDで指定した要素を変数に取得し、ページ移動後、再取得せずに使用した場合
- IDで指定した要素を変数に取得し、ページ移動後、再取得した場合
- documentプロパティでDOMを変数に取得し、ページ移動後、再取得せずに使用した場合
- documentプロパティでDOMを変数に取得し、ページ移動後、再取得した場合
- まとめ
実験に使うのは、以下のHTML文書です。
ページ移動前
↑このページのソース
<html>
<body>
<p id="ichibanjukusita">りんご</p>
<p>りんご</p>
<p>りんご</p>
<p>りんご</p>
<p>りんご</p>
</body>
</html>
ページ移動後
↑このページのソース
<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君。
「りんご」を取ったあとに「みかん」も欲しくなったので、みかんの木に移動したけれど、みかんの木の登りかた(メソッド)が分からなかったので
「みかん」を手に入れることはできなかった・・・・・・。
要素ひとつを変数に取得し、ページ移動後、再取得した場合
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君。
「りんご」を取ったあとに「みかん」も欲しくなったので、みかんの木に移動し、みかんの木の登りかた(メソッド)を知っていたので
「みかん」も手に入れることができた。
同じ要素すべてを変数に取得し、ページ移動後、再取得せずに使用した場合
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君は大きなハンマーでりんごの木を叩き、「りんご」をすべて落としてから、「りんご」をひとつ入手した。
しばらくして「みかん」も欲しくなったので、みかんの木に移動したけれど、myCollection君はハンマーを持ってこなかったので、
「みかん」を手に入れることはできなかった・・・・・・。
同じ要素すべてを変数に取得し、ページ移動後、再取得した場合
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君は「りんご」を取ったあと、みかんの木がある場所にハンマーを持ってきたので、「みかん」をすべて落とし、目的の「みかん」を入手することに成功した。
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君は、目印のついている実を打ち落とすことができる魔法の銃を持っています。まず、「りんご」を打ち落とし、
次にみかんの木に移動しました。・・・・・・が、目印を忘れてしまったため、
「みかん」を打ち落とすことができませんでした・・・・・・。
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"を思い出したので、
「みかん」も打ち落とすことができました。
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さんは、一つの窓から望遠鏡を使って、いろんな場所にある木のかたちを見ることができます。しかし、一回望遠鏡を使ってしまえば、あとは望遠鏡なしでも、いろんな場所にある木のかたちをライブで見ることができます。
これは、望遠鏡の使用で発動する 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さんは一回望遠鏡を使ってしまえば、あとは望遠鏡なしでもいろんな場所にある木のかたちをライブで見ることができます。だからその都度、望遠鏡を使っていろんな場所にある木を見ることができるのは当然ですね。
まとめ
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で操作する)
VBAでIEを操作している途中、ポップアップや通知バーが出てきたらどうすればいいでしょうか?
今回は、その辺に焦点をあててみたいと思います。
ポップアップや通知バーが出たら、選択肢を選んで次へ進んだり、ファイルを保存したりする・・・・・・
これをオブジェクトとして操作する方法もあるようですが、正直、ちょっと煩雑です。
もともと、楽がしたくてIEの自動操作を行っているのに、そのコードを書くために長い時間を割いたり、結局上手くいかなくて何回も試行錯誤したり・・・・・・。
そのうち、本来の目的以上に時間を浪費してしまう矛盾に陥ってしまいます。まぁ、コードの技術は上がりますが(@_@;)
うー・・・ん。
まあ、その都度悩むのはやめにして、ポップアップや通知バーの操作はもれなく SendKeysメソッド で記述する作戦にしようと思います。
SendKeys とはなんぞや?
ということですが、これは、アクティブなアプリケーションにキーストロークを送信することができるメソッド・・・・・・
もうちょっと噛み砕いて言うと、人間が行うキーボード入力を再現することができるメソッド、とゆーことになります。
- SendKeys 引数
引数には、送信するキーストロークを文字列式で指定します。
例えば、IEでファイルメニューを開くなら、Alt + F なので、下記のようにします。
- SendKeys "%f"
% はAltキーを押しながら、ということを表す特殊文字です。
キー操作をどのように表現するかは、MSDN 又はその他のサイトを参照下さいませ。
上記をふまえて、通知バーでファイル保存する操作を再現してみたいと思います。
と、その前に、IEがそもそも画面の前面にないと、キーストロークが他のアプリケーションに対して送信されるので、下記のWin APIのSetForegroundWindow関数でIEを最前面に表示して、アクティブにしておきます。(下記は、Win32 API での例)
Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
SetForegroundWindow (ie.hWnd)
太字の行は、モジュールの先頭(宣言エリア)に記述します。
さて・・・・・・、話を戻しましょう。
ファイルを保存するリンクやボタンをクリックすると、下記のような通知バー(ポップアップの場合もある)が表示されます。
画像に表示されているように、「保存」にはショートカットキー"S"が割り当てられているのがわかります。ショートカットキーはAltキーとの同時押しなので、
- SendKeys "%s", True
とすることで、名前を指定せずに保存できます。(第二引数の True は、キーストロークが渡るまで処理を中断することを表します)
ショートカットキーが効かないページや、名前を付けて保存したいような場合は、Alt + N で通知バーにフォーカスを当ててから、TABキーとEnterで操作することもできます。
まずは、Alt + N で通知バーにフォーカスを当てます。
- SendKeys "%n", True
最初に、「実行」が選択されていることがわかります。次に、TABキーで移動し「保存」を選択します。
- SendKeys "{TAB}", True
「名前を付けて保存」は、この状態で下方向を押すと現れ、さらに「保存」の下なので、下方向を2回送信します。
- SendKeys "{DOWN 2}", True
あとは、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
テキストエリア
タグ名: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文書のオブジェクト化されたものを取得することができます。
こんな感じに図示してみました。
厳密には、このHTMLをオブジェクト化したものをDOMと呼ぶのか、これをオブジェクトのように扱う概念・仕様のことを DOM と呼ぶのか実はよく知らない(@_@;)
さて、このようにDOMを取得するのですが、少し勘違いしそうなのは、通常のオブジェクトツリーのように.(ピリオド)で区切って
ie.document.html.body.h1.プロパティ(またはメソッド)
というような記述には、残念ながらできません。
メンドウですが、要素、言い換えれば「タグ」を documentプロパティに用意された、getElementBy~(または、getElementsBy~) メソッド で、オブジェクトとして再取得する必要があります。(Element〔要素〕をget〔取得〕する)
たとえば、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タグを取得するのなら、こんなイメージ。
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 = "てすと" ThenSet p = tmp Exit For End If Next
さてさて、オブジェクトとして参照できたなら、あとはいつも通り
p.プロパティ
p.メソッド
のように、記述することができます。
オブジェクトにどのようなプロパティやメソッドが用意されているかは、インテリセンス(自動補完システム)で確認することができます。
DOMのインテリセンスをオンにするには、下記を参照設定します。
Microsoft HTML Object Library
ここまでをふまえ、当ブログ記事を表示したIEでちょっとした操作を試してみます。
2018-08-11の記事タイトルを変更してみる・・・・・・
ソースの表示などで、記事タイトルが<a>タグで挟まれていること、CSSを適用するためにClass属性が付加されていることに着目します。
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
どちらも、結果、同じようになります。
変更前
変更後
でも・・・・・・、記事タイトルを変更してみたところで何のメリットもないっす(@_@;)←ぇ
次回以降、VBAから、テキストボックスに文字列を入力したり、セレクトボックスの項目を選択したり、チェックボックス・ラジオボタンをON・OFFしたり、リンクやボタンをクリックしたり、とか色々やってみたいと思います。