作りながら学ぶVBAプログラミング

VBA スクレイピングでデータの自動収集。

サイトのデータを収集してきてデータ解析を行う場面は山ほど発生します。
サイトに掲載されているデータを取り出すことをスクレイピングといいます。
よく使われるのが気象庁の気温データとか、降水量とか、過去の天気情報とかですね。

以前は、VBAのブラウザ機能でHTMLのソースを受信し、HTML構文を解析していたものですが、最近はそれをやってくれるモジュールがあります。
SeleniumBasic というモジュールがVBAでよく使われているモジュールです。
SeleniumBasicの導入と使い方について解説します。

SeleniumBasicの導入

Selenium の基本モジュールをダウンロードしてきます。
Seleniumbasic


※最新版のダウンロードになりますので 2.0.9.0 とは限りません。

exe を実行することで SeleniumBasic がインストールされます。

VBA で SeleniumBasic を使用する前準備

SeleniumBasic をインストールしたマシンでは、VBAの「ツール」⇒「参照設定」で selenium Type Library を選択できるようになります。
selenium Type Libraryを選択してOKを入れます。



Selenium は、クロームのバージョンと一致していないと使えない

Selenium は、クロームのバージョンと一致していないと使えないので常に一致させるためのモジュールをインストールしておきます。

WebDriverManager-for-VBA のダウンロード
Code をクリックして DownLoad Zip で zip をダウンロードし展開(解凍)します。


WebDriverManager-for-VBA を vba にインポートして使うようにします。



スクレイピングモジュールの実際


selenium が入ったVBAでサイトのページを読み込む

以下が基本形になります。
Dim Driver As New Selenium.WebDriver
‘ブラウザを開かない
Driver.AddArgument “headless”
‘Chrome を呼び出す
SafeOpen Driver, Chrome

‘ターゲットページを呼び出す
Dim elmLoop As WebElement
Dim iUrl

iUrl = “https://~~”
Driver.Get iUrl

‘ターゲットページの目的のタグを取り出す。
For Each elmLoop In Driver.FindElementsByXPath(“//*[@class=””yuRUbf””]/a”)
‘elmLoop.tagname
‘elmLoop.Text
If elmLoop.Text <> “” Then

End If
Next

FindElementsByXPathの求め方


とりあえず、ターゲットのページを開きます。



デベロッパーツールを開きます。



抜き出したい場所を選択します。



抜き出したい場所の XPath をCopyします。


※デベロッパーツールのソース部分で右クリックすることで Copy 等のコマンドメニューが表示されます。

Xpathの連続性を調整します。

Xpathの連続性を調整します。前例では以下のような Xpath がコピーされました。
Copy XPathを取り出します)
//*[@id=”tablefix1″]/tbody/tr[3]/td[2]

書き方としては以下のようになりますが、 ” に気を付けてください。
※VBAのダブルクォートでくくった文字列の中に ” を入れる場合には “” と記述できるので Copy してきた XPath の内容の ” 部分を “” にします。

For Each elmLoop In Driver.FindElementsByXPath(“//*[@id=“”tablefix1“”]/tbody/tr[3]/td[2]”)

tbody が気温のテーブルという意味で、その次の tr[3] は、そのテーブルの3行目という意味になっています。
さらに td[2] は、3行目の2カラム目を意味しているわけですが、連続して2月の気温を取り出すのだとしたら、
行はすべての行ということになりますので、以下のように記述します。

For Each elmLoop In Driver.FindElementsByXPath(“//*[@id=””tablefix1″”]/tbody/tr[*]/td[2]”)

tr の行番号はなんでもありってことで * です。
テーブルの1行目~2行目は取り込まなくていいのでカウンターなどをつかってデータの収集を省略すればいいでしょう。

全データということで処理すると以下のようなループになります。
For Each elmLoop In Driver.FindElementsByXPath("//*[@id=""tablefix1""]/tbody/tr[*]/td[2]")
data(count)=elmLoop.Text
count=count+1
Next


1月から12月まで取り出すとしたらループを2重化することでも可能です。
for r=3 to 102
for c=1 to 14
For Each elmLoop In Driver.FindElementsByXPath("//*[@id=""tablefix1""]/tbody/tr["+format(r,"0")+"]/td["+format(c,"0")+"]")
data(r,c)=elmLoop.Text
Next
Next c
Next r


FindElements のカウンターでデータの終わりを決める

前述の1月から12月まで取り出すループの場合、最終行が102と決め打ちにしてありますが、これをFindElementsByXPathの Count 属性で継続を判定する方法もあります。
r=3
do while Driver.FindElementsByXPath("//*[@id=""tablefix1""]/tbody/tr["+format(r,"0")+"]).count>0
for c=1 to 14
For Each elmLoop In Driver.FindElementsByXPath("//*[@id=""tablefix1""]/tbody/tr["+format(r,"0")+"]/td["+format(c,"0")+"]")
data(r,c)=elmLoop.Text
Next
Next c
r = r + 1
loop


VBA スクレイピングでデータの自動収集まとめ

データの自動収集は、モジュールによってかなり簡単に取り込めるようになりました。
Selenium は、収集だけじゃなくクリック動作なども指定できるのでサイトの自動運用も可能です。

ですが、あくまでもサイトのページのタグを基準に操作を行うため、そのサイトのページに更新がかかるとプログラムの修正は必須になります。
常時データがちゃんと取れているか確認しなければいけないことは言うまでもありません。

基本は、自サイトのデータでなければ、あくまでも他サイトのデータの取り込みが楽になると考えて利用することが適切な使い方です。
スクレイピングのプログラムの外注もそこを踏まえ、常時メンテナンスの料金がかかる前提で外注すべきです。

  • このエントリーをはてなブックマークに追加

ゆずまる・ゆぅべぇ

システムエンジニア/プログラマ歴数十年のゆずまるです。

バナーにもあるように変体的な犬マニアで生まれてからこのかた犬のいない日は経験していません。
中でもコッカースパニエルとラブラドールが死ぬほど好き!

そんなゆずまるは、20数種類の様々な言語を使用してシステム開発をおこなってきました。

そこで使ってきた各言語の自作ライブラリ中のVBAのライブラリを公開しています。

※姉妹サイトに C,PHP などのサイトもネット上に浮遊させております。そちらでもお役にたてましたら無上の喜びw

あなたの一助になれば幸いです。