適材適所

パソコン作業の自動化・効率化のための情報を発信するブログ(VBA,PowerShellなど)

VBAでウェブスクレイピング_IE操作編_その3

前回は、記事タイトルをひとつ取得するところまで実装できたのでした。

今回はページを遷移して、すべての記事タイトルを取得することが目的です。

前回のコードはこんな感じでした(ieの処理待ち部分を関数に外出ししました)。

Sub sample()
   Dim ie As InternetExplorer: Set ie = New InternetExplorer
   ie.Visible = True
   ie.Navigate "https://www.tekizai.net/"
   waite ie 

   Dim htmlDoc As HTMLDocument: Set htmlDoc = ie.Document
   Dim htmlAnc As HTMLAnchorElement
   Set htmlAnc = htmlDoc.getElementsByClassName("entry-title-link").Item(0)

   Debug.Print htmlAnc.innerText
   Set ie = Nothing
End Sub

Function waite(ie As InternetExplorer) As Boolean
   Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
      DoEvents
   Loop
   waite = True
End Function

htmlDoc.getElementsByClassName("entry-title-link").Item(0) を HTMLAnchorElementに割り当てることで、innerTextメソッドを呼び出したのでした。

ここまでできていれば、あとは次ページにアクセスして次ページがなくなるまで同じことを繰り返すだけです。

次ページをクリックする

「次のページ」のリンクを見てみます。

f:id:shinmai_papa:20190801111739p:plain このリンクのHTMLソースを見てます。 該当箇所の抜粋です。

<div class="pager autopagerize_insert_before">
<span class="pager-next">
<a href="https://www.tekizai.net/?page=1563706375" rel="next">次のページ</a>
</span>
</div>

次のページは、aタグで、innnerTextが「次のページ」になっています。残念ながらclass属性はありません。getElementsByClassNameメソッドが使えない・・・。

今度は別の方法でaタグを取得してみましょう。

classやidがない場合のタグの取得方法

classやidがない場合は、getElementsByTagNameというメソッドを使って要素を取得することができます。 innerTextをイミディエイトウィンドウに出力してみます。

Sub sample()
   Dim ie As InternetExplorer: Set ie = New InternetExplorer
   ie.Visible = True
   ie.Navigate "https://www.tekizai.net/"
   waite ie
   Dim htmlDoc As HTMLDocument: Set htmlDoc = ie.Document
   Dim htmlAnc As HTMLAnchorElement: Set htmlAnc = htmlDoc.getElementsByTagName("a").Item(0)
   Debug.Print htmlAnc.innerText

End Sub

Function waite(ie As InternetExplorer) As Boolean
   Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
      DoEvents
   Loop
   waite = True
End Function

「スマートフォン用の表示で見る」が出力されました。 HTMLソースにはあるけど、表示していないやつです。わかりづらいやつが例で出てきてしまいましたが、まぁしょうがない。

こんな調子で、aタグを取得することができました。

次は、このgetElementsByTagNameで取得できた要素を逐一調べて、innerTextが「次のページ」に一致したものがゲットしたいaタグです。

全ての要素にアクセスする

全ての要素にアクセスするためには、For each でアクセスするのが簡単です。

こんな感じになります。

Sub sample()
   Dim ie As InternetExplorer: Set ie = New InternetExplorer
   ie.Visible = True
   ie.Navigate "https://www.tekizai.net/"

    waite ie

   Dim htmlDoc As HTMLDocument: Set htmlDoc = ie.Document
   Dim htmlAnc As HTMLAnchorElement
   For Each htmlAnc In htmlDoc.getElementsByTagName("a")
      If htmlAnc.innerText = "次のページ" Then
         htmlAnc.Click
      End If
   Next htmlAnc
End Sub

Function waite(ie As InternetExplorer) As Boolean
   Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
      DoEvents
   Loop
   waite = True
End Function

勢い余ってクリックまでしてしまいました。実際に動かしてみると、クリックされ、次ページに遷移しました。

クリックする場合はHTMLAnchorElementのClickメソッドを呼び出します。ちなみにHTMLAnchorElement要素以外のHTM要素ならどれでもClickメソッドを呼び出すことができます。

これで任意のaタグを取得することができました。また、次ページまで遷移することができたので、これをひたすら繰り返して記事のタイトルをすべて取得する目途が立ちました。

今度は、クリックについてもう少し理解を深めるために、単純に最後のページまで遷移するプログラムを作ってみましょう。

最初から最後のページまで遷移する

最初から最後のページまで遷移する処理の流れを考えてみます。 こんな流れでしょうか。

f:id:shinmai_papa:20190801111742p:plain

Sub sample()
   Dim ie As InternetExplorer: Set ie = New InternetExplorer
   ie.Visible = True
   'トップページにアクセスする
   ie.Navigate "https://www.tekizai.net/"
   waite ie
   '次ページのaタグがあるかどうかの判定結果
   Dim hasAtag As Boolean
   Dim htmlAnc As HTMLAnchorElement

   Do While True
      Dim htmlDoc As HTMLDocument: Set htmlDoc = ie.Document
      hasAtag = False
      ’次ページのaタグがあるか?
      For Each htmlAnc In htmlDoc.getElementsByTagName("a")
         If htmlAnc.innerText = "次のページ" Then
            ’aタグをクリック
            htmlAnc.Click
            'aタグがあったのでtrueに
            hasAtag = True
            Exit For
       End If
   Next htmlAnc

    ’aタグがない場合、処理終了
    If hasAtag = False Then
       Exit Do
      End If
      waite ie
   Loop
Set ie = Nothing
End Sub

Function waite(ie As InternetExplorer) As Boolean
   Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
      DoEvents
   Loop
   waite = True
End Function

Do whileでtrueになるまでループします。つまり無限ループです。Exit Doが呼び出されるまでループします。

変数hasAtagがfalse、つまり該当のaタグが見つからなかった場合に抜けだします。

これを実行すると、矢継ぎ早にieの画面が最終ページまで遷移します。

あとは、この途中で各ページの記事タイトルを取得するだけです。

先ほどのフローに一つだけ処理が追加されます。こんなイメージでしょうか。

f:id:shinmai_papa:20190801111746p:plain

記事のタイトルを取得する処理が加わりました。 記事のタイトルはイミディエイトウィンドウに出力するようにしましょう。

コード的には、コメントの箇所に新たに処理を追加します。

Sub sample()

   Dim ie As InternetExplorer: Set ie = New InternetExplorer
   ie.Visible = True
   'トップページにアクセスする
   ie.Navigate "https://www.tekizai.net/"
   waite ie
   '次ページのaタグがあるかどうかの判定結果
   Dim hasAtag As Boolean
   Dim htmlAnc As HTMLAnchorElement
   Do While True
      Dim htmlDoc As HTMLDocument: Set htmlDoc = ie.Document
      'ここで全ての記事タイトルを取得する処理
      getTitle htmlDoc
      hasAtag = False
      '次ページのaタグがあるか?
      For Each htmlAnc In htmlDoc.getElementsByTagName("a")
         If htmlAnc.innerText = "次のページ" Then
            'aタグをクリック
            htmlAnc.Click
            'aタグがあったのでtrueに
            hasAtag = True
            Exit For
         End If
      Next htmlAnc

      'aタグがない場合、処理終了
      If hasAtag = False Then
         Exit Do
      End If
      waite ie
   Loop
   Set ie = Nothing
End Sub

Function waite(ie As InternetExplorer) As Boolean
   Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
      DoEvents
   Loop
   waite = True
End Function

’全ての記事タイトルを取得する処理
Sub getTitle(htmlDoc As HTMLDocument)
   Dim htmlAnc As HTMLAnchorElement
   For Each htmlAnc In htmlDoc.getElementsByClassName("entry-title-link")
      Debug.Print htmlAnc.innerText
   Next htmlAnc
End Sub

これまでの解析から、次ページはClass="entry-title-link"で定義されていることがわかっています。

そのため、先ほどのaタグと同じ要領でfor eachですべての要素にアクセスし、イミディエイトウィンドウに出力します。

実行してみると、全ての記事タイトルがイミディエイトウィンドウに出力されました。

ただ記事タイトルを取得するだけでもなんだか長くなってしまいました。

スクレイピングは結構地道なものです・・・。

ということで、今回はここまでにします。

次回で最後です。インプットボックスへの書き込みについてを見ていきたいと思います。

お読みいただき、ありがとうございました。

www.tekizai.net