Fippiyのプログラム学習内容アウトプットBlog

日々の学習内容をアウトプットして振り返りを実施する。

VBAプログラム開発、スクレイピング・ページネーション対応する【1】URLを取得する

前回までに、書籍情報一覧表示から、本の情報を取得してExcelのワークシートへデータを取得することが出来るようにしました。

 

しかし、取得していたのは最初のページのみ。

Webの一覧情報ページは件数が増えてくると複数ページに跨がった表示する仕様となっている為、1ページ目のみの情報しか取得できていませんでした。

そこで、複数ページに対応したVBAスクレイピングコードにしていきます。

まずは書籍情報取得はせずに、ページネーション情報からURLを取得します。 

今回の目的

ページネーションからURLを取得する 

なぜやるか

次ページへのリンク情報がページネーションにあるので、必要なデータを取得できるようにするため

やりたいこと

  • ページネーション情報を取得する
  • 取得情報からURLだけを抜き出す
  • 結果をワークシートへ表示し、情報が取得できていることを確認する

やったこと

  • 現状を確認する
  • どうやって次のページを取得するか検討する
  • ページネーションデータを取得する
  • ページネーションデータからリンク情報を取得する
  • ページネーションがない場合を考慮する

実施内容

現状把握する

最初のページだけを取得していた

スクレイピングを実施するURLを指定して、書籍一覧を取得していました。しかし、表示されていたサイトは一覧表示の「最初のページ」だったので、全てのデータがとれているとは限りませんでした。

f:id:Fippiy:20190613101833j:plain

最初のページ

1ページに表示されるタイトルの上限は20件としているので、それ以上のタイトルがある場合は次のページが作成され、そこに表示されています。

複数ページがある場合は、ページが表示されており、どのページが表示されているかが確認できるようになっています。

また、リンクが貼られており、ページ移動ができるようになっています。

 

# 2ページ目のリンクをクリック時

f:id:Fippiy:20190613101954j:plain

2ページ目

2ページ目に遷移した状態です。こちらに1ページ目に治まらなかった情報が表示されます。

今までは、指定したURLに対してのみスクレイピングしていました。

指定したURLとは、書籍一覧ページ。詳しく言うと、書籍一覧の1ページ目です。2ページ目以降は指定していないので、データ取得の対象にはなっていませんでした。

つまり、1ページ目の上限である20件をこえたものは、URLが異なる為、ページ毎に対応するURLを指定して開く必要があります。

 

 

ページネーション対応する

ページネーション表示を確認する

次のページに移動するには、ページネーションに作成されるリンクをクリックすればできそうです。

f:id:Fippiy:20190613103326j:plain

3ページ目あり

3ページ目までが存在する場合のページネーション表示です。1ページ目が青くなっているので、1ページ目が選択されて表示されている状態です。

 

リンク情報として何があるか 

現在のページと「前のページ、次のページ」がない場合はリンクは生成されません

先程の表示では、1ページ目を表示しているので同じページである1ページ目は遷移する必要がないので、リンクは生成されません。また、最初のページであるため、前のページはないので同様にリンクは生成されません

残る情報は2ページ目、3ページ目、次のページです。この3つに対してリンク情報ありで表示されています。

 

1ページ目のデータ収集後には次の2ページ目に遷移し、続きのデータが取得できる…を次のページがなくなるまで繰り返し行いたいので、「次のページ」のリンクを取得できる構造とすれば、リンク情報が複数あっても、必ず次のページへ移動できそうです。

 

ページネーションからリンク取得する

取得コードのベースを用意する

書籍情報取得のVBAはいったん置いておき、次ページへのリンク取得のみのコードを作成していきます。

# スクレイピングベースコード

Sub pagecheck()
 Dim objIE As InternetExplorer 'IEオブジェクトを準備

 '新しいIEオブジェクトを作成してセット
 Set objIE = CreateObject("Internetexplorer.Application")
 objIE.Visible = True 'IEを表示

'navigateURL
 Dim OpenPage As String
 OpenPage = "https://protected-fortress-61913.herokuapp.com/book"
'最初に開くURL

 Dim i As Integer
 i = 1

 objIE.navigate OpenPage
'IEでURLを開く

 Call WaitResponse(objIE)
'読み込み待ち

 Dim htmlDoc As HTMLDocument
'HTMLドキュメントオブジェクトを準備
 Set htmlDoc = objIE.document
'objIEで読み込まれているHTMLドキュメントをセット

 '作業ワークシート指定
 Dim SWSheet As Worksheet
'ScrapingWorksheet
 Set SWSheet = ThisWorkbook.Worksheets("テスト")
'テスト用ワークシートに出力

  'ページネーションURL取得処理をここに追加


 objIE.Quit 'objIEを終了させる
 MsgBox "データ取得が完了しました。"

End Sub

書籍詳細情報を取得するのに利用したコードをそのまま利用します。スクレイピングすることには変わりないので、当然といわれればそうなのですが…。

ページ遷移することを考えて、URLを格納する変数を用意しています。 

 

ページネーションURLを取得する 

まず実際にページネーションからリンク情報を取得してワークシートへ結果を表示します。

ページネーションのHTMLから何を取得すればいいかを確認します。

# HTML(最初のページ)

<ul class="pagination" role="navigation">
 <li class="page-item disabled" aria-disabled="true" aria-label="« 前へ">
  <span class="page-link" aria-hidden="true">‹</span>
 </li>
 <li class="page-item active" aria-current="page">

  <span class="page-link">1</span>

 </li>
 <li class="page-item">

  <a class="page-link" href="~/book?page=2">2</a>

 </li>
 <li class="page-item">

  <a class="page-link" href="~/book?page=3">3</a>

 </li>
 <li class="page-item">
  <a class="page-link" href="~/book?page=2" rel="next" aria-label="次へ »">›</a>
 </li>
</ul>

ページネーション部分のHTMLです。この情報全体を取得して、さらにこの中からaタグのURLを取得すれば、ページネーションの全URLが抜き出せそうです。 

ページネーション全体に対して"pagination"とクラス名が付与されていますので、これを利用して情報を取得します。

 

# ページネーション取得(先程の追加部分へ記述)

Dim Pagination As HTMLUListElement 'ページネーションデータ
Dim PagiLink As HTMLAnchorElement '次ページリンク
 
'ページネーションクラス名取得
Set Pagination = htmlDoc.getElementsByClassName("pagination")(0)
 
For Each PagiLink In Pagination.getElementsByTagName("a")
 Cells(i, 2).Value = PagiLink.outerHTML
 i = i + 1
Next PagiLink

Pagination変数にクラス名"pagination"としてページネーションエリアのHTMLを格納しました。

("pagination")(0)としているのは、ページネーションを2つ表示しているためです。Webページ作成の際に、ページ上部でも下部でもページ移動できるようにするために2つ準備したためです。

今回は、次ページへの情報が取得できればいいので、最初に表示されるページネーションからデータ取得するようにしました。

 

そして、取得したページネーションHTMLからaタグを順番に取り出してワークシートへ出力しています。

f:id:Fippiy:20190613111815j:plain

URL取得

1ページ目となる最初のページに表示されていたページネーションからaタグのURLを取得しました。

※ページ欄とNext欄は後ほど使います。この記事では扱いません。

 

「2ページ目」「3ページ目」「次のページ=2ページ目」の情報が取得できています。

最初のページでは、「前のページ」と「現在のページ=1ページ目」はリンクが生成されていませんので、結果にもありません。

これで、必要なURLが取得できました。

 

ページネーションがない場合を判定する

次ページへのリンク情報を取得できましたが、ここでページネーションがない場合を考慮しておく必要があります。

1ページに20件まで表示という仕様なので、10件など、2ページ目がそもそも必要ない場合は、ページネーションが表示されません。

f:id:Fippiy:20190613113800j:plain

ページネーションなし

所有書籍のページですが、20件以下のデータ時はページネーション自体が表示されていません。

 

この状態VBAを実行するとページネーション取得クラスである"pagination"が有りませんので、そこでエラー表示になります。

f:id:Fippiy:20190613114050j:plain

エラー表示

f:id:Fippiy:20190613114104j:plain

停止した場所

何も入っていない変数に対して要素を取り出そうとしているのでプログラムとしては値が設定されていないという判断をしているためです。

  

そこで、対象クラス情報(paginationクラス情報)に対してデータが取得できているかどうかの判定をするコードを追加します。

判定作成にあたってはこちらを参考にさせて頂きました、ありがとうございます。

www.relief.jp

 

これを元に判定文を追記しました。

# ページネーション取得

Dim Pagination As HTMLUListElement 'ページネーションデータ
Dim PagiLink As HTMLAnchorElement '次ページリンク
 
'ページネーションクラス名取得
Set Pagination = htmlDoc.getElementsByClassName("pagination")(0)
 
If Pagination Is Nothing Then
 'ページネーション未取得時
 Cells(i, 2).Value = "ページネーションなし"
Else
 
 For Each PagiLink In Pagination.getElementsByTagName("a")
  Cells(i, 2).Value = PagiLink.outerHTML
  i = i + 1
 Next PagiLink
 
End If

Pagination変数にはgetElementsByClassName("pagination")(0)として"pagination"というクラス名の情報をいれていました。

これに対して、データが入っているかを判定する処理を追加しました。

何も入っていないときは”ページネーションなし”という文字を出力させ、情報がある場合は既存のaタグ情報を取り出す設定としました。

f:id:Fippiy:20190613114840j:plain

HTML上にpaginationがない

ページネーションがない状態でスクレイピングすると、このようになりました。

これで、ページネーションの状態にかかわらず、エラー表示することはなくなりました。

 

以上でページネーション有無を判定し、ある場合はURLを取得することができました。

 

次回は、取得URLから「次のページ」を判定して、次のページの情報を取得します。