【SeleniumBasic】Google検索してURLをシートに書き出す【ExcelVBA】

以前こういう記事を書いたところ、実際にお使いになった方から相談をもらいました。

相談内容

全部は載せませんが、だいたい以下のような内容でした。

実現したかったのは下記です。

1検索結果の各URL(飛び先のサイトURL)も取得してエクセルに記載する。
2検索結果N件目以降と指定できるようにしたい。
3できれば100件くらいまとめて取得したい。

回答

私の回答も全部は載せませんが、だいたい下記のような内容で回答しました。

お問い合わせのありましたExcel相談について、下記のとおり回答します。

>1検索結果の各URL(飛び先のサイトURL)も取得してエクセルに記載する。

⇒Attributeで”href”を指定することで取得可能です。

>2検索結果N件目以降と指定できるようにしたい。

⇒これはもともとGoogle検索の検索文字列(“https://www.google.com/search?q=”)の後ろに
&start=と続ければ可能ですので、検索文字列を工夫することにしました。

>3できれば100件くらいまとめて取得したい。

⇒これも検索文字列の後ろに&num=100とすることで可能です。

2と3については、下記のサイトを参考にしました。

Google検索で検索結果の表示件数を増やす方法 – 情シスの自由帳

https://jo-sys.net/google-search/

Google 検索の URL パラメータリスト – WebOS Goodies

http://webos-goodies.jp/archives/50785287.html

解説

URLを取得するには

SeleniumでURLを取得するには、aタグの要素をまず見つけ出し、その要素に対してAttributeで”href”を指定します。

次の図は、実際にGoogle検索で検索結果画面を表示して、F12キーで開発者コンソールを開いたところです。

aタグ部分。hrefというキーワードの後ろにURLがある。

赤い枠でくくった数行下にh3タグがあるのが分かるでしょうか?

検索結果はh3タグのtextから取得し、URLは赤い枠の中にあるように、aタグからとってきます。実際にSeleniumを使って書くと、次のようになります。

Set elements = driver.FindElementsByCss(".yuRUbf a")
:
(中略)
:
(ループの中で)
sh.Cells(lRow, ii).Value = elements(ii).Attribute("href")

なお、今回たまたま”yuRUbf”というクラスの中にaタグが一つしかなかったので簡単なコードとなっていますが、もしaタグが複数あるなら、このような書き方では値を取得できません。

「もっと見る」ボタン

以前は「次へ」というボタンがあり、検索結果がページ分けされ、1、2、3というように番号がふってあったのですが、現在はありません。そのかわり、「もっと見る」ボタンがあります。

「もっと見る」ボタン

このボタンをクリックすると、さらに検索結果が表示されます。

この「もっと見る」がとてもやっかいでした。ローカルウィンドウで確認したところ、IsDisplayedやIsEnabledといったプロパティがことごとく常にTrueで、ボタンが表示されていてもいなくても変化がないのです。

ローカルウィンドウで「もっと見る」ボタンの要素を確認。IsDisplayedもIsEnabledも常にTrueで変化なし。

ところがブラウザ上では表示されていない(不可視化)の場合もあります。

検索結果の終わりかどうか、「もっと見る」ボタンが見えているかどうかで判断するために、どのプロパティを見ればわかるか分からず、悩みました。

そこで注意深く観察したところ、「もっと見る」ボタンがブラウザ上から見えなくなる瞬間に、F12の方で変化する部分があるのに気づきました。

「もっと見る」ボタンを押したときに変化する部分(赤枠の中)

style=”transform: scale(1);”というのがどういう意味なのか私には分かりません。

しかし、ボタンが表示されているときにはscale(0)、ボタンを押してボタンが不可視となった瞬間にscale(1)となることが目視で確認できました。

おそらくJavaScriptなどで使っているのでしょうけど、意味は不明です。重要なのは変化があること。

ということで、この部品を制御で使うことにしました。

もちろんGoogle検索の仕様を理解した上で使っている訳ではないので、ある日突然仕様変更してしまった場合には、この制御は全く使えないものとなるでしょう。

あくまで”ツール”ですのでその辺はご容赦ください。

連続スクロール

今回、Google検索の検索オプションの「連続スクロール」はオンが前提です。

Google検索の検索オプション

オフの場合はうまく動作しません。

連続スクロールというのは、「もっと見る」ボタンをクリックしなくても、スクロールダウンするだけで次々に検索結果が表示されるという機能です。

手動で検索しているときは便利ですが、自動で検索し検索結果をExcelに転記したいときにはやっかいな機能です。

Seleniumでスクロールダウンするときは、JavaScriptを使います。この辺りのコードは覚えていないので、てっとり早くChatGPTに教えてもらいました。

私のした質問。

ExcelVBAでSeleniumBasicを使ってChromeでスクロールダウンするには、どうしたらよいですか。

ChatGPTの回答(長いので一部だけ)。

selenium.ExecuteScript “window.scrollTo(0, document.body.scrollHeight);”

検索結果が毎回変わる

今回試行錯誤していて気が付いたことの一つに、検索結果が毎回変わるという問題があります。

だいたいの検索結果は同じであるようですが、表示順が変わります。

最初に検索したときにはTopに出てきたサイトが、次に同じ単語で検索したら今度は3番目に出てくる・・・・というような違いがちょくちょくありました。

今回、最初に考えたコードでは次のようになっていましたが、途中で変えました。

  • ①検索結果の文字列をゲット
  • ②Excelに①を転記
  • ③飛び先サイトのURLをゲット
  • ④Excelに③を転記

ところが、②から③の間にGoogle検索での表示順が変わってしまい、Excelに転記したときには順番が正しくないという不具合が発生しました。

そこで、次のように変えました。

  • ①ざっくりと検索結果の塊をテキストとURLごとゲット
  • ②①をさらに細かくテキストだけで分ける
  • ③①をさらに細かく、URLだけで分ける
  • ④Excelに②と③を一度に書き出す

FindElementsBy○○はWevDriverクラスで使うことが多いのですが、WebElementクラスでも使えます。

WebElementにもFindElementsByCssがある

これを利用して、最初に「.yuRUbf」でElementsをざっくりと把握し、その後TextとAttribute(“href”)は、各Elementに対するFindElementByTag(“h3”)や、FindElementByTag(“a”)で取得するという構成にしました。

    '検索結果を取得
    Dim elements As Selenium.WebElements
    Set elements = driver.FindElementsByCss(".yuRUbf")
    driver.Wait clWAIT
    
    Dim stResults() As Variant  '検索結果を格納する多次元配列。

    '検索結果を配列にいったん格納
    Dim Cnt As Long
    Cnt = elements.Count
    
    ReDim stResults(1 To 2, 1 To Cnt)
    
    Dim ii As Long
    For ii = 1 To Cnt
        stResults(1, ii) = elements(ii).FindElementByTag("h3").Text             '検索結果文字列
        stResults(2, ii) = elements(ii).FindElementByTag("a").Attribute("href") '飛び先サイトのURL
    Next ii

今回作ったもの

ワークシート

セルB1に検索したい文字列を入れます。

セルI1に検索結果の何件目から情報を把握するか入力します。

シート名は使っていませんが、このブックの先頭シートである必要があります。

フォームコントロールのボタンを作って、マクロの登録で「Google検索」マクロを登録してあります。

コード

Option Explicit
'ワークシート制御用Enum
Enum e
    検索結果 = 1
    飛び先のサイトURL
    TheEnd = 飛び先のサイトURL
End Enum

'
'作成:野口香
'作成:2023/12/22
'
'Google検索の「設定」で「連続スクロール」をオン(規定値)にしてある前提で作ってあります。
'
Public Sub Google検索()
    '作業用シート
    Dim sh As Worksheet
    Set sh = ThisWorkbook.Worksheets(1)
    
    'Seleniumを用意
    Dim driver As Object
    Set driver = CreateObject("Selenium.WebDriver")
    
    'スクレイピングのお作法・・・wait時間
    Const clWAIT As Long = 100
    
    'Chromeをスタート
    driver.Start "Chrome"
    
    'ブラウザを最大サイズに
    driver.Window.Maximize
    
    '検索文字をワークシートから取得
    Dim stMoji As String
    stMoji = sh.Range("B1").Value
    
    '検索結果の何件目以降か把握
    Dim lOver As Long
    lOver = sh.Range("I1").Value
    If lOver > 0 Then
        lOver = lOver - 1   'Google検索の仕様(0起算)に合わせるため、1をマイナスしておく。
    End If
        
    'GoogleURLを生成する
    Dim stUrl As String
    stUrl = "https://www.google.com/search?q=" & stMoji & "&start=" & lOver & "&num=100"
    sh.Range("B2").Value = stUrl    '参考程度にワークシート上に検索に使うURL文字列を転記
    
    'ワークシートの前回検索結果をクリアコンテンツ
    Dim lLast1 As Long
    lLast1 = sh.Cells(sh.Rows.Count, e.検索結果).End(xlUp).Row
    Dim lLast2 As Long
    lLast2 = sh.Cells(sh.Rows.Count, e.飛び先のサイトURL).End(xlUp).Row
    If lLast1 > lLast2 Then
        'Nothing
    Else
        '大きい方を採用
        lLast1 = lLast2
    End If
    
    Const clStart As Long = 4   'データ行の始まり
    
    If lLast1 <= clStart Then
        '前回データがないのでなにもしない
    Else
        '前回データをクリアコンテンツ
        sh.Range(sh.Cells(clStart, e.検索結果), sh.Cells(lLast1, e.TheEnd)).ClearContents
    End If
    
    
    '検索
    driver.Get stUrl
    driver.Wait clWAIT
    
    '検索結果を取得
    Dim elements As Selenium.WebElements
    Set elements = driver.FindElementsByCss(".yuRUbf")
    driver.Wait clWAIT
    
    Dim stResults() As Variant  '検索結果を格納する多次元配列。

    '検索結果を配列にいったん格納
    Dim Cnt As Long
    Cnt = elements.Count
    
    ReDim stResults(1 To 2, 1 To Cnt)
    
    Dim ii As Long
    For ii = 1 To Cnt
        stResults(1, ii) = elements(ii).FindElementByTag("h3").Text             '検索結果文字列
        stResults(2, ii) = elements(ii).FindElementByTag("a").Attribute("href") '飛び先サイトのURL
    Next ii
    
    Dim lNext As Long
    lNext = Cnt   '既に調べてある件数を把握する
    Dim myBy As Selenium.By
    Set myBy = New Selenium.By
    If driver.IsElementPresent(myBy.Css("#botstuff > div > div.sdjuGf > div.WZH4jc.w7LJsc > a.T7sFge.sW9g3e.VknLRd")) Then
    
        '「もっと見る」のstyleがtransform: scale(1);なら可視、transform: scale(0);なら不可視。
        'IsDisplayedやIsEnabledは常にtrueで変化がなかったので使えず、このstyleの変化を拾うことにした。
        
        Do While driver.FindElementByCss("#botstuff > div > div.sdjuGf > div.WZH4jc.w7LJsc > a.T7sFge.sW9g3e.VknLRd").Attribute("style") = "transform: scale(1);"
            
            driver.Wait clWAIT
            '「もっと見る」があるならめいいっぱいスクロールダウンしておく
            driver.ExecuteScript ("window.scrollTo(0, document.documentElement.scrollHeight);")
            driver.Wait clWAIT
            
            '検索結果を取得
            Set elements = driver.FindElementsByCss(".yuRUbf")
            driver.Wait clWAIT
            
            '検索結果を配列にいったん格納
            Cnt = elements.Count
            If Cnt = lNext Then
                'もう新しい検索結果はない
            Else
                ReDim Preserve stResults(1 To 2, 1 To Cnt)
                For ii = lNext To Cnt
                    stResults(1, ii) = elements(ii).FindElementByTag("h3").Text
                    stResults(2, ii) = elements(ii).FindElementByTag("a").Attribute("href")
                Next ii
                
            End If
            
            lNext = Cnt
        Loop
    End If
    
    '検索結果を取得
    Set elements = driver.FindElementsByCss(".yuRUbf")
    driver.Wait clWAIT
    Cnt = elements.Count
    If lNext < Cnt Then
        '最後の検索結果が残っているので、それらも取得する
        
        '検索結果を配列にいったん格納
        ReDim Preserve stResults(1 To 2, 1 To Cnt)
        For ii = lNext To Cnt
            stResults(1, ii) = elements(ii).FindElementByTag("h3").Text
            stResults(2, ii) = elements(ii).FindElementByTag("a").Attribute("href")
        Next ii
    End If

    '配列の内容をExcelに書き出す
    sh.Range(sh.Cells(clStart, e.検索結果), sh.Cells(clStart + UBound(stResults, 2) - 1, e.TheEnd)).Value = WorksheetFunction.Transpose(stResults)
    
    '終了
    driver.Close
    
    'オブジェクト解放
    Set sh = Nothing
    Set driver = Nothing
    Set myBy = Nothing
    
    '終了メッセージ
    MsgBox "終了"
End Sub

ZIP

今回作ったものをZIPにして置いておきます。

https://kn-sharoushi.com/wp-content/uploads/2023/12/20231220Google検索.zip

改変は自由ですが、著作権は放棄していません。