【チュートリアル】VBAでIE自動化ツールを作る(その11) 書籍検索ツール開発(実装・画面遷移)

こんにちは、自動化エンジニアをしています。kozuです。

エクセルVBAによりWebページの要素(テキストボックス、ボタンなど)を操作したり、表示されている情報を取得する自動化ツールの開発方法を紹介します。実際のWebサイトを自動操作し情報を取得するマクロの開発を通して、自動化ツールの開発について学ぶことができます。

本連載では、チュートリアルということでVBAの開発手順から始め、IEの操作についてどのようなページでも共通で使用できるコード(共通部品)を作成します。最初に共通部品を作成しておくことで、自分でIEの自動化ツールを作成する際に少量の実装で効率よく開発できるようになります。

この章では、これまでに実装した共通部品を使用したIEの自動化ツールの開発を行っていきます。出版社(秀和システム)のWebサイトから指定した条件で新しい本を検索するツールを開発します。前回はインプット情報の取得処理を作成したので、今回はIEの操作として画面遷移の処理を実装していきます。

1.対象者

ブラウザで定期的に手作業で行っているデータ入力やデータ収集等の作業から開放されたい、楽したいと考えている方を対象に、エクセルVBAで自動化するツールの開発ができるようになることを目指しています。プログラミングの経験がない方でも、コードをコピーすれば開発できるようになっています。

2.開発環境

以下の環境を使用します。
バージョンは異なっていても問題ないと思います。
・OS:Windows10
・エクセル:Microsoft Excel2007
・ブラウザ:InternetExploer11

3.セレクタを確認する

IEを操作するために、まずは要素を特定するためのセレクタを確認します。本連載(その3)要素の調べ方(その4)要素の取得パターンで解説しています。今回使用するセレクタは以下の通りです。

① トップ画面の「詳細検索」ボタン
② 検索フォームの「キーワード」
③ 検索フォームの「タイトル」
④ 検索フォームの「著者名」
⑤ 検索フォームの「ジャンル」
⑥ 検索フォームの「シリーズ」
⑦ 検索フォームの「ISBN」
⑧ 検索フォームの「表示件数」
⑨ 検索フォームの「並び順」
⑩ 検索フォームの「検索」ボタン
⑪ 検索結果の本の詳細ページのリンク(本のタイトル)
⑫ 検索結果の次ページのリンク(次の◯件)

確認した結果が以下になります。「SearchBooks」の一番上(Option Explicitの下)に追加してください。
※「SELECTOR_RESULT_DETAIL」と「SELECTOR_RESULT_DETAIL_CHILD 」は両方とも⑪に対応し、前者はページ内の全ての本のリンクのセレクタであり、後者はページ内の1つの本のセレクタになります。「{0}」に1スタートの整数を指定して使用します。

'トップページのセレクタ
Const SELECTOR_SEARCH_DETAIL As String = "a.btnSearchBox"

'検索フォームのセレクタ
Const SELECTOR_FORM_KEYWORD As String = "input[name=""search_keyword""]"
Const SELECTOR_FORM_TITLE As String = "input[name=""search_title""]"
Const SELECTOR_FORM_AUTHOR As String = "input[name=""search_author""]"
Const SELECTOR_FORM_GENRE As String = "select[name=""search_genre""]"
Const SELECTOR_FORM_SERIESE As String = "select[name=""search_series""]"
Const SELECTOR_FORM_ISBN As String = "input[name=""search_isbn""]"
Const SELECTOR_FORM_AMOUNT As String = "select[name=""amount""]"
Const SELECTOR_FORM_ORDER As String = "select[name=""order""]"
Const SELECTOR_FORM_BUTTON As String = "a.search_submit"

'検索結果のセレクタ
Const SELECTOR_RESULT_DETAIL As String = "a.next"
Const SELECTOR_RESULT_NEXT As String = "div.ttl a"
Const SELECTOR_SEARCH_DETAIL As String = "a.btnSearchBox"

'検索フォームのセレクタ
Const SELECTOR_FORM_KEYWORD As String = "input[name=""search_keyword""]"
Const SELECTOR_FORM_TITLE As String = "input[name=""search_title""]"
Const SELECTOR_FORM_AUTHOR As String = "input[name=""search_author""]"
Const SELECTOR_FORM_GENRE As String = "select[name=""search_genre""]"
Const SELECTOR_FORM_SERIESE As String = "select[name=""search_series""]"
Const SELECTOR_FORM_ISBN As String = "input[name=""search_isbn""]"
Const SELECTOR_FORM_AMOUNT As String = "select[name=""amount""]"
Const SELECTOR_FORM_ORDER As String = "select[name=""order""]"
Const SELECTOR_FORM_BUTTON As String = "a.search_submit"

'検索結果のセレクタ
Const SELECTOR_RESULT_DETAIL As String = "div.ttl a"
Const SELECTOR_RESULT_DETAIL_CHILD As String = "li:nth-child({0}) div.ttl a"
Const SELECTOR_RESULT_NEXT As String = "a.next"

4.画面操作(検索フォーム入力)

セレクタの確認ができましたので、実際に操作する処理を実装していきます。トップページの詳細検索フォームを表示し、フォームの入力まで実装します。「SearchBooks」に以下のコードを追加してください。

'書籍情報取得
Public Function GetBookInfos(ByVal url As String, ByRef info As SearchInfo, ByVal getCount As Integer) As BookInfo()
   Dim aryBookInfo() As BookInfo
   Dim objIE As New InternetExplorer
   
   'IEでURLのページを表示
   Call IEControl.OpenUrl(objIE, url)
   
   '詳細検索クリック
   Call IEControl.ClickLink(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_SEARCH_DETAIL))
   
   '詳細検索
   Call SearchDetail(objIE, info)
   
   GetBookInfos = aryBookInfo
End Function

'詳細検索
Private Sub SearchDetail(ByVal objIE As InternetExplorer, ByRef info As SearchInfo)
   'フォーム入力
   'キーワード
   Call IEControl.InputText(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_KEYWORD), info.Keyword)
   'タイトル
   Call IEControl.InputText(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_TITLE), info.Title)
   '著者名
   Call IEControl.InputText(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_AUTHOR), info.Author)
   'ジャンル
   Call IEControl.SelectListValue(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_GENRE), info.Genre)
   'シリーズ
   Call IEControl.SelectListValue(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_SERIESE), info.Seriese)
   'ISBN
   Call IEControl.InputText(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_ISBN), info.ISBN)
   '表示件数
   If info.Amount <> "" Then
       Call IEControl.SelectListValue(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_AMOUNT), info.Amount)
   End If
   '並び順
   If info.Order <> "" Then
       Call IEControl.SelectListValue(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_ORDER), info.Order)
   End If
   
End Sub

次に、「Sheet1(インプット)」に以下の通りにコードを編集してください。

Public Sub 書籍検索()
   '省略
   
   '書籍情報取得
   Call SearchBooks.GetBookInfos(url, info, getMaxCount)
   
End Sub

「インプット」シートに以下の通りに値を設定し、「実行」ボタンをクリックして動作を確認してみます。以下の画像の通りフォームの全ての項目に値が設定されれば問題ありません。

画像1

画像2

5.画面操作(検索ボタンクリック)

フォームの入力が正しくできましたので、「検索」ボタンをクリックします。「SearchBooks」に以下のコードを追加してください。「実行」ボタンをクリックし、検索結果が表示されることを確認してください。

'詳細検索
Private Sub SearchDetail(ByVal objIE As InternetExplorer, ByRef info As SearchInfo)
   'フォーム入力
   '省略
   
   '検索ボタンクリック
   Call IEControl.ClickButton(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_FORM_BUTTON))
End Sub

画像3

6.画面操作(詳細ページの遷移)

検索結果の表示まで操作できましたので、検索結果の本の詳細ページに遷移する操作を実装します。ここで、今までとは異なる点があります。今までは操作対象の要素が1つしかありませんでしたが、今回は操作対象が複数あり、数がわからないという点です。

手順として、まずは「IEControl.GetHtmlObjByQuerySelectorAll」で1ページに表示されている本のリンク要素を取得し、要素数を取得します。次に、要素の数だけ繰り返し、本のリンクをクリックして詳細ページに遷移します。最後に、「IEControl.PageBack」で前のページに戻ります。この手順ですべての本の詳細ページを表示することができます。繰り返しの前で本のリンク要素を取得し、繰り返しの中で再度同様に取得していますが、ページの遷移で取得していた要素が参照できなくなってしまうため、ページを戻ったときに再度取得するようにしています。

'書籍情報取得
Public Function GetBookInfos(ByVal url As String, ByRef info As SearchInfo, ByVal getCount As Integer) As BookInfo()
   '省略
   
   'ページ内に表示されている本の数を取得
   Dim objIEBookList As Object
   Dim elementsCount As Integer
   Set objIEBookList = IEControl.GetHtmlObjByQuerySelectorAll(objIE, SELECTOR_RESULT_DETAIL)
   elementsCount = objIEBookList.Length
   
   Dim i As Integer
   For i = 0 To elementsCount - 1
       Set objIEBookList = IEControl.GetHtmlObjByQuerySelectorAll(objIE, SELECTOR_RESULT_DETAIL)
       
       '詳細ページへ遷移
       Dim selector As String
       selector = Replace(SELECTOR_RESULT_DETAIL_CHILD, "{0}", i + 1)
       Call IEControl.ClickLink(IEControl.GetHtmlObjByQuerySelector(objIE, selector))
       
       '検索結果のページに戻る
       Call IEControl.PageBack(objIE)
   Next
   
   GetBookInfos = result
End Function

「インプット」シートを以下のように設定し実行すると、ページに表示されている本の詳細ページに遷移し、検索結果のページに戻る操作が繰り返されることが確認できます。

画像4

7.画面操作(次ページの遷移)

1ページ内の全ての本の詳細ページにアクセスした後、さらに取得する場合は「次の◯件」をクリックし、次のページに遷移します。「SearchBooks」に以下の通りにコードを編集してください。ページ内の全部の本を見た後に「ExistsElement」で「次の◯件」の要素が存在するか確認します。要素が存在する場合は次のページに遷移し、要素が存在しない場合は繰り返しを終了します。「ExistsElement」では要素を取得したときに要素が存在する場合は処理が正常に進み結果をTrueにしますが、要素が存在しない場合はエラーになるため、「On Error GoTo」で「Catch:」まで処理を飛ばし結果をFalseにすることで判定しています。

'書籍情報取得
Public Function GetBookInfos(ByVal url As String, ByRef info As SearchInfo, ByVal getCount As Integer) As BookInfo()
   '省略
   
   '本の詳細情報取得
   Do While True
       'ページ内に表示されている本の数を取得
       Dim objIEBookList As Object
       Dim elementsCount As Integer
       Set objIEBookList = IEControl.GetHtmlObjByQuerySelectorAll(objIE, SELECTOR_RESULT_DETAIL)
       elementsCount = objIEBookList.Length
       
       Dim i As Integer
       For i = 0 To elementsCount - 1
           Set objIEBookList = IEControl.GetHtmlObjByQuerySelectorAll(objIE, SELECTOR_RESULT_DETAIL)
           
           '詳細ページへ遷移
           Dim selector As String
           selector = Replace(SELECTOR_RESULT_DETAIL_CHILD, "{0}", i + 1)
           Call IEControl.ClickLink(IEControl.GetHtmlObjByQuerySelector(objIE, selector))
           
           '検索結果のページに戻る
           Call IEControl.PageBack(objIE)
       Next
       
       '「次の◯件」の要素が存在するか確認
       If ExistsElement(objIE, SELECTOR_RESULT_NEXT) = False Then
           '次のページがない場合は終了
           Exit Do
       Else
           '次のページに遷移する
           Call IEControl.ClickLink(IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_RESULT_NEXT))
       End If
   Loop
   
   GetBookInfos = result
End Function


'要素の存在確認
Private Function ExistsElement(ByVal objIE As InternetExplorer, ByVal selector As String) As Boolean
   On Error GoTo Catch
   
   '要素が存在しない場合はエラーでCatchに入る
   Call IEControl.GetHtmlObjByQuerySelector(objIE, selector)
   ExistsElement = True
   GoTo Finally
   
Catch:
   ExistsElement = False
Finally:
End Function

実行すると、検索結果のページ内の全部の本の詳細ページ、次のページがある場合は次のページに遷移し、遷移先の全部の本の詳細ページにアクセスすることで、検索結果の全ての本にアクセスされることが確認できます。


ここまでで、画面遷移の処理ができましたので、次回は詳細ページから情報を取得する処理を実装します。

この記事が気に入ったらサポートをしてみませんか?