VBAプログラム開発、スクレイピング【1】取得準備をしてデータ取得ができることを確認する
VBAコーディングによるプログラム作成を実施していくこととなりました。
まずスクレイピングを実施したいと思っていますので、スクレイピングをするための準備から始めることにしました。
今回の目的
実際にスクレイピングを行い、データが取得できていることを確認する
なぜやるか
環境を整備しておくことで、常にコーディングのみでスクレイピングができる状態にしておくため
やりたいこと
やったこと
実施内容
環境を確認する
まずは元となるPCの環境です。
OS:Microsoft Windows 7 Professional Service Pack 1
今となっては結構古い環境ですが…この環境で作成を行います。
VBAスクレイピング実施方法を確認する
まずはスクレイピング実施にあたり、手順を確認しました。
こちらを参考にさせて頂きました、ありがとうございます。
こちらを元に設定を実施します。
まずはVBEを開きます。
ツールバーのツールから参照設定を選択。
ライブラリ選択画面
以下のライブラリを追加します。
- Microsoft Internet Controls
VBAでインターネットエクスプローラを操作するのに使用
- Microsoft HTML Object Library
VBAでHTMLを扱うのに使用
チェックをいれてOKを押下してライブラリを適用します。
スクレイピングを動作させてみる
実際にスクレイピングによるデータ取得ができることを確認します。
参考サイトのコードをベースに書籍管理ページの一覧からタイトル名を一つ取得してホップアップ表示します。
# test
Sub test()
Dim objIE As InternetExplorer
Set objIE = CreateObject("Internetexplorer.Application")
objIE.Visible = True
objIE.navigate "https://protected-fortress-61913.herokuapp.com/book"
Call WaitResponse(objIE)
Dim htmlDoc As HTMLDocument
Set htmlDoc = objIE.document
MsgBox htmlDoc.getElementsByClassName("list-book-title")(0).innerHTML
End Sub
Sub WaitResponse(objIE As Object)
Do While objIE.Busy = True Or objIE.readyState < READYSTATE_COMPLETE
DoEvents
Loop
End Sub
こちらを動作させて、ホップアップを確認します。
~/bookにアクセスして最初の本のタイトルを出力しました。
スクレイピング処理によってサイトからデータが取得できていることが確認できました。
ここからコードについて詳しく見ていきます。
コードの確認
変数宣言の実施
スクレイピング用プロシージャの冒頭部分から確認です。
Dim objIE As InternetExplorer
Set objIE = CreateObject("Internetexplorer.Application")
objIEはIEのオブジェクトであることを宣言しておきます。
単にオブジェクトとして宣言するなら、As Object宣言でいいのですが、IE用オブジェクトと決まっているので、InternetExplorerとして宣言しました。
その上でobjIEに新規IEアプリとしてのオブジェクトとして作成しています。
オブジェクト宣言とset利用
オブジェクト宣言後にオブジェクトを新規作成しました。
この手順についてはこちらを参考にさせて頂きました、ありがとうございます。
オブジェクト変数に値を代入するにはSetによる宣言が必須となっているようです。
また、Setを使用することでその後のコーディング上でSetによって宣言した変数に対して"objIE.navigate "など続きのコードを書くことで、処理が実施できます。
変数宣言は必ず実施する
VBAではオブジェクト宣言せずとも変数を扱うことはできます。この場合はVariant型になり、データの種類に関係なく文字やオブジェクトが格納できます。
しかし、何でも扱えるが故にエラーも発生しやすくなるため、明示的に宣言しておくことで未然に防げるようにしました。
IEを表示させる
objIE.Visible = True
IEオブジェクトの表示設定をしています。
詳しくはこちらを参考にさせて頂きました、ありがとうございます。
この制御によってIEを表示して操作を実施したり、IE表示しないままデータだけ取得と言ったことが可能となります。
今回はまずスクレイピングが実施できることを確認するためにも、対象サイトに移動できていることを確認するために表示させるようにしました。
objIE.navigate "https://protected-fortress-61913.herokuapp.com/book"
navigateによって指定したサイトを表示させています。
今回はPHPで作成していた書籍情報サイトにアクセスするようにしました。
初期アクセス時にはログインしていないので、ログインが必要となります。
今回は動作確認のみですので、ログインは手動で実施して、その後のスクレイピング動作確認のみとしています。
IE表示待機処理を行う
Call WaitResponse(objIE)
スクレイピング用プロシージャ下部に記述しているWaitResponseという名前のプロシージャを呼び出しています。
# WaitResponse
Sub WaitResponse(objIE As Object)
Do While objIE.Busy = True Or objIE.readyState < READYSTATE_COMPLETE
DoEvents
Loop
End Sub
呼び出しされているプロシージャです。
スクレイピング用コードサンプルそのままの記述ですが、内容としてはIEの読み込み表示完了を待つ処理を実施しています。
HTMLの表示を完了させてから、HTML内のデータを取得する必要があるため、IEに指定したURLサイトの表示が完了するのを待っています。
この処理がないと、HTMLが表示される前にスクレイピングが開始となり、結果として何もないページからデータを取得することになり失敗します。
詳しくCALLプロシージャをみる
- Do While 〜 Loop
Do 〜 Loop間のコードをループするコードです。While 〜 の結果がFalseとなるとループから抜けます。
While内にIE読み込みに対する結果を参照させ、IE読み込み完了→Falseを返してループを抜けることで、読み込み完了を確認して次へ進む形としています。
callで呼び出すプロシージャとしておくことで、他のIE読み込み待ちにも使用できます。
- DoEvents
こちらは、発生したイベントがOSによって処理されるように制御を戻す…といった説明がよくありました。
これではよく分からなかったのですが、私的に要約すると、VBA処理中にユーザーから何らかの処理が加わるとそちらを先に実行する…。と解釈しました。
この処理がない場合にDo〜Loop処理を開始すると、無限ループに陥った際にその他の操作が全く受け付けできない状態となります。VBA動作時にPCが固まったような現象ですね。
これを未然に防止するためにDoEventsをループ中に設定しています。
- objIE.Busy = True
objIEはIEのオブジェクトです。直前の処理はobjIE.navigateで、IEを開き対象のURLへアクセスしています。
アクセス処理中はビジー状態となり、objIE.Busyに対してTrueを返します。IE表示完了した段階でFalseを返します。
FalseになればWhile文=Falseとなるのでループが終了します。これによって、IEの読み込みが完了するまで待機し続け、仮に他の処理を実施したい…となれば、DoEventsを設定しているので、処理可能となります。
調べるにあたっては、こちらを参考にさせて頂きましたありがとうございます。
- objIE.readyState < READYSTATE_COMPLETE
ところで、While文には条件がもう一つあります。
こちらの処理もIE読み込み完了を確認するための処理です。
readyState指定によってobjIEの読み込み状態を数値で返します。値は0〜4の5種類で、読み込み完了状態=4となります。
READYSTATE_COMPLETEは定数の4です。記述の意味としては"objIE.readyState < 4"とすれば全く同じですが、そのまま"4"ではマジックナンバーとなり分かりにくいという点もあるので、定数であるREADYSTATE_COMPLETEと書いておく方が読み手にも分かりやすいでしょう。
busyとreadystateの双方を補完する形とするために両方の処理を書いておきorとすることで、完了時点で本loopから抜ける記述となっています。
調べるにあたっては、こちらを参考にさせて頂きましたありがとうございます。
HTMLドキュメントを取得する
IE起動しHTML表示が完了すれば、HTMLデータを取得していきます。
Dim htmlDoc As HTMLDocument
Set htmlDoc = objIE.document
HTMLドキュメントとしてhtmlDoc変数を宣言します。
そして、宣言したhtmlDocへSetを利用してobjIE内のドキュメントを格納しました。
取得データを表示させる
MsgBox htmlDoc.getElementsByClassName("list-book-title")(0).innerHTML
MsgBoxにより、続く内容の結果をダイアログボックスで表示させます。
getElementsByClassName()によって、クラス名指定によるデータを取得します。今回であれば、"list-book-title"タグに対してタイトル名を記載していたので、タイトル名を取得しています。(0)とすることで、取得オブジェクトの0番目(一番最初)のタイトルのみを取得しました。
innerHTMLによって取得クラスに対してのHTMLの中身を取得しています。
これによって"list-book-title"クラス内のHTML内のテキストのみ…を表示、つまりタイトル名の一つ目だけを表示させることができました。
以上でスクレイピングが実施出来ていることを確認しました。
次は、1件のデータのみではなく初期ページに対しての全データをとれるようにしていきます。