私は出版社に勤める、出版人の端くれです。
なので当然、Amazonにおける書籍の在庫ステータスは気になります。Amazonでは、いつのまにか在庫なしになっている場合もありますので、少しだけでも在庫ステータスを感知して置く必要があります。
そこで今回、VBAのIE制御を用いて、Amazonの在庫ステータスを取得するツールを作成しましたので、紹介します。
どんな処理を行う?
初めにどんな処理を行っていくか、簡単に解説します。
流れとしては、
- Amazonでシートから取得したISBNを検索
- 結果の一番最初にあるリンク(書籍詳細ページへのリンク)に飛ぶ
- 書籍詳細ページから在庫ステータスを取得
- シートに書き込む
となります。
最終的には以下のような画面になります。
はじめに
はじめに、以下のようなExcelシートを作成します。
1列目:書名、2列目:ISBN、3列目:在庫ステータスです。
1列目と2列目を入力しておいてください。
コードの紹介
以下が全コードになります。
Option Explicit #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr) #Else Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long) #End If Sub getInventoryStatus() Dim objIE As InternetExplorer Dim url As String Dim htmlDoc As HTMLDocument url = "https://www.amazon.co.jp/" '対象となるURL 'IEを起動し表示させる Set objIE = CreateObject("Internetexplorer.Application") objIE.Visible = True Dim i As Long, lastRow As Long lastRow = Cells(Rows.Count, 1).End(xlUp).Row 'ISBNの数だけループさせる For i = 2 To lastRow 'Amazonのページを表示 objIE.navigate (url) Call IEの読み込み待ち(objIE) Set htmlDoc = objIE.document 'Amazonの検索窓を取得→ISBN(cells(i,2))を入力→検索ボタンをクリック Dim serchForm As HTMLInputTextElement Set serchForm = htmlDoc.getElementById("twotabsearchtextbox") serchForm.Value = Cells(i, 2) Dim btnSerch As HTMLFormElement Set btnSerch = htmlDoc.getElementsByClassName("nav-input")(0) btnSerch.Click '検索結果の画面を取得し、最初のAnchor(リンク)を取得し、そのリンクに飛ぶ Call IEの読み込み待ち(objIE) Set htmlDoc = objIE.document Dim a As HTMLAnchorElement Set a = htmlDoc.getElementsByClassName("a-link-normal a-text-normal")(0) objIE.navigate (a.href) '在庫状況のテキストをcells(i,3)に書き込む Call IEの読み込み待ち(objIE) Set htmlDoc = objIE.document Cells(i, 3).Value = htmlDoc.getElementById("availability").outerText Next End Sub Sub IEの読み込み待ち(objIE) Dim timeOut As Date '完全にページが表示されるまで待機する timeOut = Now + TimeSerial(0, 0, 20) Do While objIE.Busy = True Or objIE.readyState <> 4 DoEvents Sleep 1 If Now > timeOut Then objIE.Refresh timeOut = Now + TimeSerial(0, 0, 20) End If Loop timeOut = Now + TimeSerial(0, 0, 20) Do While objIE.document.readyState <> "complete" DoEvents Sleep 1 If Now > timeOut Then objIE.Refresh timeOut = Now + TimeSerial(0, 0, 20) End If Loop End Sub
コードの解説
では、コードを解説していきます。途中で、若干のHTMLに関する知識が必要となりますが、スクレイピングに必要な程度の簡単なものですので、HTMLの苦手な方も頑張って取り組んでみてください。
Option Explicit #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr) #Else Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long) #End If
この部分は呪文のようなコードです。意味を理解せずとも、とりあえず記述しておきましょう。一応、簡単に意味を説明しておきます。VBAでIEを制御する場合、IEがWebページにアクセスしたら、そのページが読み込まれるまで待つ処理が必要になります。その処理はWindowsの機能であるSleepを用いる必要があるため、それを使用するための処理が記述しています。
Sub getInventoryStatus() Dim objIE As InternetExplorer Dim url As String Dim htmlDoc As HTMLDocument url = "https://www.amazon.co.jp/" '対象となるURL
この部分では、変数の宣言を行い、スクレイピングの対象となるAmazonのページのURLを指定しています。
'IEを起動し表示させる Set objIE = CreateObject("Internetexplorer.Application") objIE.Visible = True Dim i As Long, lastRow As Long lastRow = Cells(Rows.Count, 1).End(xlUp).Row
この部分では、IEオブジェクトを作成し、objIEに格納し、それを表示させています(visible=True)。それに加えて、カウンタ変数と最終行を示す変数を宣言しています。
For i = 2 To lastRow 'Amazonのページを表示 objIE.navigate (url) Call IEの読み込み待ち(objIE) Set htmlDoc = objIE.document
ここから、シートに入力されている書籍の数だけ処理をループさせていきます。まずはAmazonの最初のページを表示させています。IEにページを読み込ませるまで処理を中断する必要があるので、IEの読み込み待ち というサブプロシージャを用いています。この処理については後述します。その後、その読み込まれたページをhtmlDocという変数にセットしています。このhtmlDocから各種エレメントを取得していきます。
'Amazonの検索窓を取得→ISBN(cells(i,2))を入力→検索ボタンをクリック Dim serchForm As HTMLInputTextElement Set serchForm = htmlDoc.getElementById("twotabsearchtextbox") serchForm.Value = Cells(i, 2) Dim btnSerch As HTMLFormElement Set btnSerch = htmlDoc.getElementsByClassName("nav-input")(0) btnSerch.Click
この部分では、Amazonのページの上位置する検索窓のエレメントを取得し、そこにISBNを入力しています。そして、検索を行うボタンのエレメントを取得し、そのボタンをクリックするという処理を行っています。これでISBNで検索することができます。
エレメントを取得する際には、getElementByIDとgetElementByClassNameを用いています。これは、HTMLの知識のない方には難しい部分になります。
Amazonの検索窓には、以下の画像のようなidとclassが設定されています。オレンジの文字がid、青の文字がclassです。idとclassというのは、HTML上における目印のようなものと捉えてくださって構いません。
ちなみに、どのようなidやclassが指定されているかは、Chromeを使用されている方だと、Chromeメニューの、「その他のツール」→「デベロッパーツール」を使用することで確認できます。
htmlDocからgetElementByIDとgetElementByClassNameによって、これらidやclassが設定されたエレメントを探しだし、取得します。今回の画像だと、id="twotabsearchtextbox"と指定されていますので、それをhtmlDocからgetElementByIdで指定し、取得しています。また、id名の冒頭の#およびclass名の冒頭.(ドット)は、それぞれがid名・class名を表すための接頭語のようなものです。なので、getElemetByIdなどでidを設定する際には、冒頭の#は必要ありません。
ちなみに、HTMLでは、基本的に、1つのページには複数のidを記述することができません。つまり、twotabsearchtextboxというidが設定されたエレメントは、このページにはこれしかありません。それに対して、class名は、同じものを1つのページに複数設定できてしまいます。なので、取得したいエレメントにidが設定されていた場合は、それを指定するようにしましょう。
そして、検索窓のエレメントが取得できましたので、そのvalueプロパティに、ISBNを設定します。これで、検索窓にISBNが入力された状態になります。そして、検索を行うためのボタンのエレメントを取得し、メソッド:クリックを実行し、検索結果のページを表示させます。この検索を行うためのボタンには、idが設定されておらず、class="nav-input"のみが設定されています。
この場合、getElementByClassNameを用います。しかし、先でも説明しましたが、id名はユニークなものであるのに対して、class名は同じものが複数存在します。なので、class名を指定しただけでは目的のエレメントを取得できません。
そのため、指定したいエレメントが、そのページ上で何番目に位置するかを指定する必要があります。HTMLソースを確認し、そのclass名を持つものの何番目に該当するかを確認してください。
今回の場合はclass="nav-input"をの中では最初に位置するものだったので、getElementsByClassName("nav-input")(0)で要素を取得しています。
こういった、DOMと呼ばれる部分の操作は、HTMLを知らない方には難解です。これについては、以下のサイトに詳しいので、参照してください。
'検索結果の画面を取得し、最初のAnchor(リンク)を取得し、そのリンクに飛ぶ Call IEの読み込み待ち(objIE) Set htmlDoc = objIE.document Dim a As HTMLAnchorElement Set a = htmlDoc.getElementsByClassName("a-link-normal a-text-normal")(0) objIE.navigate (a.href)
検索結果が表示されると、IEの読み込み待ちを行い、htmlドキュメントを再度取得します。この処理は、ページが遷移する度に必要となるものですので必ず行ってください。ISBNで検索すると、かならず目的の書籍が最初に表示されます(ISBNはユニークな値です)。検索結果の最初のエレメント(getElementsByClassName("s-access-detail-page")(0))を取得し、そのエレメントのプロパティ:href(書籍詳細ページのリンク)からリンクを取得し、そこに飛びます。
'在庫状況のテキストをcells(i,3)に書き込む Call IEの読み込み待ち(objIE) Set htmlDoc = objIE.document Cells(i, 3).Value = htmlDoc.getElementById("availability").outerText Next
そして、cells(i,3)に在庫ステータスを示すエレメントのテキストを入力。次の書籍にループします。これで処理は終了です。
Sub IEの読み込み待ち(objIE) Dim timeOut As Date '完全にページが表示されるまで待機する timeOut = Now + TimeSerial(0, 0, 20) Do While objIE.Busy = True Or objIE.readyState &lt;&gt; 4 DoEvents Sleep 1 If Now &gt; timeOut Then objIE.Refresh timeOut = Now + TimeSerial(0, 0, 20) End If Loop timeOut = Now + TimeSerial(0, 0, 20) Do While objIE.document.readyState <> "complete" DoEvents Sleep 1 If Now > timeOut Then objIE.Refresh timeOut = Now + TimeSerial(0, 0, 20) End If Loop End Sub
このプロシージャに関しては、以下のサイトから転載しました。そちらも参照してください。
https://www.vba-ie.net/code/ieview2.html
まとめ
今回は、Amazonの在庫ステータスをVBAで取得する方法を紹介しました。これを改変すると、ランキング情報などを取得することもできますし、他のサイトのスクレイピングもできるようになります。ぜひ参考にしてください。