適材適所

WindowsやPowerShellやネットワーク、IBMなどのシステム系の話やポイ活など気になったことも載せているブログです。

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

Internet Explorereはサポートが終了します!

マイクロソフトからIEのサポート終了が発表されています。

サポートが終了すると、脆弱性が見つかったときなど適切なアップデートが行われず、危険に晒される可能性があります。

またウェブページによっては正しく表示されなくなる可能性があります。

この記事は残しておきますが、非推奨になる可能性があります。

HTMLとは

この記事は↓の続きで、IEを使ったスクレイピングのやり方を書いていきます。

VBAでウェブスクレイピング_IE操作編_その1 - 適材適所

↑の記事もご参照頂ければ幸いです。

↑の記事ではHTMLはウェブページの設計図のようなものであること、ブラウザはそれを頼りに、ページを表示していることを述べています。

VBAのコードに戻る前に、もう少しHTMLの理解に時間を裂きましょう。

理解のために、簡単なHTMLを見てみます。

 
<html>
    <body>
    Hello World
    </body>
</html>

これをメモ帳などで拡張子を「.html」として、保存するとブラウザで開くことができます。

f:id:shinmai_papa:20190729224918p:plain

HTMLの正体はただのテキストなんです。

HTMLのすごいところは、「タグ」というものを文書の中に埋め込むことで、

その文書が構造を伴って、デザインできるようになるところ!!

このスクレイピングをするときも、htmlの構造とタグを分析することになるので、この辺りはちゃんと理解しておく必要があります。

タグとは

タグとは、htmlの中の色々な要素がどういった役割なのかを示すものです。

例えば、aタグはリンクであることを表し、imgタグは画像であることを表します。

タグは基本的に、<○○>~</○○>という形をとります。

○○がタグ名、内容が~になります。

また、タグにはこれ以外にも「属性」を与えることができます。

例えば、ウェブページにリンクを貼りたいときは、aタグを使いますが、リンク先のアドレスは属性を使って定義します。

 
<a href="sample.html">リンク先</a>

この属性というのもスクレイピングする上ではタグと同様、重要な意味を持ちます。

HTMLにアクセスするときは、タグと、この属性の情報を手掛かりに行います。

簡単ではありますが、スクレイピングにおける重要な概念である、タグと属性について説明してみました。

次からようやくIEを使ったスクレイピングについて触れていきたいと思います。

IEを使ったスクレイピングにおける主要な操作

IEを使ったスクレイピングにおいて、必要となる動きは、

  • 情報を集める
  • リンクをクリックする
  • インプットボックスに入力する

が半分以上な気がします。

この記事では、まず「情報を集める」について詳しく説明します。

情報を集める

情報を集める。これがまさにスクレイピングを行う目的ではないでしょうか。

HTMLの中にある情報を取得する際に知っておくべき属性が二つあります。それは、

  • id属性
  • class属性

の二つです。

それぞれの使い方のイメージはこんな感じです。

 
<h1 id="title"><a href="https://www.tekizai.net/">適材適所</a></h1>
<a class="entry-title-link" href="https://www.tekizai.net/entry/2019/07/25/080000">VBAでウェブスクレイピングをマスターするIE操作編①</a>
<a class="entry-title-link" href="https://www.tekizai.net/entry/2019/07/24/080000">VBAを使ってIBM Notesでメールを送信するファイルを添付する</a>

このブログのトップ画面かソースから抜粋です(この記事執筆当時です)。

1行目のh1タグとは、そのページの一番大きな見出しです。

h1タグにtitleという、意味を与えています。

2、3行目のclassは、aタグにentryという意味を与えています。

機能は似ており、タグに意味を与えます。

id="title"は同じページ上で存在してはいけないのに対し、class=""entry-title-link"はいくつ存在してもいいという違いがあります。

主な使われ方はウェブページのデザインなどです。

例えば、id=titleの部分のフォントサイズを大きくして・・・といった使われ方です。

デザインは一般的にCSS(カスケーディングスタイルシート)というもので定義されます。

スクレイピングで情報を集めるときは、この二つのタグが重要な意味を持ちます。

ここで一旦、VBAのコードに戻ってみましょう。

先述の記事で登場したのはこのようなコードでした。

 
Sub sample()
    Dim ie As InternetExplorer
    Set ie = New InternetExplorer
    ie.Visible = True
    ie.Navigate "https://www.tekizai.net/"
    Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
        DoEvents
    Loop
    Dim htmlDoc As HTMLDocument
    Set htmlDoc = ie.Document
    Set ie = Nothing
End Sub

前提として、「Microsoft Internet Controls」と「Microsoft HTML Object Library」の参照設定が必要なコードです。

何をするコードかというと、インターネットエクスプローラーを開始して、このブログへアクセスし、HTMLを取得するというコードです。

Set htmlDoc = ie.DocumentでHTMLが取得でき、

このHTMLの中身を解析して取得したいタグや属性を探し出します。

解析と聞くと難しく聞こえますが、やってみると案外簡単にできる・・・

場合が多いです。

ウェブページの作られ方次第ですが・・・。

実際のHTMLを見てみる

IEの機能で、実際のHTMLを見ることが出来ます。

右クリックすると出てくるメニューの中に「ソースの表示」がありますので、それをクリックします。

f:id:shinmai_papa:20190729225204p:plain

ソースを見てみると、色々なことがごちゃごちゃ書かれており、訳がわかりません。

でもめげないでください。

今回の目的は、ブログのエントリ一覧を取得してみることです。

とにかくとっかかりが必要です。

試しに、ブログのエントリの文字列を検索してみましょう。

f:id:shinmai_papa:20190729225248p:plain

あ、ありました。

もうちょっと検索してみると、どうやらブログの記事タイトルは、aタグ+entry-title-linkというclass名で囲まれてるっぽい、という予想がつきます。

試してみましょう。

HTMLからclass属性を取得する

特定のclass属性を取得したい場合は、HTMLDocumentオブジェクトのgetElementsByClassNameメソッドを呼び出します。

引数は、取得したいclass名です。

今回の場合は「entry-title-link」です。

 
Sub sample()
    Dim ie As InternetExplorer
    Set ie = New InternetExplorer
    ie.Visible = True
    ie.Navigate "https://www.tekizai.net/"
    Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
        DoEvents
    Loop
    Dim htmlDoc As HTMLDocument
    Set htmlDoc = ie.Document
    '追加
    Debug.Print htmlDoc.getElementsByClassName("entry-title-link")
    Set ie = Nothing
End Sub

これを実行してみると・・・

f:id:shinmai_papa:20190729225330p:plain

エラーですね・・・。

なんででしょう。

getElementsByClassNameメソッド

新たに追加した「getElementsByClassName」が怪しいですね。

この言葉をバラバラにすると、get Elements By Class Name。

get Elements By Class Name

Elementsとなっています。

これ、実は要素を複数取得するものなのです。

そのため、Debug.Printする際に、要素数を指定する必要があります。

要素は0から始まります。とにかくコードを修正してみましょう。

 
Sub sample()
    Dim ie As InternetExplorer
    Set ie = New InternetExplorer
    ie.Visible = True
    ie.Navigate "https://www.tekizai.net/"
    Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
        DoEvents
    Loop
    Dim htmlDoc As HTMLDocument
    Set htmlDoc = ie.Document
    '修正
    Debug.Print htmlDoc.getElementsByClassName("entry-title-link")(0)
    Set ie = Nothing
End Sub

これを実行してみると、イミディエイトウィンドウに

https://www.tekizai.net/entry/2019/07/25/080000

と表示されました(実行するタイミングにより異なるURLが出力されます)。

なんか違う。

これじゃない感がすごいです。

欲しいのは、URLじゃなくて、記事タイトルなんです。

もうちょっと「getElementsByClassName」について調べてみます。

困ったときは、VBEのオブジェクトブラウザーです。

VBEの画面でF2を押すと出てきます。検索窓にいれて、検索してみます。

f:id:shinmai_papa:20190729225508p:plain

おぉ、どうやらgetElementsByClassNameの戻り値はIHTMLElementCollectionみたいですね。

うん、なんだ、このIHTMLElementCollectionとは。

これも調べてみます。
f:id:shinmai_papa:20190729225531p:plain

MSHTMLの中にあるClassみたいですね。

まだわかりません。もうちょっと詳しく見てみます。

f:id:shinmai_papa:20190729225601p:plain

緑のアイコンの左上に緑の○があるメンバー、

これがメソッドの呼び出しを省略したときに呼ばれるメソッドです。

オブジェクトブラウザによると、

getElementsByClassNameメソッドを呼び出したときによばれていたのは「item」というプロパティみたいですね。

ではitem(0)はどんなデータ型なのか、下記のコードで見てみましょう。

 
Sub sample()
    Dim ie As InternetExplorer: Set ie = New InternetExplorer
    ie.Visible = True
    ie.Navigate "https://www.tekizai.net/"
    Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
        DoEvents
    Loop
    Dim htmlDoc As HTMLDocument: Set htmlDoc = ie.Document
    '修正
    Debug.Print TypeName(htmlDoc.getElementsByClassName("entry-title-link").Item(0))
    Set ie = Nothing
End Sub

実行すると、HTMLAnchorElementと出てきました。

HTMLAnchorElementについて、オブジェクトブラウザーで検索してみるとデフォルトプロパティは「href」つまりリンク先のURLのようです。

f:id:shinmai_papa:20190729225637p:plain

既定のプロパティはhrefなので、https://www.tekizai.net/entry/2019/07/25/080000が返ってきたのですね。

ここで注意が必要なのは、今回取得したキー「entry-title-link」はたまたまHTMLAnchorElementでしたが、どんな要素が返ってくるかケースバイケースで変わります。

タイトルを取得する

ではタイトルを取得したい場合はどのようにすればよいでしょうか。

f:id:shinmai_papa:20190729225714p:plain

改めてHTMLを見てみるとタイトルはタグに囲まれていますね。

○○という形です。

タグに囲まれた○○を取得したいのです。

その場合、HtmlAnchorElementのinnerTextというメソッドで取得することができます。

これはよくあるパターンです。

では実際に取得してみましょう。

 
Sub sample()
    Dim ie As InternetExplorer: Set ie = New InternetExplorer
    ie.Visible = True
    ie.Navigate "https://www.tekizai.net/"
    Do While (ie.Busy Or ie.ReadyState <> READYSTATE_COMPLETE)
        DoEvents
    Loop
    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
<.div>

VBAでウェブスクレイピングをマスターするIE操作編その1
欲しいものが取得できました。やっと一つのタイトルを取得できました。

うーん、だいぶ長くなってしまったので、いったんここまでにして、

↓記事では、すべてのページを遷移してすべての記事タイトルを取得するやり方について解説しいますのでご参照頂ければ幸いです。

www.tekizai.net

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