【ExcelVBA】Yahoo!地図からルート検索【SeleniumBasic】
- [記事公開]2023.06.14
- VBA
- ExcelVBA, SeleniumBasic
昨日の記事の続きです。
Yahoo!地図のルート検索を、SeleniumBasicもWebdriverもなしにExcelVBAだけでアクセスして情報をゲットするのは難しいという話を、お問い合わせをいただいた方にメールしたところ、職場でSeleniumBasic等のダウンロードが申請すれば許可されるかもしれないということでした。
SeleniumBasicとWebdriverがダウンロードできるのであれば、もう何も困ることはありません。
ということで今回は、Yahoo!地図のルート検索の距離を、SeleniumBasicを使って取得する方法を紹介します。
仕様
Yahoo!地図でルート検索すると、候補が3件表示されます。
今回のその3件の距離数を把握したいとのことでした。
さらに、出発地点と到着地点を入れ替えたときの3件の距離数も把握したいとのこと。
ワークシートの体裁はこんな感じになります。
シート名に特に制約はありませんが、先頭にあるシートを使うことにしました。
使うブラウザはChromeかEdgeで、どちらか使いやすい方でよいとのことでしたので、Chromeにしました。
環境
Windows10
MicrosoftOffice365
Chromeバージョン114.0.5735.110(Official Build) (64 ビット)
ChromeのWebdriver(chromedriver.exe)はブラウザに合わせたものをダウンロードしてあります。
VBEの参照設定で、下記のとおりSelenium Type Libraryにチェックをいれてあります。
コード
私が考えたコードはこんな感じです。
Option Explicit
'ワークシートに表示する情報のヘッダ
Enum e
出発地 = 1
目的地
往路距離1
往路距離2
往路距離3
復路距離1
復路距離2
復路距離3
TheEnd = 復路距離3
End Enum
'
'作成:野口香
'作成:2023/06/14
'
Public Sub GetMap()
'作業用シート
Dim sh As Worksheet
Set sh = ThisWorkbook.Worksheets(1)
'Seleniumを用意
Dim driver As Object
Set driver = CreateObject("Selenium.WebDriver")
'スクレイピングのお作法・・・wait時間
Const clWAIT As Long = 500
'Chromeをスタート
driver.Start "Chrome"
'ブラウザを最大サイズに
driver.Window.Maximize
'前回までの情報をクリアしておく
Const clHeader As Long = 6
sh.Range(sh.Cells(clHeader + 1, e.往路距離1), sh.Cells(sh.Rows.Count, e.TheEnd)).ClearContents
'Yahoo!地図のURL
Const cstUrl As String = "https://map.yahoo.co.jp/route/car"
driver.Get cstUrl
driver.Wait clWAIT * 15 'けっこう長めに待たないとだめみたいだ・・・①
'Loop開始前、準備
'ワークシート上の調査対象件数が何件か把握しておく
Dim ii As Long, lRow As Long
Const cstStart As Long = clHeader + 1 'データが入っている行の最初の行の行番号
lRow = sh.Cells(cstStart, e.目的地).End(xlDown).Row
If lRow = sh.Rows.Count Then
lRow = cstStart
End If
'出発地
Dim el As Object
Dim myBy As By
Set myBy = New By
Dim Keys As Keys
Set Keys = New Keys
For ii = cstStart To lRow
'「スタート地点の入力を削除」ボタンが存在するなら・・・②
If driver.IsElementPresent(myBy.XPath("//*[@id=""search_route_page""]/div[1]/div[1]/div/div[1]/div[1]/div/button")) Then
'「スタート地点の入力を削除」ボタンを押す(これをやっておかないとInputボックスの中身がクリアされなかった) ・・・②
driver.FindElementByXPath("//*[@id=""search_route_page""]/div[1]/div[1]/div/div[1]/div[1]/div/button").Click
driver.Wait clWAIT
End If
'出発地
Set el = driver.FindElementByXPath("//*[@id=""route_start""]")
el.Clear
driver.Wait clWAIT
el.SendKeys sh.Cells(ii, e.出発地).Value
driver.Wait clWAIT
el.SendKeys Keys.Tab 'ここでタブキーを押すのは、これをやっておかないと出発地点の候補がリスト表示されてしまい、次の目的地のエレメントに覆いかぶさってしまい、
'「そんなエレメントは見つからない」というエラーになってしまうため。・・・③
driver.Wait clWAIT
'「ゴール地点の入力を削除」ボタンを押す(これをやっておかないとInputボックスの中身がクリアされなかった)
If driver.IsElementPresent(myBy.XPath("//*[@id=""search_route_page""]/div[1]/div[1]/div/div[1]/div[2]/div/button")) Then
driver.FindElementByXPath("//*[@id=""search_route_page""]/div[1]/div[1]/div/div[1]/div[2]/div/button").Click
driver.Wait clWAIT
End If
'目的地
Set el = driver.FindElementByXPath("//*[@id=""route_goal""]")
el.Clear
driver.Wait clWAIT
el.SendKeys sh.Cells(ii, e.目的地).Value
driver.Wait clWAIT
'「ルートを検索」ボタンをクリック
driver.FindElementByXPath("//*[@id=""search_route_page""]/div/div[2]/button").Click
driver.Wait clWAIT * 10 '少し長めに待つ
'往路の距離を取得する
Call GetKM("往路", sh, ii, driver, clWAIT)
'「スタートとゴールを入れ替え」ボタンを押す
driver.FindElementByXPath("//*[@id=""search_route_page""]/div[1]/div[1]/div/div[2]/div/button").Click
driver.Wait clWAIT * 10 '少し長めに待つ
'復路の距離を取得する
Call GetKM("復路", sh, ii, driver, clWAIT)
Next ii
'終了
driver.Close
'オブジェクト解放
Set sh = Nothing
Set driver = Nothing
Set el = Nothing
'終了メッセージ
MsgBox "終了"
End Sub
Private Sub GetKM(ByVal st路, ByRef sh As Worksheet, ByRef ii As Long, ByRef driver As Object, ByRef clWAIT As Long)
Dim lColumn As Long
If st路 = "往路" Then
lColumn = e.往路距離1
Else
lColumn = e.復路距離1
End If
'距離1を取得
sh.Cells(ii, lColumn).Value = Replace(driver.FindElementByXPath("//*[@id=""search_route_page""]/div[2]/ul/li[1]/button/div/div[1]/span").Text, "-", "")
driver.Wait clWAIT
'距離2を取得
sh.Cells(ii, lColumn + 1).Value = Replace(driver.FindElementByXPath("//*[@id=""search_route_page""]/div[2]/ul/li[2]/button/div/div[1]/span").Text, "-", "")
driver.Wait clWAIT
'距離3を取得
sh.Cells(ii, lColumn + 2).Value = Replace(driver.FindElementByXPath("//*[@id=""search_route_page""]/div[2]/ul/li[3]/button/div/div[1]/span").Text, "-", "")
driver.Wait clWAIT
End Sub
解説
順に説明します。
①長めに待つ
スクレイピングではネットワークを介してサーバにアクセスする動作が相手方サーバに迷惑をかけないよう、わざとWait時間をあちこちで設けているものなんですが、今回Yahoo!地図にアクセスした直後のWait時間は相手方サーバに迷惑をかけないためというより、読み込みに時間がかかるためにかなり長めにしました。
でないと、なかなかすべての要素が表示されなかったです。
すべての要素が表示されきる前に次のステップに進んでしまうと、「そんな要素はない」とVBAエラーになってしまうのです。
この辺のWait時間はネットワーク環境にもよるものがあると思うので、お使いの環境でチューンナップしてください。
②スタート地点の入力を削除
スタート地点の入力を削除とはどういうことかというと、最初このIf文は書いていませんでした。
ところがデバッグ中に、次のようなエラーが発生しました。
ん?なんじゃこりゃ?
検索対象となったものがすべて目的地に入ってしまっているようです。
ゴールのインプットボックスにカーソルを置いて確かめたところ、
確かに、そこまでに検索していたすべての目的地が、一つのインプットボックスに書かれていました。
コードにはインプットボックスの中身をクリアコンテンツするための
el.Clear
というステップを用意しておいたのですが、うまくいっていないようです。
サイトをよく見ると、×のマークがボックスの右側にあります。
このボタンを押さないと、インプットボックスの中身がきちんとクリアされないようです。
なおこの×ボタンは、1回目の入力のときには存在しません。
何らかの入力があると、×ボタンが表示されます。
そのためIf文で×ボタンの要素が存在するかどうか調べています。要素が存在するかどうかIsElementPresentを使っています。
ゴール地点の入力を削除するボタンも同様に制御しています。
③タブキーを押す
出発地点の入力を終えた後、タブキーを1回押しています。
これはなぜかというと、次のような不具合が起きないようにするためです。
要素は可視である必要があります(人間が見てちゃんと見える状態)。このように要素が覆われてしまうと、「その要素は可視状態ではない」とエラーになります。
ということで、Tabキーを1回押してインプットボックスに入っているカーソルをボックスの外に出しています。
これにより、候補のリストが表示されるのを避けることができます。
④GetKM
距離については往路と復路とで同じ要素を参照します。
同じ要素をだらだらとコード内に重複して書きたくなかったのでGetKMというサブルーチンにしました。
引数は第1引数以外はすべてRef参照(呼び出し元と共有)としました。
まとめ
以上、Yahoo!地図から情報を取得する方法について紹介しました。
なお、Yahoo!地図の利用規約を確認したところ、スクレイピングを禁止する規程はみつけることができませんでしたが、「合理的に必要相当な数を超える利用、乱用」は禁止されていましたので、自動実行する際は必要最小限のアクセスとなるよう、お気を付けください。また、Wait時間は長めにとるのが無難でしょう。
本日作ったものをZIPにしておいておきます。
https://kn-sharoushi.com/wp-content/uploads/2023/06/20230614GetYahooMapBySeleniumBasic.zip
-
前の記事
【ExcelVBA】Yahoo!地図のルート検索で情報取得はできるか【SeleniumBasicなしで】【Webdriverなしで】 2023.06.13
-
次の記事
労使協定の有効期限 2023.06.15