この記事でできるようになること
- querySelectorAllで取得したコレクションに対してループ処理
ExcelVBAでWebページから情報を自動収集(スクレイピング)する時に超便利なメソッド querySelector
とquerySelectorAll
。
CSSセレクタがそのまま使えて超便利だが、何故かquerySelectorAllに対してFor Eachを使ってループ処理を行うと処理を終えるタイミングで謎のエラーが発生して切断される事象に頭を悩ませていました。
会社の人に相談しながらどうにか回避策を見つけたので共有したい。
結論から言うとFor Eachは使わず For で回避する
こうしたらエラーなしで動きました。
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Googleで「VBA」を検索して結果を取得する ' Sub web_scraping() Dim oIE As InternetExplorer Set oIE = New InternetExplorer 'IE初期化 oIE.Visible = True 'IE表示/非表示 Dim URL As String URL = "https://www.google.com/search?q=VBA" 'アクセスするURL oIE.Navigate2 (URL) 'URLを開く Call BrowserWait(oIE) '読み込み待機 Dim els Set els = oIE.Document.querySelectorAll(".rc > .r") 'elsの要素数分だけ繰り返し処理 Dim i As Integer For i = 0 To els.Length - 1 Debug.Print els.Item(i).querySelector("h3").innerText 'ページ名 Debug.Print els.Item(i).querySelector("a").href 'ページURL Next oIE.Quit 'IEクローズ Set oIE = Nothing 'オブジェクト開放 End Sub Sub BrowserWait(oIE As InternetExplorer) '// 読み込みが完了するまで待つ While oIE.readyState <> READYSTATE_COMPLETE Or oIE.Busy = True DoEvents Sleep 200 Wend '// 読み込み完了後の安定化待ち Sleep 200 End Sub
※コピペしてテストしたい場合は参照設定でMicrosoft Internet Controls
を有効化してください
これで安心してquerySelectorAllを活用できるので無理してSelenium環境作らなくてもExcel単体である程度は頑張れそうです!
そのうち1からスクレイピングツール作っていく入門記事でも書こうかな。
エラーが発生したパターン
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Sub web_scraping() Dim oIE As InternetExplorer Set oIE = New InternetExplorer 'IE初期化 oIE.Visible = True 'IE表示/非表示 Dim URL As String URL = "https://www.google.com/search?q=VBA" 'アクセスするURL oIE.Navigate2 (URL) 'URLを開く Call BrowserWait(oIE) '読み込み待機 Dim els Set els = oIE.Document.querySelectorAll(".rc > .r") 'elsの要素が見つからなくなるまで繰り返し処理 Dim e For Each e In els Debug.Print e.querySelector("h3").innerText 'ページ名 Debug.Print e.querySelector("a").href 'ページURL Next '★動くには動くがここでエラーが発生する oIE.Quit 'IEクローズ Set oIE = Nothing 'オブジェクト開放 End Sub Sub BrowserWait(oIE As InternetExplorer) '// 読み込みが完了するまで待つ While oIE.readyState <> READYSTATE_COMPLETE Or oIE.Busy = True DoEvents Sleep 200 Wend '// 読み込み完了後の安定化待ち Sleep 200 End Sub
なんでエラーになるんだろう
会社の人に教わりながらウォッチウィンドウで”そもそもどんなプロパティを持っているんだろう”と調べようとしたらウォッチした瞬間死にました(笑)。
他にもいろいろ調べていたところ以下の記事も発見。
どうやらそもそもForEach使うな的なことでしょうかね。