【SeleniumBasic】Google検索してURLをシートに書き出す【ExcelVBA】
- [記事公開]2023.12.24[最終更新]2023.12.26
- VBA
- ExcelVBA, SeleniumBasic
以前こういう記事を書いたところ、実際にお使いになった方から相談をもらいました。
相談内容
全部は載せませんが、だいたい以下のような内容でした。
実現したかったのは下記です。
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キーで開発者コンソールを開いたところです。
赤い枠でくくった数行下に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で、ボタンが表示されていてもいなくても変化がないのです。
ところがブラウザ上では表示されていない(不可視化)の場合もあります。
検索結果の終わりかどうか、「もっと見る」ボタンが見えているかどうかで判断するために、どのプロパティを見ればわかるか分からず、悩みました。
そこで注意深く観察したところ、「もっと見る」ボタンがブラウザ上から見えなくなる瞬間に、F12の方で変化する部分があるのに気づきました。
style=”transform: scale(1);”というのがどういう意味なのか私には分かりません。
しかし、ボタンが表示されているときにはscale(0)、ボタンを押してボタンが不可視となった瞬間にscale(1)となることが目視で確認できました。
おそらくJavaScriptなどで使っているのでしょうけど、意味は不明です。重要なのは変化があること。
ということで、この部品を制御で使うことにしました。
もちろん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クラスでも使えます。
これを利用して、最初に「.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
改変は自由ですが、著作権は放棄していません。
-
前の記事
ハラスメント防止措置の優先順位 2023.11.15
-
次の記事
記事がありません