【チュートリアル】VBAでIE自動化ツールを作る(その12) 書籍検索ツール開発(実装・詳細情報取得)

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

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

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

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

1.対象者

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

2.開発環境

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

3.タイトルを取得する

詳細ページのタイトルを取得するために、まずは要素を特定するためのセレクタを確認します。本連載(その3)要素の調べ方(その4)要素の取得パターンで解説しています。「SearchBooks」に以下のコードを追加してください。

'詳細ページのセレクタ
Const SELECTOR_DETAIL_TITLE As String = "h1.titleType1"

次に、詳細ページの情報を取得するプロシージャを追加します。タイトルは「IEControl.GetHtmlObjByQuerySelector」プロシージャで取得した要素の「innerText」プロパティで取得します。検索フォーム(テキストボックスの場合)に値を設定するときは「Value」プロパティに設定していましたが、今回は処理で使用する"値"ではなく、表示されている"テキスト"を取得するため、「innerText」プロパティを使用します。

'本詳細情報取得
Private Function GetBookInfo(ByVal objIE As InternetExplorer) As BookInfo
   Dim info As BookInfo
   
   'タイトル取得
   info.Title = IEControl.GetHtmlObjByQuerySelector(objIE, SELECTOR_DETAIL_TITLE).innerText
   
   GetBookInfo = info
End Function

「GetBookInfo」を呼び出すため、「GetBookInfos」に以下の処理を追加してください。For文のページ内の繰り返しの中で本情報配列の要素数を追加します。初回は配列が空になっているため要素が0個の配列を再定義し、2回目以降は「ReDim Preserve」で配列の要素数を増やします(ReDim Preserveは今までに格納した値を残したまま再定義する)。「GetBookInfo」を呼び出し結果を配列の最後尾に格納します。

'書籍情報取得
Public Function GetBookInfos(ByVal Url As String, ByRef info As SearchInfo, ByVal getCount As Integer) As BookInfo()
   '省略
   
   Dim isFirst As Boolean: isFirst = True
   
   '本の詳細情報取得
   Do While True
       '省略
       
       Dim i As Integer
       For i = 1 To elementsCount
           '省略

           '本情報配列追加
           If isFirst Then
               ReDim aryBookInfo(0)
               isFirst = False
           Else
               ReDim Preserve aryBookInfo(UBound(aryBookInfo) + 1)
           End If
           
           '本詳細情報取得
           aryBookInfo(UBound(aryBookInfo)) = GetBookInfo(objIE)
           
           '検索結果のページに戻る
           Call IEControl.PageBack(objIE)
       Next
       
       '「次の◯件」の要素が存在するか確認
       '省略
   Loop
   
   GetBookInfos = aryBookInfo
End Function

4.テーブルの値の取得

著者~定価の項目が以下の画像の通り、テーブル要素として表示されています。この表の場合、左側が項目名で右側が項目に対応する値になっています。HTMLで表すと前者が「th(テーブルのヘッダ)」要素、後者が「td(テーブルのデータ)」要素になっています。

テーブル2

単純に値の要素のセレクタをコピーすると著者の場合「#main > div.detail > div.right > table > tbody > tr:nth-child(1) > td > a」となり、「tr:nth-child(1)」で1行目を指定しています。レイアウトが変わらなければ問題ないですが、表の途中に新しい行が追加されたり行が削除された場合、行数で指定すると取得される値がずれてしまいます。

そこで、今回はヘッダー(左側の項目名)の値を確認してから取得する方法で実装します。

まずは、セレクタを追加します。「SearchBooks」に以下の通りコードを追加してください。途中の「>」は省略可能です。

'詳細ページのセレクタ
Const SELECTOR_DETAIL_TITLE As String = "h1.titleType1"
Const SELECTOR_DETAIL_TABLE_TR As String = "div.right table tbody tr"
Const SELECTOR_DETAIL_TABLE_TH As String = "div.right table tbody tr:nth-child({0}) th"
Const SELECTOR_DETAIL_TABLE_TD As String = "div.right table tbody tr:nth-child({0}) td"

次に、「GetBookInfo」に以下の通り処理を追加してください。「objTableTr 」にテーブルのtr要素を全行取得し、行数分繰り返しth要素、tr要素を取得します。th要素の「innerText」プロパティで表示されているテキストを参照し、「InStr」でth要素のテキストに第2引数に指定した文字列が含まれているかを確認します。それぞれの項目名が含まれる場合、本情報構造対の対応するプロパティに値を設定します。

'本詳細情報取得
Private Function GetBookInfo(ByVal objIE As InternetExplorer) As BookInfo
   '省略

   'テーブルからtr要素を全行取得
   Dim objTableTr As Object
   Set objTableTr = IEControl.GetHtmlObjByQuerySelectorAll(objIE, SELECTOR_DETAIL_TABLE_TR)
   
   Dim i As Integer
   Dim header As String
   Dim objTh As Object
   Dim objTd As Object
   Dim selectorTh As String
   Dim selectorTd As String
   
   For i = 1 To objTableTr.Length
       'i行のセレクタを取得
       selectorTh = Replace(SELECTOR_DETAIL_TABLE_TH, "{0}", i)
       selectorTd = Replace(SELECTOR_DETAIL_TABLE_TD, "{0}", i)
       
       'i行の要素を取得
       Set objTh = IEControl.GetHtmlObjByQuerySelector(objIE, selectorTh)
       Set objTd = IEControl.GetHtmlObjByQuerySelector(objIE, selectorTd)
       
       'ヘッダーのテキストを判定し、項目ごとの情報を取得
       If InStr(objTh.innerText, "著者") > 0 Then
           info.Author = objTd.innerText
       ElseIf InStr(objTh.innerText, "ジャンル") > 0 Then
           info.Genre = objTd.innerText
       ElseIf InStr(objTh.innerText, "書店発売日") > 0 Then
           info.Release = objTd.innerText
       ElseIf InStr(objTh.innerText, "ISBN") > 0 Then
           info.ISBN = objTd.innerText
       ElseIf InStr(objTh.innerText, "判型・ページ数") > 0 Then
           info.BookFormat = objTd.innerText
       ElseIf InStr(objTh.innerText, "定価") > 0 Then
           info.Price = objTd.innerText
       End If
   Next
   
   GetBookInfo = info
End Function

5.URLの取得

最後に、本情報の値として詳細ページのURLを取得します。「GetBookInfo」に以下の通りコードを追加してください。IEオブジェクトの「LocationURL」で現在表示しているページのURLを取得できます。

'本詳細情報取得
Private Function GetBookInfo(ByVal objIE As InternetExplorer) As BookInfo
   '省略
   
   'URL取得
   info.Url = objIE.LocationURL
   
   GetBookInfo = info
End Function

6.取得件数の制御

「インプット」シートに「取得件数」の設定項目があります。これは、検索結果から実行時に取得する件数を制限するために設けています。「Do While」文のループで検索結果からすべての本の情報を取得しますが、設定した「取得件数」に達したら終了する処理を入れる必要があります。

「SearchBooks」の「GetBookInfos」プロシージャを以下のように編集してください。「Do While」文のループでページの繰り返し、「For」文のループで1冊の本の詳細ページに遷移、情報取得、前のページに戻る処理を行っています。最後に取得した情報の配列の件数と引数の取得件数を比較し超えている場合は「Do While」文のループを終了し全体の取得処理を終了します。

'書籍情報取得
Public Function GetBookInfos(ByVal url As String, ByRef info As SearchInfo, ByVal getCount As Integer) As BookInfo()
   '省略
   
   '本の詳細情報取得
   Do While True
       '省略
       
       Dim i As Integer
       For i = 1 To elementsCount
           '省略
           
           '取得件数を確認
           If UBound(aryBookInfo) + 1 >= getCount Then
               '取得件数に達したら終了
               Exit Do
           End If
       Next
       
       '省略
   Loop
   
   GetBookInfos = aryBookInfo
End Function

実行したときに取得件数に達したら未取得の本が残っていたり、次のページがあっても処理が終了することを確認できます。


ここまでで詳細ページの情報を取得する処理ができました。次回は取得した情報をシートに出力する処理を実装します。





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