SeleniumVBAを入れてみたものの、「で、実際にやりたい操作はどう書くの?」というところで止まっていませんか?
導入と基本の流れは前の記事「Excelでもブラウザ操作!SeleniumVBAの使い方」で書いたので、この記事は実際に書くコードに集中した実践編です、導入はそちらで済んでいる前提で進めます
クリックや入力、待機、要素の存在チェック、プルダウン、複数要素のループ、スクショ、JavaScript実行…と、ブラウザ自動化で「結局これ毎回書くよね」というコードをまとめました、私が普段よく使う書き方をベースにしているので、コピペして手を入れながら使ってもらえればと思います
最初に一番だいじな「要素の探し方」から見ていきます、ここを By.CssSelector に寄せておくと、あとがぐっとラクになります
要素の探し方は By.CssSelector に寄せる
SeleniumVBAで要素を探すときは FindElement(By.X, “値”) という形で書きます、この By には id・class・name・タグ名・CSSセレクタ・XPath…と何種類か指定できるんですが、初心者のうちは全部覚えようとしなくて大丈夫です
個人的なおすすめは、基本は By.CssSelector ひとつに寄せるやり方です、CSSセレクタはWeb制作でも使う書き方で、id・class・属性・子孫…とだいたいの探し方をこれ1本でカバーできます、覚えることが減るぶん、つまずきにくくなります
「探したいもの」と「CSSセレクタの書き方」を対応表にするとこんな感じです
| 探したいもの | CSSセレクタ | 例 |
|---|---|---|
| id で探す | #id名 | #search |
| class で探す | .class名 | .btn |
| name 属性で探す | [name=’値’] | [name=’q’] |
| タグ名で探す | タグ名 | div |
| 属性で探す | タグ[属性=’値’] | input[type=’text’] |
| 子孫をたどる(スペース) | .親 .子 | .list .item |
| 直下の子に限定(>) | .親 > .子 | .list > .item |
そして、これらをスペース(子孫)や「>」(直下の子)でつなげられるのがCSSセレクタの強みです、階層をたどって狙った要素をピンポイントに指定できるので、似た要素がたくさんあるページほどここが効いてきます
属性での絞り込み([id^=]など)や :not・:nth-child といったCSSセレクタ自体の書き方は、別記事「CSSセレクタの書き方 自動化で要素を狙う指定方法」に詳しくまとめたので、狙った要素がうまく取れないときはこちらもどうぞ
たとえば id が “search” の検索ボックスを探すなら、こう書けます、By.Name や By.ID を使い分けなくても、CSSセレクタの書き方を変えるだけで済むのがラクなところです
' id が search の要素を探す
Dim el As WebElement
Set el = driver.FindElement(By.CssSelector, "#search")
' name 属性が q の要素を探す(Googleの検索ボックスなど)
Set el = driver.FindElement(By.CssSelector, "[name='q']")ちなみに FindElementByCssSelector(“#search”) のように、Byを書かずに呼べるショートカットのメソッドも用意されています、どちらでも結果は同じなので、書きやすい方で大丈夫です
CSSで足りないとき … テキスト検索はループ、親だけXPath
正直に書いておくと、CSSセレクタは「要素の中の文字で探す」のと「子から親へさかのぼる」のが苦手です、SeleniumVBAには By.LinkText や By.XPath もあるので手っ取り早くはそれを使えばいいんですが、By.CssSelectorに寄せ切りたいなら、テキスト検索は独自functionで再現できます
考え方はシンプルで、CSSで候補を集めて、VBAでループしながらtext一致を探すだけです、まずはリンクを表示文字で探す関数、全aタグを集めてループするので、これで By.LinkText の代わりになります
' リンクを表示文字で探す(全aタグをループしてtext一致・LinkTextの代わり)
Public Function FindLinkByText(driver As WebDriver, ByVal keyword As String) As WebElement
Dim els As WebElements, el As WebElement
Set els = driver.FindElements(By.CssSelector, "a")
For Each el In els
If InStr(el.GetText, keyword) > 0 Then
Set FindLinkByText = el
Exit Function
End If
Next el
Set FindLinkByText = Nothing ' 見つからなければ Nothing
End Function同じ考え方を、リンク以外の要素にも広げたのがこちらです、探す範囲のCSSセレクタとキーワードを渡すと、その中からtext一致の要素を返します、「送信」という文字のボタンのような、By.XPath でやりがちなテキスト検索を、CSSとループだけで書けます
' 指定CSSの要素をループして表示文字で探す(XPathを使わずtext一致)
Public Function FindByText(driver As WebDriver, ByVal cssSel As String, _
ByVal keyword As String) As WebElement
Dim els As WebElements, el As WebElement
Set els = driver.FindElements(By.CssSelector, cssSel)
For Each el In els
If InStr(el.GetText, keyword) > 0 Then
Set FindByText = el
Exit Function
End If
Next el
Set FindByText = Nothing
End Function
' 使い方(button の中からテキスト「送信」を探す)
' FindByText(driver, "button", "送信").Clickこれで、リンク文字もテキスト一致も By.CssSelector +ループで書けるので、覚える By を増やさずに済みます
そして、CSSでもループでも唯一どうにもならないのが、子から親へさかのぼる逆引きです、CSSは子孫しか選べないので、ここだけは By.XPath の “..”(親を表す書き方)を使います
' 見つけた要素から親をたどる(CSSでは無理・XPathの ".." を使う)
Dim child As WebElement, parent As WebElement
Set child = driver.FindElement(By.CssSelector, "#price")
' その要素を起点に ".." で親要素を取る
Set parent = child.FindElement(By.XPath, "..")整理すると、子・孫への絞り込みはCSSセレクタ、テキストで探すのはCSSセレクタ+ループの独自function、子から親への逆引きだけがXPathの “..”、という棲み分けです、ほぼCSSに寄せておいて、親をたどるときだけXPathを出す、くらいの感覚でちょうどいいと思います
よく使う操作コード集
ここからは、見つけた要素に対して「何をするか」のコードをまとめていきます、どれも前の記事の基本コード(StartChrome → OpenBrowser → NavigateTo でページを開いたあと)の流れの中で使う想定です
クリックする・文字を入力する
クリックは Click、文字入力は SendKeys です、入力欄に元から入っている文字を消したいときは、SendKeys の clearBeforeTyping を True にするか、先に Clear を呼びます
' ボタンをクリック
driver.FindElement(By.CssSelector, ".btn-search").Click
' 入力欄に文字を打ち込む
driver.FindElement(By.CssSelector, "[name='q']").SendKeys "SeleniumVBA"
' 元の文字を消してから入力(clearBeforeTyping)
driver.FindElement(By.CssSelector, "#keyword").SendKeys "新しい値", clearBeforeTyping:=True
' 入力欄を空にするだけ
driver.FindElement(By.CssSelector, "#keyword").Clear
' フォームを送信する
driver.FindElement(By.CssSelector, "#keyword").Submitテキスト・属性・CSSを取り出す
表示テキストを取るのは GetText、リンク先(href)などの属性は GetAttribute、文字色などの見た目は GetCSSProperty で取れます、メソッド名を間違えやすいところなので、ここはまとめて覚えておくと安心です
Dim el As WebElement
Set el = driver.FindElement(By.CssSelector, "#title")
' 表示テキストを取る
Debug.Print el.GetText
' 属性を取る(リンク先の href など)
Debug.Print el.GetAttribute("href")
' 見た目のCSS値を取る(文字色など)
Debug.Print el.GetCSSProperty("color")
' 中のHTMLやタグ名を取りたいとき
Debug.Print el.GetInnerHTML
Debug.Print el.GetTagName
' 表示・有効・選択の状態を真偽値で取れる
Debug.Print el.IsDisplayed
Debug.Print el.IsEnabled
Debug.Print el.IsSelected名前を取り違えやすいので注意です、テキストは .Text ではなく GetText(メソッド)、CSS値は GetCSSProperty で取ります、PythonのSeleniumと名前が少し違うので、ここは間違えやすいポイントです
待機する(ImplicitMaxWait が基本)
ブラウザ自動化でハマりやすいのが「要素がまだ表示されていないのに探しにいって失敗する」パターンです、対策の基本が ImplicitMaxWait です
これを設定しておくと、以降の FindElement が、要素が出てくるまで自動でリトライしながら待ってくれます、ページ表示がもたつくサイトでも、これだけでだいぶ安定します
' 以降の FindElement は、最大10秒まで要素を待つ(ミリ秒指定)
driver.ImplicitMaxWait = 10000
' 単純にその場で止めたいだけなら Wait(ミリ秒)
driver.Wait 1000 ' 1秒待つImplicitMaxWait は「要素が出るまで待つ賢い待機」、Wait は「とにかく指定ミリ秒止まる」だけのシンプルな待機です、基本は ImplicitMaxWait を最初に1回セットしておき、どうしても固定で待ちたい場面だけ Wait を足す、という使い分けがおすすめです
要素があるか確認する(IsPresent)
覚えておきたいのが、FindElement は要素が見つからないとエラーになる、という点です、なので「あるかどうか分からない要素」を相手にするときは、いきなり FindElement しないほうが安全です
そこで使うのが IsPresent です、これは要素があるかどうかを True / False で返してくれるので、エラーで止まらずに分岐できます、第3引数で「この要素は最大何ミリ秒待つか」も指定できます
' 要素があるかを True / False で確認する
If driver.IsPresent(By.CssSelector, ".alert") Then
Debug.Print "アラートが出ています"
End If
' 待ち時間を指定しつつ、見つかった要素を受け取る
Dim el As WebElement
If driver.IsPresent(By.CssSelector, "#result", 5000, , el) Then
Debug.Print el.GetText ' 見つかったときだけ使う
End If最後の引数に ByRef で変数を渡すと、見つかった要素をそのまま受け取れます、「あるか確認 → あれば使う」を1回で書けるので、地味に便利です
プルダウン(select)を選ぶ
プルダウン(select要素)は、専用のメソッドで選べます、表示文字で選ぶ SelectByVisibleText、値で選ぶ SelectByValue、順番で選ぶ SelectByIndex の3つを覚えておけば十分です
Dim sel As WebElement
Set sel = driver.FindElement(By.CssSelector, "#prefecture")
' 表示されている文字で選ぶ
sel.SelectByVisibleText "東京都"
' value 属性の値で選ぶ
sel.SelectByValue "13"
' 上から数えた順番で選ぶ(0始まり)
sel.SelectByIndex 0複数の要素をまとめて取る・ループする
1つではなく、条件に合う要素を全部取りたいときは FindElements(複数形)を使います、これは要素のコレクションを返すので、For Each でぐるっと回せます、一覧ページから値をまとめて拾うときの定番です
Dim els As WebElements
Dim el As WebElement
' class="item" の要素を全部取る
Set els = driver.FindElements(By.CssSelector, ".item")
For Each el In els
Debug.Print el.GetText
Next el表をまるごとExcelに取り込む
VBAでブラウザを動かす一番のうまみが、取ってきた値をそのままExcelに書き出せることです、ページの表(table要素)なら、TableToArray で2次元配列にできるので、シートへの貼り付けがかなりラクになります
' ページの表を2次元配列にして、シートに一気に貼る
Dim tbl As WebElement
Set tbl = driver.FindElement(By.CssSelector, "table#data")
Dim arr As Variant
arr = tbl.TableToArray
Range("A1").Resize(UBound(arr, 1) + 1, UBound(arr, 2) + 1).Value = arr
' 1セルずつ書きたいときは For Each で
Dim els As WebElements, el As WebElement, r As Long
Set els = driver.FindElements(By.CssSelector, ".price")
r = 1
For Each el In els
Cells(r, 1).Value = el.GetText
r = r + 1
Next el逆に、Excelの一覧を読みながらページを順に開く、という回し方もできます、A列に並べたURLやIDを For で回して、そのつど NavigateTo でページを開けば、Excel主導のブラウザ自動化が組めます
スクリーンショットを撮る
画面の証跡を残したいときは SaveScreenshot です、ページ全体・特定の要素だけ・縦に長いページの全体…と撮り分けられます
' 表示中の画面を保存
driver.SaveScreenshot "C:\temp\shot.png"
' 縦に長いページ全体を保存(fullScreenShot)
driver.SaveScreenshot "C:\temp\full.png", , fullScreenShot:=True
' 特定の要素だけ保存
Dim el As WebElement
Set el = driver.FindElement(By.CssSelector, "#chart")
el.SaveScreenshot "C:\temp\chart.png"iframe・タブを切り替える
ページの中に別ページが埋め込まれている iframe の中身は、そのままだと操作できません、SwitchToFrame で中に入って、終わったら SwitchToDefaultContent で元に戻ります
' iframe の中に入る
Dim frame As WebElement
Set frame = driver.FindElement(By.CssSelector, "iframe#content")
driver.SwitchToFrame frame
' iframe 内の要素を操作したあと、元のページに戻る
driver.SwitchToDefaultContentタブやウィンドウの切り替えは Windows 経由で行います、タイトルやURLで目的のタブに移ったり、新しいタブを開いたりできます
' タイトルやURLで目的のタブへ切り替える
driver.Windows.SwitchToByTitle "注文一覧"
driver.Windows.SwitchToByUrl "https://example.com/orders"
' 次のタブへ・新しいタブを開く
driver.Windows.SwitchToNext
driver.Windows.SwitchToNew windowType:=svbaTabJavaScriptを実行する・スクロールする
標準のメソッドだけだと届かない操作は、ExecuteScript でJavaScriptを直接走らせて片付けられます、戻り値も受け取れるので、ちょっとした計算やページ内の値取得にも使えます
JS側では、VBAから渡した引数を arguments[0] の形で受け取れます、よく使うのは「目的の要素まで画面をスクロールする」用途です
' JSを実行して戻り値を受け取る(5 * 10 = 50)
Dim ret As Variant
ret = driver.ExecuteScript("return arguments[0]*arguments[1];", 5, 10)
Debug.Print ret
' 指定した要素までスクロールする
Dim el As WebElement
Set el = driver.FindElement(By.CssSelector, "#footer")
driver.ExecuteScript "arguments[0].scrollIntoView(true);", el
' 画面を下に1000pxスクロールする
driver.ExecuteScript "window.scrollBy(0,1000);"エンターキーなどの特殊キーを送る
検索ボックスに文字を打って、そのままエンターで検索したい…というのもよくあります、特殊キーは WebKeyboard を用意して、SendKeys の文字とつなげて送ります
Dim k As New WebKeyboard
Dim box As WebElement
Set box = driver.FindElement(By.CssSelector, "[name='q']")
' 検索語を打ってエンターキーで送信
box.SendKeys "SeleniumVBA" & k.ReturnKey
' Ctrl + A で入力欄を全選択する
box.SendKeys k.Chord(k.CtrlKey, "a")ヘッドレス・起動オプションを付ける
画面を出さずに裏で動かす(ヘッドレス)や、起動時のオプションを足したいときは、StartChrome のあとに CreateCapabilities で設定オブジェクトを作って、OpenBrowser に渡します
Dim driver As New WebDriver
driver.StartChrome
Dim caps As WebCapabilities
Set caps = driver.CreateCapabilities
' 画面を出さずに動かす(ヘッドレス)
caps.RunInvisible
' 起動オプションを足す
caps.AddArguments "--start-maximized"
' ダウンロード先と確認ダイアログの設定
caps.SetDownloadPrefs downloadFolderPath:="C:\dl\", promptForDownload:=False
' 設定を渡してブラウザを開く
driver.OpenBrowser capsヘッドレスにすると画面が出ないぶん動作が軽くなるので、定期実行や大量処理のときに向いています、ただ動作確認のうちは、画面を出したまま挙動を見ながら作るほうが安心です
便利なヘルパー関数を用意しておく
同じような書き方を毎回くり返すなら、自分用の小さな関数(ヘルパー)にまとめておくと、コードがぐっと読みやすくなります、ここでは私がよく使うものを紹介します、どれも検証済みのメソッドだけで組んでいるので、そのままコピペして使えます
TryFind … 無ければ Nothing を返す(エラーを出さない)
さっき書いたとおり FindElement は要素が無いとエラーになります、TryFind は IsPresent を使って、見つかれば要素を、無ければ Nothing を返します、「あるかどうか分からない要素」を扱うときの土台になります
' 存在チェック付き取得(無ければNothing・エラーを出さない)
Public Function TryFind(driver As WebDriver, ByVal sel As String, _
Optional ByVal waitMS As Long = 3000) As WebElement
Dim el As WebElement
If driver.IsPresent(By.CssSelector, sel, waitMS, , el) Then
Set TryFind = el
Else
Set TryFind = Nothing
End If
End FunctionWaitFor … 出るまで待って、ダメなら明示エラー
WaitFor は逆に、「この要素は確実に出るはず」という場面用です、時間内に出れば返し、出なければ「何のセレクタで失敗したか」が分かるエラーを出します、原因が分からないまま止まるより、メッセージが残るぶんデバッグがラクになります
' 要素が出るまで待って取得(ダメなら明示エラー)
Public Function WaitFor(driver As WebDriver, ByVal sel As String, _
Optional ByVal timeoutMS As Long = 10000) As WebElement
Dim el As WebElement
If driver.IsPresent(By.CssSelector, sel, timeoutMS, , el) Then
Set WaitFor = el
Else
Err.Raise vbObjectError + 513, , "要素が見つかりません: " & sel
End If
End FunctionCss … By.CssSelector を短く書くラッパー
毎回 driver.FindElement(By.CssSelector, “…”) と書くのは長いので、Css(driver, “…”) だけで済むようにしたラッパーです、CSSセレクタに寄せる方針と相性がよく、本文のコードがかなりスッキリします
' By.CssSelector 短縮ラッパー
Public Function Css(driver As WebDriver, ByVal sel As String) As WebElement
Set Css = driver.FindElement(By.CssSelector, sel)
End Function
' 使うとこう書ける
' Css(driver, ".btn-search").ClickSafeClick … 存在・表示・有効を確かめてからクリック
クリックは、要素はあっても「まだ表示されていない・押せない状態」だと失敗することがあります、SafeClick は IsPresent で存在を確かめ、さらに IsDisplayed と IsEnabled が True のときだけ Click します、押せないときに止まらず素通りするので、流れが安定します
' 安全クリック(存在+表示+有効を確認してClick)
Public Sub SafeClick(driver As WebDriver, ByVal sel As String, _
Optional ByVal waitMS As Long = 5000)
Dim el As WebElement
If driver.IsPresent(By.CssSelector, sel, waitMS, , el) Then
If el.IsDisplayed And el.IsEnabled Then el.Click
End If
End Subスクロール系 … 最下部まで・要素を画面中央へ
スクロールまわりも、ExecuteScriptでJavaScriptを走らせる独自functionにしておくと便利です、よく出番があるのが、遅延読み込みや無限スクロールのページで最下部まで自動で送って中身を出し切る処理と、目的の要素を画面の真ん中まで持ってくる処理です
' 最下部までオートスクロール(無限スクロール・遅延読み込み対策)
' 高さが伸びなくなったら読み切ったとみなして止まる
Public Sub ScrollToBottom(driver As WebDriver, _
Optional ByVal maxLoops As Long = 30, _
Optional ByVal waitMS As Long = 800)
Dim lastH As Double, newH As Double, i As Long
lastH = driver.ExecuteScript("return document.body.scrollHeight;")
For i = 1 To maxLoops
driver.ExecuteScript "window.scrollTo(0, document.body.scrollHeight);"
driver.Wait waitMS
newH = driver.ExecuteScript("return document.body.scrollHeight;")
If newH = lastH Then Exit For
lastH = newH
Next i
End Sub
' 対象の要素を画面の中央へスクロール
Public Sub ScrollToCenter(driver As WebDriver, el As WebElement)
driver.ExecuteScript "arguments[0].scrollIntoView({block:'center', inline:'center'});", el
End SubScrollToBottomは、ページの高さが伸びなくなったら「もう読み切った」と判断して止まる作りなので、件数の読めない無限スクロールでも空回りしません、ScrollToCenterは要素を上端ではなく中央に置くので、固定ヘッダーに隠れて押せない…みたいなクリック事故も減らせます
ハマりどころとTips
最後に、書きはじめのうちにつまずきやすいところを、軽くまとめておきます
- 閉じ忘れに注意 … 処理の終わりに CloseBrowser → Shutdown を呼ばないと、裏でWebDriverが残ることがあります、処理の最後に毎回呼ぶ形にしておくのがおすすめです
- まず ImplicitMaxWait をセット … 「要素がまだ無い」系のエラーは、最初に ImplicitMaxWait を入れておくだけでかなり減ります
- 存在チェックは IsPresent で … 無いかもしれない要素を FindElement で取りにいくとエラーになります、迷ったら IsPresent で分岐するのが安全です
- メソッド名の取り違えに注意 … 探すのは By.CssSelector(By.Cssではない)、テキストは GetText、CSS値は GetCSSProperty です、名前が紛らわしいので、ここはコピペで間違いを防ぐのがラクです
このあたりを押さえておけば、たいていの「動かない…」はかなり減らせると思います、あとは作りたい処理に合わせて、ここまでのコードを組み合わせていく感じです
まとめ
SeleniumVBAの実践コードを、ひととおり並べてみました、最後にポイントを整理しておきます
- 要素の探し方は By.CssSelector に寄せると覚えることが減る、リンク文字やテキスト検索はCSS+ループの独自functionで再現でき、XPathが要るのは親への逆引き(“..”)だけ
- 操作は Click / SendKeys / GetText / GetAttribute が基本、待機は ImplicitMaxWait をまずセット
- 無いかもしれない要素は IsPresent で確認、プルダウンや複数要素・スクショ・JS実行も専用メソッドで書ける
- よく使う処理は TryFind / WaitFor / Css / SafeClick のようにヘルパー関数化しておくと読みやすい
まだ導入が済んでいない方は、まず「Excelでもブラウザ操作!SeleniumVBAの使い方」で土台を作ってから、この記事のコードを動かしてみてください
ドライバーの更新まわりで困ったときは「VBAでChromeDriverを自動更新する方法」、これまでの定番だったSeleniumBasicから入りたい方は「SeleniumBasicの導入方法」も合わせて読んでもらえると、VBAでのブラウザ自動化がだいぶ見通しよくなると思います



コメント
コメント一覧 (2件)
はじめまして。sele_chanと申します。
私は主にXParthを使っているのですがシャドウルート内検索では使えないので、その代替としてCssSelectorを使っているのですが、あまり理解していませんでしたので参考にしたいと思います。
シャドウルートはXPathはNGでしたね
好みもある気がしますが、XPathよりわかりやすい気がするのでCssSelectorおススメです!