Webスクレイピング 1Internet Explorerの操作とポイント 2020.07.26
2020年時点での内容です。WindowsやOfficeのバージョンによっては動作しない可能性があります。インターネットエクスプローラーのサポート終了に伴い、本ページのコードは現在では動作しません。記録として残しています。
VBAからInternet Explorer(以下IE)を制御してのWebスクレイピング※に興味を持ったので、試してみました。「ライブラリ(COMコンポーネント)」を利用し、VBAから Internet Explorer(以下、IE)を制御するといった内容ですが、いくつか気を付けることがあったので備忘録として残すことにしました。
※[Webスクレイピング] ウェブサイト上の情報を取得し、それを加工して新たな情報を生成すること
備忘録とあわせ、実践状況を記録として残す。現時点では以下。
●任意のキーワードによるGoogle検索の結果(タイトル、リンク先)をシートに書き出し
ポイント
・VBEの参照設定
・IE起動
・プログラムの待機
・待機が一定時間を超えるとプログラム終了
・CPUの負荷軽減
IE起動と留意点
1.VBEの参照設定
VBAからIEを制御するには、参照設定で次のライブラリを追加することが望ましい。
・Microsoft HTML Object Library
・Microsoft Internet Controls
まずこれらを設定する。
VBEのメニューバー「ツール」→「参照設定」を選択。
表示されたダイアログで「Microsoft HTML Object Library」「Microsoft Internet Controls」ライブラリを探し、チェックをいれて「OK」。
参照設定が適切であれば、インテリセンスに追加されている。
2.インスタンスの生成とIEの表示
IEを扱うプログラムではまず、IEのインスタンスを生成する。インスタンスを生成するとIEを制御できるようになる。
Sub Sample1() 'VBAからIEを起動し本サイトトップページを表示 Dim objIE As New InternetExplorer 'インスタンス生成 objIE.Visible = True 'IEを表示 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 End Sub
●「Sample1」実行結果
オブジェクト型変数を宣言するときに「New」キーワードを指定すると、オブジェクトのインスタンスが生成される(参照設定がされていることが前提)。Setステートメントで改めてオブジェクトへの参照を代入する必要はない。
コード途中の「objIE.Visible = True」はIEの画面表示プロパティで、「True」は表示、「False」は非表示である。
IEを画面に表示しなくとも、バックグラウンドでは動作しているので、画面で確認する必要がなければ、表示しなくても構わない。「Web上の情報を取得しシートに反映する」といった処理等では表示の必要性は低い。
インスタンスを複数生成すれば、それぞれでIEを起動できる。
Sub Sample2() '複数のIEを起動し「Excel Tips for Learners」を表示 Dim objIE_1 As New InternetExplorer 'インスタンス生成1 Dim objIE_2 As New InternetExplorer 'インスタンス生成2 objIE_1.Visible = True 'IE1を表示 objIE_2.Visible = True 'IE2を表示 objIE_1.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込1 objIE_2.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込2 End Sub
●「Sample2」実行結果
配列を用いて複数のインスタンスを生成し、IEを起動する例。
Sub Sample2_ex() '複数のIEを起動し本サイトトップページを表示 Dim objIE(1 To 3) As New InternetExplorer Dim i As Long For i = 1 To 3 objIE(i).Visible = True objIE(i).navigate "https://excel.syogyoumujou.com/index.html" Next End Sub
【補足情報】
プログラム内でIEオブジェクトの参照を生成(インスタンス生成)すれば「参照設定」を行わなくてもIEは起動できる。だが、インテリセンス表示がなかったり、コードを追加する必要があるため、「参照設定」することが望ましい。
「参照設定」をせずにIEを起動する例
Sub Sample1_ex() '「参照設定」をせずにVBAからIEを起動し本サイトトップページを表示 Dim objIE As Object 'オブジェクト型変数の宣言 Set objIE = CreateObject("Internetexplorer.Application") 'IEオブジェクトのインスタンス生成 objIE.Visible = True 'IEを表示 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 End Sub
CreateObject関数は、ActiveXオブジェクトの参照を生成して返す。参照設定なしでActiveXオブジェクト (Dictionary、FileSystemObject等) を使用したいときに用いられる。
3.プログラムの待機 1
Webページからテキスト等のデータを取得する場合、IEのドキュメント(Document)オブジェクトを使用する。Documentオブジェクトは、IEのページ読み込みが終わらないと取得できない。そのため、ページの読み込みが終わるまで、プログラムを待機させる必要がある。
次のコードを実行すると、問題がなければ直ぐにページが表示されるが、内部処理では、ページ読み込みが終わるまでプログラムを待機し、IEを表示している。
Sub Sample3() 'ページ読込が終わるまでプログラムを待機 Dim objIE As New InternetExplorer 'インスタンス生成 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 Do 'Do-Loop間で読込待機。 Loop Until objIE.Busy = False And objIE.readyState = READYSTATE_COMPLETE objIE.Visible = True 'IEを表示 End Sub
プログラムの中では、Do-Loop間の繰り返しで待機し、Busyプロパティが「False」で、かつReadyStateプロパティが「READYSTATE_COMPLETE」になったら処理を抜ける。
Busyプロパティ
値の取得のみ可能なプロパティで、Webページが読み込み中かどうかを返す。値は「True」か「False」で、読み込み中だと「True」。複数のフレームで構成されたページの場合、フレームの読み込み毎に一旦「False」を返すようである。
ReadyStateプロパティ
値の取得のみ可能なプロパティで、IEオブジェクトのドキュメント読み込み状態を返す。読み込み状態は5段階あり、全データ読み込み完了が「READYSTATE_COMPLETE」である。「Microsoft Internet Controls」の組み込み定数として確認することができる。
組み込み定数の実態は数値であり、数値を直接使用することも可能である。それぞれの定数に対応する数値は以下である。
組み込み定数 | 数値 | 状態 |
---|---|---|
READYSTATE_UNINITIALIZED | 0 | デフォルト値/未完了 |
READYSTATE_LOADING | 1 | ロード中 |
READYSTATE_LOADED | 2 | ロード完了/操作不可能 |
READYSTATE_INTERACTIVE | 3 | 操作可能 |
READYSTATE_COMPLETE | 4 | 全データ読み込み完了 |
4.プログラムの待機 2
ページを読み込む際、何らかの原因によって読み込みが終わらないケースもあるかもしれない。先に提示したコードでは、読み込みが終わらない場合、Do-Loopが無限ループに陥る可能性がある。それを防ぐには、Do-Loopで一定時間経過するとループを抜けるコードを追加すればよい。
コメント赤字部が追加箇所。
Sub Sample4() 'ページ読込が終わるまでプログラムを待機。読込に10秒以上かかったら強制終了 Dim datWait As Date 'Date型変数(日付と時間用) Dim objIE As New InternetExplorer 'インスタンス生成 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 datWait = Now() + TimeValue("00:00:10") '10秒後の時間を格納 Do 'Do-Loop間で読込待機。 If datWait < Now() Then Exit Do '10秒以上経過したらループを抜ける Loop Until objIE.Busy = False And objIE.readyState = READYSTATE_COMPLETE objIE.Visible = True 'IEを表示 End Sub
Now関数とTimeValue関数で、10秒後の時間を予めDate型変数に格納し、Do-Loop内のNow関数が、変数の時間を超えたらループを抜けている。
5.プログラムの待機 3
VBAはプログラムの実行中、CPUに負荷がかかる。今回のようなDo-Loopでプログラムを待機させる場合、高い負荷が継続することがあるため、負荷の軽減が望ましい。軽減のためにはWin32APIのSleep関数を用いるとよい。
Win32API関数を利用する場合は、予め標準モジュールの宣言セクション(モジュールの先頭)に宣言を記述する必要がある。Excelのバージョンによって宣言が異なるため、分岐を用いた例を下に示す。
'標準モジュールの宣言セクションに記述 #If VBA7 Then 'Excel2010以上の場合 Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) #Else 'Excel2007以下の場合 Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #End If
Sub Sample5() 'ページ読込が終わるまでプログラムを待機。CPUの不可軽減策 Dim datWait As Date 'Date型変数(日付と時間用) Dim objIE As New InternetExplorer 'インスタンス生成 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 datWait = Now() + TimeValue("00:00:10") '10秒後の時間を格納 Do 'Do-Loop間で読込待機。 If datWait < Now() Then End '10秒以上経過したら強制終了 Call Sleep(50) '0.05秒処理を中断 Loop Until objIE.Busy = False And objIE.readyState = READYSTATE_COMPLETE objIE.Visible = True 'IEを表示 End Sub
Win32API
APIとは「Application Programming Interfaces」の略で、アプリケーションからOSを操作するためのもの。Win32APIは、32bitのWindowsの機能を利用するためのもの。
Sleep関数
呼び出し側スレッドを一定時間だけ中断。中断する時間をミリ秒で指定。1秒=1000ミリ秒。
6.新たなページの読み込みと画面更新
IEで新たなページを読み込む場合は、該当のIEオブジェクトで再度、Navigateメソッドを実行する。
Sub Sample6() '新たなページの読み込み Dim datWait As Date 'Date型変数(日付と時間用) Dim objIE As New InternetExplorer 'インスタンス生成 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 datWait = Now() + TimeValue("00:00:10") '10秒後の時間を格納 Do 'Do-Loop間で読込待機。 If datWait < Now() Then End '10秒以上経過したら強制終了 Loop Until objIE.Busy = False And objIE.readyState = READYSTATE_COMPLETE objIE.Visible = True 'IEを表示 Application.Wait Now() + TimeValue("00:00:02") '2秒待機 '///新たなページを読み込み objIE.navigate "https://excel.syogyoumujou.com/freesoft/analysis_sudoku.html" End Sub
IEでページを再読み込みする場合は、IEオブジェクトのRefreshメソッドを実行する。
Sub Sample7() 'ページの更新 Dim datWait As Date 'Date型変数(日付と時間用) Dim objIE As New InternetExplorer 'インスタンス生成 objIE.navigate "https://excel.syogyoumujou.com/index.html" 'ページ読込 datWait = Now() + TimeValue("00:00:10") '10秒後の時間を格納 Do 'Do-Loop間で読込待機。 If datWait < Now() Then End '10秒以上経過したら強制終了 Loop Until objIE.Busy = False And objIE.readyState = READYSTATE_COMPLETE objIE.Visible = True 'IEを表示 Application.Wait Now() + TimeValue("00:00:02") '2秒待機 objIE.Refresh 'ページ更新 End Sub
参考サイト
VBAのIE制御入門
【エクセルVBAでIE操作】10分で終わるセッティングとWEBページの閲覧確認
[VBA]30分あればできるVBAスクレイピング
【ExcelVBAでスクレイピング入門】
VBAによるWebコンテンツの取得