自動化厨のプログラミングメモブログ │ CODE:LIFE

Python/ExcelVBA/JavaScript/Raspberry Piなどで色んなことを自動化

【VBA】querySelectorAllで取得した要素に対してForEachを使うと謎のエラーが発生する

この記事でできるようになること

  • querySelectorAllで取得したコレクションに対してループ処理

ExcelVBAでWebページから情報を自動収集(スクレイピング)する時に超便利なメソッド querySelectorquerySelectorAll

CSSセレクタがそのまま使えて超便利だが、何故かquerySelectorAllに対してFor Eachを使ってループ処理を行うと処理を終えるタイミングで謎のエラーが発生して切断される事象に頭を悩ませていました。

f:id:maru0014:20190517222059p:plain
このWebページに問題があるため、Internet Explorerのタブを開き直しました

会社の人に相談しながらどうにか回避策を見つけたので共有したい。

結論から言うと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使うな的なことでしょうかね。

IE,Edgeでdocument.querySelectorAllをforEachする方法 - Qiita

querySelectorAll()の戻り値はArrayじゃないよ - Qiita