11
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VBAによるWebコンテンツの取得

Last updated at Posted at 2019-08-20

はじめに

同僚から、「ExcelからWebサイトをスクレイピングするVBAスクリプトがうまく動かなくなったので直して欲しい」という依頼を受けた際に調べたことをまとめました。

VBAでWebのコンテンツを取得するには、以下の4つの方法が代表的なようです。

  • InternetExplorer(以下、「IE」)をVBAから操作する。
  • HTMLDocumentのcreateDocumentFromUrlメソッドを使う。
  • XMLHTTP60を使い文字列としてコンテンツを取得しHTMLとしてパースする。
  • Selenium Webdriver を使う。

現時点での僕の方針を先に書くと、

  1. 自由にソフトウェアがインストールできる環境なら、VBA以外の言語を検討
  2. 静的ページが対象で、ページ内のリンクを辿る必要があるならHTMLDocument
  3. 静的ページが対象で、ページ内のリンクを辿る必要が無いならXMLHTTP
  4. スクレイピング対象コンテンツがJavascriptで読み込まれるならIE

と、なります。

以下では、それぞれの方法のメリット・デメリットをまとめました。
Seleniumを使う方法以外については、使用例のコードを書きました。

それぞれの手法のメリット・デメリット

Internet ExplorerをVBAから操作する

ブラウザをプログラムから操作するので、およそ人間の操作であれば、ほぼそれをトレースして、プログラムに行わせることができます。
対象であるWebサイトがJavascriptでコンテンツを読み込んでいる場合には、この方法がもっとも有力に思えます。
一方プログラムを職場なんかの縛られた環境で利用する場合は、その環境でいつまでIEが利用できるのか注意を払っておく必要があります。

メリット

  • Javascriptで読み込まれるコンテンツへの操作にも対応できる。
  • プログラムがブラウザの操作と結びついているので、非エンジニアにも何が起こっているのか分かりやすい。

デメリット

  • 取得したいコンテンツとは関係ない画像なども読み込むため、場合によっては遅い。
  • 「IEを使い続けることは技術的負債」 のように言われている中で、新規にIEによるスクレイピングのコードを書くことは不安(職場の端末でIEがサポートされなくなる将来に、一度に改修を行う必要がでるかもしれない)。

HTMLDocumentのcreateDocumentFromUrlメソッドを使う

1つのライブラリにしか依存しておらず、コードもシンプルに書けます。
静的なウェブサイトのリンクを巡回してコンテンツをダウンロードするような用途であれば、この方法がオススメです。

メリット

  • IEに依存しない。
  • 余計なコンテンツを取得しないので多分速い。

デメリット

  • Javascriptで読み込まれるコンテンツには対応できない。

XMLHTTP60で文字列としてコンテンツを取得しHTMLとしてパースする

HTTP GET メソッドを送って、それに対するレスポンス文字列をHTMLとしてパースする方法です。
欲しいコンテンツが相対URLで書かれていない場合や、HTMLとしてパースする必要のない、WebAPIを叩いてXMLやJSONを取得する場合には、この方法がオススメです。

メリット

  • 多分最も高速。
  • 一度文字列を介するので、オブジェクトの管理が楽。

デメリット

  • パースされたHTMLDocumentは元のURLの情報を持たないので、相対URLで書かれたリンクを扱うのに苦労する。
  • Javascriptで読み込まれるコンテンツには対応できない。

Selenium Webdriver を使う(未検証)

VBAからSelenium Webdriverを扱うライブラリである SeleniumBasic に関する、Web上のいくつかの解説を読んだだけで、実際にはコードを書いていません。

僕の印象では、VBAでスクレイピングをせにゃならん状況と言うのは、やむなくVBAを使ってることが多いです。
Selenium Basicを入れられるくらい自由に環境をいじれるなら、XlwingsでPythonとExcelを連携するとか、メインの処理はもっと書きやすい言語でやって最後にCSVを吐いてExcelから読ませるとかした方が良いようにも思います。

メリット

  • Javascriptで読み込まれるコンテンツにIE以外のブラウザで対応できる
  • 別の言語でSelenium Webdriverに慣れてる人には書きやすい

デメリット

  • 環境を整えるのが大変
  • 複数のツールに依存するので問題の切り分けやデバッグが大変そう

それぞれの方法でHTMLDocument を取得する関数

引数としてURLを与えて、HTMLDocumentオブジェクト(への参照)を返す関数として実装してみました。
返されるHTMLDocumentオブジェクトの生存期間が、他のオブジェクトの影響を受ける場合は、そのオブジェクトをグローバル変数とはせず、関数の引数として一緒に渡すことにしました。
こうしておく方が、変数名の変更なんかが自由にできると思います。

以下全ての例で、HTMLDocumentを使うため、VBEのメニューから
[ツール] → [参照設定]→ "Microsoft HTML Object Library" にチェック
をしておく必要があります。

IE

  • [ツール] → [参照設定]→ "Microsoft Internet Controls" にチェックしておく
  • 呼び出し元のIEオブジェクト(以下では objIE)が閉じられると、この関数が返したHTMLDocumentも参照先が無くなって以降の操作ができなくなるので、IEの生存期間には注意すること
Private Function getHTMLDoc_withIE(ByVal url As String, ByRef objIE As InternetExplorer) As HTMLDocument
  ' IE で対象ページを開く
  Call objIE.Navigate(url)

  ' 完全にページが表示されるまで待機する
  Do While objIE.Busy = True Or objIE.readyState <> READYSTATE_COMPLETE
    DoEvents
  Loop
  Do objIE.Document.readyState <> "complete"
    DoEvents
  Loop

  ' 関数の返り値に設定
  Set getHTMLDoc_withIE = objIE.Document
End Function

HTMLDocumentのcreateDocumentFromUrlメソッド

  • 呼び出し元となってるHTMLDocumentオブジェクト(以下では htmldoc)が失われると、この関数が返したHTMLDocumentも参照先が無くなって以降の操作ができなくなるので、オブジェクトの生存期間には注意すること
Private Function getHTMLDoc_withHTMLDoc(ByVal url As String, ByRef htmldoc As HTMLDocument) As HTMLDocument
  ' HTMLDoc オブジェクトで対象ページを開き関数の返り値に設定
  Set getHTMLDoc_withHTMLDoc = htmldoc.createDocumentFromUrl(url, vbNullString)

  ' 完全にページが表示されるまで待機する
  Do getHTMLDoc_withHTMLDoc.readyState <> "complete"
    DoEvents
  Loop
End Function

XMLHTTP60

  • [ツール] → [参照設定]→ "Microsoft XML v6.0" にチェックしておく
Private Function getHTMLDoc(ByVal url As String) As IHTMLDocument
  Dim http As XMLHTTP60
  Set http = New XMLHTTP60

  ' GETメソッドで対象ページを取得
  Call http.Open("GET", url, False)
  Call http.Send()

  ' 待つ
  Do While http.readyState <> READYSTATE_COMPLETE
    DoEvents
  Loop

  ' 取得した文字列をHTMLとしてパースし関数の返り値に設定
  Set getHTMLDoc = New HTMLDocument
  Call getHTMLDoc.Write(http.responseText)
End Function

参考にしたWebサイト

VBAでWebページを取得する方法 @ so-zou.jp
InternetExplorerオブジェクトのdocument.readyStateプロパティ @ VBAのIE制御入門
HTMLDocument Class @ Microsoft .NET
VBAのスクレイピングを簡単楽にしてくれるSelenium @ エクセルの神髄
[Excel VBAコーディング ガイドライン案] (https://qiita.com/mima_ita/items/8b0eec3b5a81f168822d) @ Qiita

11
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?