ExcelマクロVBA技術解説
VBAのスクレイピングを簡単楽にしてくれるSelenium

ExcelマクロVBAの問題点と解決策、エクセルVBAの技術的解説
最終更新日:2019-06-16

VBAのスクレイピングを簡単楽にしてくれるSelenium


VBAでWebスクレイピングする方法としてIE自動操作がありますが、VBA記述が結構面倒になります、
もっと簡単にスマートにVBAを書きたいと思ったら…SeleniumBasicを使ってみましょう。


SeleniumBasicは、エクセルVBAでのWeb閲覧を自動化することを強力かつ簡単に実現してくれます。
今回は、ブラウザはChromeを使っての自動操作を解説します。
Google Chromeは別途インストールしておいてください。

IE操作については以下を参照
VBAでのInternetExplorer自動操作
VBAでInternetExplorerを操作したくなることがあります。Webのデータを取得したり、リンクをクリックしたりと、これらを自動で行う事が出来ます。使うのは、InternetExplorer というオブジェクトです。

WEBページのいろいろな取得方法については以下を参照

WEBデータの取得方法
WEBページのデータを取得して、エクセルのデータとして取り込みたいとの要望が多いようです。マクロVBAでWEBページのデータを取得する方法はいろいろあります。QueryTables InternetExplorer MSHTML MSXML2 順番に、以下で説明します。


以下では、VBAについては相応の知識があることを前提に、Seleniumの使い方のみ解説しています。
少なくとも、メソッドという単語の理解が不安な場合は、そちらを確認してからお読みください。



SeleniumBasicのインストール

以下よりダウンロードします。
http://florentbr.github.io/SeleniumBasic/
中断のRelease pageへ、
https://github.com/florentbr/SeleniumBasic/releases/tag/v2.0.9.0
執筆時点では、
SeleniumBasic-2.0.9.0.exe
これをダウンロードしてインストールします。

VBA Selenium Basic

画面指示に従って、そのままインストールします。

Selenium VBA
Selenium Basicは、以前はSelenium VBAと呼ばれていました。

https://code.google.com/archive/p/selenium-vba/
ダウンロードは、
https://code.google.com/archive/p/selenium-vba/downloads

既に開発は停止していて、後継として先のSelenium Basicに移行されています。

chromedriver.exe
以下より、お使いのChromeバージョンに合わせて、「chromedriver.exe」を入れ替えてください。

https://sites.google.com/a/chromium.org/chromedriver/downloads

http://chromedriver.chromium.org/downloads

「chromedriver.exe」を入れ替える場所(Windows10で普通にインストールした場合)は、
C:\Users\ユーザー\AppData\Local\SeleniumBasic
exeを差し替えてください。
Chromeバージョンが変更された場合は、そのバージョンに合わせて入れ替えしてください。
執筆時点では、

VBA Selenium Basic

https://chromedriver.storage.googleapis.com/index.html?path=74.0.3729.6/

こちらを使用しました。
chromedriverのバージョンが違うと動作しません。

VBEでの参照設定


VBA Selenium Basic

参照できているか確認

VBA Selenium Basic

Selenium.には、以下もあります。
ChromeDriver
EdgeDriver
FirefoxDriver
Startメソッドでブラウザを指定するので、とりあえずWebDriverにしておけば良いでしょう。

WEBサイトを表示してみましょう

何はともあれ、お決まりのヤホーを表示してみましょう。

Sub sample()
  Dim Driver As New Selenium.WebDriver
  Driver.Start "chrome"
  Driver.Get "https://www.yahoo.co.jp/"
  Stop
End Sub

プロシージャーが終了するとブラウザが閉じてしまうのでStopしています。

ブラウザ起動・終了の定型文としては以下のようにしておけば良いでしょう。



Sub sample()
  Dim Driver As New Selenium.WebDriver
  Driver.Start "chrome", "https://www.yahoo.co.jp/"
  Driver.Get "/"
  '・・・
  Driver.Close
  Set driver = Nothing
End Sub

Seleniumの基本的な使い方(株価情報を取得してみる)

Yahooを表示したので、
「経済」タブの「日経平均株価」を取得してみましょう。

取得するには、そもそもページ全体の中から、取得したいデータの場所を特定しなければなりません。
WEBページはHTMLで作成されています。
どの要素(element)を取得するかをSeleniumに指定しなければなりません。
WEBページのソースをみてHTMLタグ等からだけでは必要な情報(element)がどれかを探すのは大変だったり、たどり着けなかったりします。

Chromeのデベロッパーツールを使用します。

VBA Selenium Basic
「Google Chrome の設定」→「その他のツール」→「デベロッパー ツール」
ショートカットは、F12

VBA Selenium Basic

「デベロッパー ツール」の表示位置は、以下のアイコンで変更できます。

VBA Selenium Basic

下または右にするのが普通でしょうか。
では、まずはこれを使って、Yahooの「経済タブ」の要素を特定します。

VBA Selenium Basic

このアイコンをクリックすると、要素(element)の選択モードになります。
element選択モードを解除するには、もう一度このアイコンをクリックしてください。
WEBページ内をクリックしても選択モードは解除されます。
element選択モードで、マウスカーソルをWEBページ内で移動すると、

VBA Selenium Basic

このように、カーソル下が色付けされて表示されるのが分かるようになっています。
適当にカーソルを動かしてみて、色付けされている範囲が変更されることを確認してください。
この色付けされている範囲が、ページを構成している要素(element)になります。
このエレメントは入れ子になっていて、
エレメントの中にエレメントがあり、そのエレメントの中にもさらにエレメントがはいっている作りになっています。

上図のように、「経済」が選択された状態でクリックすると、
「デベロッパー ツール」内の該当箇所が選択されます。

VBA Selenium Basic

今度は、「デベロッパー ツール」内でマウスを移動させてみてください。
「デベロッパー ツール」のマウスカーソル位置に該当するWEBページの当該箇所が色付けされます。

element選択モードで、WEBページ内から目的の箇所を探したり、
デベロッパー ツール内の選択から目的の箇所を探したりと、双方向で探すことができます。

目的のエレメントに到達したら、デベロッパー ツールのelementを右クリックします。

VBA Selenium Basic

そして、「Copy」から「Copy selecter」をクリックしてください。
クリップボードに、CSSセレクターが入ります。
「経済」タブは、
#economy
とコピーされたはずです。
これを使って、seleniumで「経済」タブをクリックしてみましょう。

前出のコードに追記します。

VBA Selenium Basic

エレメントを見つけるので、Find○○というメソッドになります。
.FindElementByClass
.FindElementByCss
.FindElementById
.FindElementByLinkText
.FindElementByName
.FindElementByPartialLinkText
.FindElementByTag
.FindElementByXPath
いろいろなメソッドが用意されていることを確認しておきましょう。
どれを使うかは、その時々で変わってきますが、
今回は、CSSセレクターなので、
FindElementByCss
これになります。
この引数に先ほどCopyしたCSSを入れます。
そして、それをクリックなので、Clickメソッドを使用します。

VBA Selenium Basic

Sub sample()
  Dim Driver As New Selenium.WebDriver
  Driver.Start "chrome", "https://www.yahoo.co.jp/"
  Driver.Get "/"
  Driver.FindElementByCss("#economy").Click
  Stop '確認するため
  Driver.Close
  Set Driver = Nothing
End Sub

これで「経済」タブが表示されたので、

VBA Selenium Basic

クリック→elementを右クリック→Copy selector
#economyfb > div.topicscatch > div > dl > dd:nth-child(2)
これがコピーされますので、これを使って、

Sub sample()
  Dim Driver As New Selenium.WebDriver
  Driver.Start "chrome", "https://www.yahoo.co.jp/"
  Driver.Get "/"
  Driver.FindElementByCss("#economy").Click
  Dim txt As String
  txt = Driver.FindElementByCss("#economyfb > div.topicscatch > div > dl > dd:nth-child(2)").Text
  Debug.Print txt
  Driver.Close
  Set Driver = Nothing
End Sub

もうお分かりいただけると思います。
文字列を取得するのですから、Textメソッドを使用します。
結果は、イミディエイトに、
22,258.73
このように表示され、ブラウザも閉じられます。
(当たり前すぎますが、株価は実行日によって変わりますよ。)

色々なパターンでのseleniumの使い方

画面最大化
Driver.Window.Maximize

ブラウザで見えていないエレメントにはアクセスできないページもあるので、
最大化しておいた方がとりあえずは扱いやすいと思います。

Wait処理
Driver.Wait ミリ秒

Seleniumは、画面が表示されるまで自動で待ってから戻ってくるので、ページロード待ちはあまり気にする必要はありません。
なのですが、それだけでは対応できないページもあります、といいますか、結構多いです。
そこで、ページによっては明示的にwaitを入れる必要が出てきます。

必要なエレメントが見つかるまでループさせる方法にしたほうが良いのですが、
単純に、1~3秒程度待たせてしまったほうが、VBA自体は簡単になります。


テキストボックスに文字を入れる
Sub sampla1()
  Dim Driver As New Selenium.WebDriver
  Dim sKey As New Selenium.Keys
  Dim elm As Selenium.WebElement
  Driver.Start "chrome", "https://www.google.co.jp/"
  Driver.Get "/"
  Set elm = Driver.FindElementByXPath("//*[@id=""tsf""]/div[2]/div/div[1]/div/div[1]/input")
  elm.Clear
  elm.SendKeys "エクセルの神髄"
  elm.SendKeys sKey.Enter
  Stop '確認
  Driver.Close
  Set Driver = Nothing
End Sub

ここではVBAサンプルとして、Googleで「エクセルの神髄」を検索しています。
エレメント検索のバリエーションとしてXPathをにしています。
そして、サンプルとしてエレメントをオブジェクト変数にセットするようにしています。
文字列の送信は、SendKeysになりますが、
既に文字が入っている場合はその後ろに入ってしまうので一旦Clearするようにしています。


ドロップダウン(プルダウン)メニューの選択
AsSelectメソッドを使います。

Driver.FindElementByCss(css文字列).AsSelect.SelectByText "値"



Driver.FindElementByCss(css文字列).AsSelect.SelectByIndex 2

上記のほか、SelectByValueやSelectByOptionもあります。

VBA Selenium Basic


フレームを切り替える
iframeタグが使われているページの場合、各iframe内の要素にはそのままではアクセスできません。

iframeは「inline frame」の略で、インラインのフレームを作成するためのタグです。
HTMLは以下のようなものになります。
<iframe id="identiFier" src="https://・・・"

iframe内の要素にアクセスするには、その前に当該のiframeに切替てから行います。

Driver.SwitchToFrame "identiFier"

iframe内に切り替えた後は、普通にFindElement○で要素を取得できます。

WEBサイトの操作は、マウスクリックとキーボード入力で全て操作できます。
従って基本としては、ClickとSendKeysで操作することになりますが、seleniumで操作するときにいくつか専用の使い方が必要なものがあります。

色々組み合わせて目的の画面にたどり着きます

Yahooで以下の操作を行います。
「経済」→「日経平均株価」→「銘柄コード」に4689→「検索」→「アラート設定」
→「ID」入力→「次へ」→「パスワード」入力→「次へ」→「値上がり率」に15%

Option Explicit

Sub sample2()
  Dim Driver As New Selenium.WebDriver
  Dim elm As Selenium.WebElement
  Driver.Start "chrome", "https://www.yahoo.co.jp/"
  Driver.Get "/"
  Driver.Window.Maximize
  '「経済」
  Driver.FindElementByCss("#economy").Click
  '「日経平均株価」
  Driver.FindElementByCss("#economyfb > div.topicscatch > div > dl > dt:nth-child(1) > a").Click
  '「銘柄コード」に4689
  Driver.FindElementByCss("#searchText").SendKeys "4689"
  '「検索」
  Driver.FindElementByCss("#searchButton").Click
  '「アラート設定」
  Driver.FindElementByCss("#settings > span.alerts > a").Click
  '「ID」入力
  Driver.FindElementByCss("#username").SendKeys "ID"
  '「次へ」
  Driver.FindElementByCss("#btnNext").Click
  '「パスワード」入力:そのまま続けるとエラーとなるのでWait対応
  Set elm = WaitForElement(Driver, "#passwd")
  If elm Is Nothing Then
    MsgBox "「#passwd」見つかりません。"
    End
  End If
  If Not WaitForSendKeys(Driver, elm, "パスワード") Then
    MsgBox "パスワードを入力できませんでした。"
    End
  End If
  '「次へ」
  Driver.FindElementByCss("#btnSubmit").Click
  '「値上がり率」に15%
  Driver.FindElementByCss("#up_0").AsSelect.SelectByText "15%"
  Stop '確認
  Driver.Close
  Set Driver = Nothing
End Sub

Function WaitForElement(ByVal Driver As WebDriver, ByVal sCss As String) As Selenium.WebElement
  Dim cnt As Long
  On Error Resume Next
  Do
    cnt = cnt + 1
    Set WaitForElement = Driver.FindElementByCss(sCss)
    If Not Err Then
      Exit Function
    End If
    Driver.Wait 100
    If cnt > 100 Then Exit Do
  Loop
  Set WaitForElement = Nothing
End Function

Function WaitForSendKeys(ByVal Driver As WebDriver, _
             ByVal elm As WebElement, _
             ByVal sText As String) As Boolean
  Dim cnt As Long
  On Error Resume Next
  Do
    cnt = cnt + 1
    Err.Clear
    elm.Clear
    If Err.Number = 0 Then
      Err.Clear
      elm.SendKeys sText
      If Err.Number = 0 Then
        WaitForSendKeys = True
        Exit Function
      End If
    End If
    Driver.Wait 100
    If cnt > 100 Then Exit Do
  Loop
  WaitForSendKeys = False
End Function

Seleniumでブラウザを起動すると、
クッキー等は全て無効になっているので画面によっては、通常操作では必要ないログインを求められます。

Yahooのこのページでは、パスワード入力がそのままではエラーとなってしまいます。
1秒程度のWaitで問題なく進めますが、
上記ではサンプルの意味も兼ねて、エレメントへの設定が正しく行われるまで繰り返すようにしています。

最後のドロップダウンのoptionを確認する場合、
デベロッパーツールで、ドロップダウンをクリック後に、

VBA Selenium Basic

"down_0>…</select>この…をダブルクリッククリックすると、optionの中が展開されます。

VBA Selenium Basic

elementをコレクションで取得する

tdタグやliタグのように、同一タグの繰り返しを順次処理する場合は、
指定タグのエレメントをコレクションで取得し、For~Eachで処理します。

以下では、Yahooのトピック一覧を取得しています。

Sub sample4()
  Dim Driver As New Selenium.WebDriver
  Dim elm1 As Selenium.WebElement
  Dim elm2 As Selenium.WebElement
  Driver.Start "chrome", "https://www.yahoo.co.jp/"
  Driver.Get "/"
  Set elm1 = Driver.FindElementByCss("#topicsfb > div.topicsindex > ul.emphasis")
  For Each elm2 In elm1.FindElementsByTag("li")
    Debug.Print elm2.Text
  Next
  Driver.Close
  Set Driver = Nothing
End Sub

Find○○メソッドには、オブジェクト単体の取得とコレクションの取得が容易されています。
FindElementとFindElementsが存在します。
コレクションとして取得する場合は、FindElementsを使用します。

.FindElementsByClass
.FindElementsByCss
.FindElementsById
.FindElementsByLinkText
.FindElementsByName
.FindElementsByPartialLinkText
.FindElementsByTag
.FindElementsByXPath

新規ページが開かれる場合

クリックによって新規ページが開かれるような場合、新規ページへ移動する必要があります。
まずは特に何もしない場合の動作状況になります。

Sub sample3()
  Dim Driver As New Selenium.WebDriver
  Driver.Start "chrome"
  Driver.Get "https://excel-ubara.com/EXCEL/EXCEL918.html"
  Driver.FindElementByLinkText("Excelシートの複雑な計算式を解析するVBAの関数構文").Click
  Debug.Print Driver.FindElementByTag("h1").Text
  Stop '確認
  Driver.Close
  Set Driver = Nothing
End Sub

これでイミディエイトに出力されるのは、
「VBAリファレンス
Excelワークシート関数一覧(2010以降)」
このように、クリックする前の元のページのh1が取得されてしまいます。
そこで、次ページへ移動する必要があります。

Sub sample3()
  Dim Driver As New Selenium.WebDriver
  Driver.Start "chrome"
  Driver.Get "https://excel-ubara.com/EXCEL/EXCEL918.html"
  Driver.FindElementByLinkText("Excelシートの複雑な計算式を解析するVBAの関数構文").Click
  Driver.SwitchToNextWindow
  Debug.Print Driver.FindElementByTag("h1").Text
  Driver.Close
  Set Driver = Nothing
End Sub

これでイミディエイトに、以下のように出力されます。
「ExcelマクロVBAサンプル集
Excelシートの複雑な計算式を解析するVBAの関数構文」

複数開いている場合は、タイトルを保存しておいて、
Driver.SwitchToWindowByTitle "タイトル"
これで移動可能です。

以下のようなVBAコードを追加して、画面遷移を確認してみると良いでしょう。
Debug.Print "1:" & Driver.window.Title
Driver.SwitchToNextWindow
Debug.Print "2:" & Driver.window.Title
Debug.Print Driver.FindElementByTag("h1").Text

上手くいかない特殊な場合の対処方法

実際のWEBページは多種多様で、ここまでの対処できない場合が多々あります。
そのような場合は、個別の対処方法を考える必要があります。
以下に代表的な対処方法の例を載せておきます。

※以下では、Driverはモジュールレベルで宣言されているものとしています。

テキストボックスに1回で文字列を入れられない場合

Function SendKeysTextBox(ByVal argText As String, _
             ByVal sCss As String, _
             Optional ByVal waitTime As Long = 200, _
             Optional ByVal maxTime As Long = 2000) As Boolean
  On Error Resume Next
  SendKeysTextBox = False
  
  Dim t As Double
  t = Timer
  Do
    Driver.FindElementByCss(sCss).Clear
    Driver.FindElementByCss(sCss).SendKeys argText
    If Driver.FindElementByCss(sCss).Value = argText Then Exit Do
    If Timer > t + maxTime Then Exit Function
    Driver.Wait waitTime
  Loop
  
  Driver.Wait waitTime
  SendKeysTextBox = True
  On Error GoTo 0
End Function


クリック後に新ページ移動前にseleniumから戻ってきてしまう場合

Function ClickToNewPage(ByVal sCss As String, _
            Optional ByVal waitTime As Long = 200, _
            Optional ByVal maxTime As Long = 2000) As Boolean
  On Error Resume Next
  ClickToNewPage = False
  
  Dim sUrl As String
  Dim t As Double
  t = Timer
  
  sUrl = Driver.Url
  Do
    Driver.FindElementByCss(sCss).Click
    If sUrl <> Driver.Url Then Exit Do
    If Timer > t + maxTime Then Exit Function
    Driver.Wait waitTime
  Loop
  
  Driver.Wait waitTime
  ClickToNewPage = True
  On Error GoTo 0
End Function


画面ロード完了前にseleniumから戻ってきてしまう場合

Function ExistElement(ByVal sCss As String, _
           Optional ByVal waitTime As Long = 200, _
           Optional ByVal maxTime As Long = 2000) As Boolean
  On Error GoTo 0
  ExistElement = False
  
  Dim t As Double
  t = Timer
  
  Dim elms As Selenium.WebElements
  Do
    Set elms = Driver.FindElementsByCss(sCss)
    If elms.Count > 0 Then Exit Do
    If Timer > t + maxTime Then Exit Function
    Driver.Wait waitTime
  Loop
  
  Driver.Wait waitTime
  ExistElement = True
  On Error GoTo 0
End Function


遷移後のURLに直接移動する

Driver.Get Driver.Url & "?xxx&yyy"

最後の手段になりますが、遷移後のURLに移動してしまう方法も結構有効です。
クリック後にURLパラメーターが追加になるような場合は、直接パラメーターを追加したURLを指定してしまう方法になります。

元々のURLの最後が"/"で終わっているか、既に"?"が入っているか、
これらの状態によって、追加するパラメーターは適宜変更が必要になります。

URLパラメーター
URLに付け加える変数です。
クエスチョンマーク"?"をURLの末尾に付け、「変数(パラメータ)=値」の形式で指定します。
複数のパラメーターはアンパサンド(&)でつなげます。

最後に

今まで筆者はSeleniumVBAを使っていたのですが、SeleniumBasicではメソッドが結構変更になっていました。
実際に動作確認しながら本記事を書きましたが、細部においてはより効率的な使い方があるかもしれません。
ただし全体的には、SeleniumVBAより使いやすくなっているように感じました。

クローリング&スクレイピングは、サイトごとのクセを見極めることが面倒な作業になります。
特にJavaScriptでマウスオーバーにより切り替わるような場合はかなり面倒です。
SeleniumでJavaScriptのコードを実行(.ExecuteScript)することもできますが、
このようなサイトでは、結構頻繁にサイトが更新されてしまう為になかなか安定運用できない場合が多いようにも思われます。
いずれにしても、クローリング&スクレイピングではサイト変更によってVBAを変更しなければならないものです。
従って、いつでも簡単にVBA修正して運用できる環境であることが望まれます。
もし、そのような運用が難しい場合(開発会社に依頼して作ってもらう等の場合)は、
サイト変更時の対応を事前に良く確認しておくことをお勧めします。



同じテーマ「マクロVBA技術解説」の記事

VBAでのInternetExplorer自動操作
VBAでのSQLの基礎(SQL:Structured Query Language)
VBAで正規表現を利用する(RegExp)
VBAでメール送信する(CDO:Microsoft Collaboration Data Objects)
VBAでのOutlook自動操作
ADO(ActiveX Data Objects)の使い方の要点
特殊フォルダの取得(WScript.Shell,SpecialFolders)
参照設定、CreateObject、オブジェクト式の一覧
VBAのスクレイピングを簡単楽にしてくれるSelenium
VBA+SeleniumBasicで検索順位チェッカー作成
VBA+SeleniumBasicで検索順位チェッカー(改)


新着記事NEW ・・・新着記事一覧を見る

VBAにおける変数のメモリアドレスについて|VBA技術解説(11月8日)
空文字列の扱い方と処理速度について(""とvbNullString)|VBA技術解説(1月7日)
Errオブジェクトとユーザー定義エラー|VBA入門(11月5日)
シングルクォートの削除とコピー(PrefixCharacter)|VBA技術解説(11月4日)
ユーザー定義型の制限とクラスとの使い分け|VBA技術解説(11月3日)
クリップボードに2次元配列を作成してシートに貼り付ける|VBA技術解説(11月1日)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)|VBA技術解説(10月31日)
VBAクラスのAttributeについて(既定メンバーとFor Each)|VBA技術解説(10月19日)
VBAの用語について:ステートメントとは|VBA技術解説(10月16日)
VBAのマルチステートメント(複数のステートメントを同じ行に)|VBA技術解説(10月14日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|ExcelマクロVBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.変数宣言のDimとデータ型|ExcelマクロVBA入門
6.繰り返し処理(For Next)|ExcelマクロVBA入門
7.マクロって何?VBAって何?|ExcelマクロVBA入門
8.ひらがな⇔カタカナの変換|エクセル基本操作
9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説
10.セルに文字を入れるとは(Range,Value)|VBA入門



  • >
  • >
  • >
  • VBAのスクレイピングを簡単楽にしてくれるSelenium

  • このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


    記述には細心の注意をしたつもりですが、
    間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
    なお、掲載のVBAコードは自己責任で使ってください。万一データ破損等の損害が発生しても責任は負いません。




    このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
    本文下部へ