9月末といえば~♪♪
んんっ?このWebサイト、1ページ10件までしか見れないのか?
じゃあ、スクレイピングでExcelシートに書き出そうか
Webサイトのデータ(検索結果)をExcelシートに書き出すマクロを紹介します。複数ページある場合は、リンクをクリックして最終ページまでクロールします。
題材として、イチロー選手と天海祐希さんでおなじみ「SMBC日興証券」のWebサイトを使います。
※SMBC日興証券の取引アカウントをお持ちでなくてもマクロは使えます!
スクレイピングのイメージ動画です(9月は件数が多いため、8月データ)
画面左側がWebサイトです。画面のチラつきは画面遷移(次ページへ移動)です。
Webサイトをクロール(次ページ遷移)しながらExcelシートに書き出すマクロの使い方
(事前準備)VBEの参照設定
VBAでIEを操作するためには下記2つのライブラリを参照します。
- Microsoft HTML Object Library
- Microsoft Internet Controls
VBE(エディタ)で「メニュー」→「ツール」→「参照設定」を選択します。
マクロを標準モジュールに貼り付け
下記のコードを標準モジュールに貼り付けます。コードの解説は後の項で紹介します!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
Sub main() Application.ScreenUpdating = False Dim objIE As InternetExplorer Set objIE = New InternetExplorer Dim ws As Worksheet Set ws = ThisWorkbook.Sheets("list") Dim shell As Object Set shell = CreateObject("Shell.Application") 'シェルオブジェクト生成 Dim win As Object For Each win In shell.Windows '起動中のウィンドウを順番にチェック If win.Name = "Internet Explorer" Then '起動してるIEを取得 Set objIE = win Exit For End If Next Call WriteShareholderBenefitsData(objIE) MsgBox "終了しました" End Sub Sub waitIE(objIE) Do While objIE.Busy = True Or objIE.readyState < READYSTATE_COMPLETE '読み込み待ち DoEvents Loop End Sub '株主優待の検索結果をシートに書き出す Sub WriteShareholderBenefitsData(objIE As InternetExplorer) Dim ws As Worksheet Set ws = ThisWorkbook.Sheets("list") '前回書き込みデータのクリア Range(ws.Cells(2, 1), ws.Cells(Rows.Count, 5)).ClearContents Dim r As Long, c As Long '書き込み先のセルの行列番号 Dim cnt As Long Dim isLastPageFlag As Boolean isLastPageFlag = False '検索結果の最終ページに達するまで処理を繰り返す Do While isLastPageFlag = False '検索結果のHTMLを読み込む Dim htmlDoc As HTMLDocument Set htmlDoc = objIE.document Dim yuutaiTbl As IHTMLElement Set yuutaiTbl = htmlDoc.getElementById("item01") 'id名=item01 Dim tdTags As IHTMLElementCollection Set tdTags = yuutaiTbl.getElementsByTagName("td") Dim tdTag As IHTMLElement For Each tdTag In tdTags r = cnt \ 5 + 2 '書き込み先の行番号 c = cnt Mod 5 + 1 '書き込み先の列番号 ws.Cells(r, c).Value = tdTag.innerText cnt = cnt + 1 Next tdTag ' class名は同一ページ内で重複可能なので、何番目かを指定する ' ここでは1番目のnextを取得したいので、(0)を指定する Dim nextPageLink As IHTMLElement '「次の10件」のリンク Set nextPageLink = htmlDoc.getElementsByClassName("next")(0) 'class名=next If nextPageLink Is Nothing = False Then 'もし次のページがあるなら nextPageLink.getElementsByTagName("a")(0).Click 'aタグをクリック Call waitIE(objIE) '画面遷移を待機する Else '検索結果の最終ページならフラグを立てる isLastPageFlag = True End If Set htmlDoc = Nothing 'このページのHTML参照をいったん破棄 Loop End Sub |
Internet Explorerを起動して日興証券のページを開く
このマクロは「起動してるInternet Explorerを取得する」仕組みなので、事前に対象のページにアクセスしておく必要があります。
※ブラウザはInternet Explorerを使用します。
Internet ExplorerでSMBC日興証券の「マーケット情報 > 株主優待の検索」にアクセスし、検索条件を指定して検索結果を表示しておきます。
ここでは、権利確定時期=9月 で検索してみます(他の条件でもOKです)
※起動してるウィンドウを順番にチェックして、最初に見つかったInternet Explorerを処理対象として取得する仕組みなので、Internet Explorerの起動は1つのみとしてください。
マクロを実行する
IEを起動して、対象の検索結果画面を表示させた状態で、マクロのmainプロシージャを実行してください。
423件が、約75秒でExcelシートに書き出せました!
オートメーションエラーが発生した場合
下記のエラーが発生した場合は、IEを再起動してみてください。
スクレイピングコードの解説
起動済みのIEを取得する
起動してるウィンドウを順番にチェックして、最初に見つかったInternet Explorerを処理対象として取得します。
シェルオブジェクトのウィンドウは、エクスプローラーやインターネットのブラウザを取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Dim shell As Object Set shell = CreateObject("Shell.Application") 'シェルオブジェクト生成 Dim win As Object For Each win In shell.Windows '起動中のウィンドウを順番にチェック If win.Name = "Internet Explorer" Then '起動してるIEを取得 Set objIE = win Exit For End If Next |
検索結果の最終ページまで処理を繰り返す
このスクレイピングでは、検索結果のページ数、処理を繰り返します。そのため、「最終ページであるか否か」の判定処理が必要です。
変数isLastPageFlagを用意し、Falseの間(=最終ページでない)処理を繰り返します。最終ページか否かの判定は後ほど紹介します。
1 2 3 4 5 6 7 8 9 10 11 |
Dim isLastPageFlag As Boolean isLastPageFlag = False '検索結果の最終ページに達するまで処理を繰り返す Do While isLastPageFlag = False '処理 Loop |
銘柄の検索結果の範囲を取得する
検索結果のHTMLを確認してみます。HTMLの確認はChromeを使いましょう!
検索結果の部分は、id名=item01であることが確認できます。
1 |
<div class="yuutaiBox04" id="item01">検索結果の中身</div> |
HTMLオブジェクトのgetElementByIdメソッドの引数に”item01″を指定して、変数yuutaiTblに格納します。
1 2 3 4 5 |
Dim htmlDoc As HTMLDocument Set htmlDoc = objIE.document Dim yuutaiTbl As IHTMLElement Set yuutaiTbl = htmlDoc.getElementById("item01") 'id名=item01 |
検索結果テーブルのtdタグをすべて取得する
銘柄のテーブルはシンプルな作りになっています。1銘柄5項目なので、5つのtdタグが存在します。
テーブルの1行(1銘柄)のHTMLです。tdタグで挟まれている中身を取得できればよさそうですね。
1 2 3 4 5 6 7 |
<tr> <td class="center">1446</td> <td><a href="javascript:jv_Yuutai_Detail(144600)">キャンディル</a></td> <td class="center">9月末日</td> <td class="right pr30">200</td> <td>金券</td> </tr> |
getElementsByTagNameメソッドの引数にtdタグを指定して、変数tdTagsに格納します。tdタグは複数あるので、tdTagsはコレクションになります。
1 2 |
Dim tdTags As IHTMLElementCollection Set tdTags = yuutaiTbl.getElementsByTagName("td") |
ポイントは、変数yuutaiTblからtdタグを取得していることです。
yuutaiTblを指定せずに、ページ全体のすべてのtdタグにしてしまうと、銘柄の検索結果と関係のないtdタグまで取得してしまうのです。
tdタグを1つずつシートに書き出す
コレクションtdTagsから、tdタグを1つずつ取り出して変数tdTagに格納します。変数名は複数形と単数形にしておくとわかりやすいです。
- tdTags(複数形・コレクション)
- tdTag(単数形)
tdタグで囲まれた中身がinnerTextです。
1 |
<td class="xxx">ここがinnerTextだよ</td> |
tdTagのinnerTextをExcelシートのセルに書き込みます。
1 2 3 4 5 6 7 8 9 10 11 |
Dim tdTag As IHTMLElement For Each tdTag In tdTags r = cnt \ 5 + 2 '書き込み先の行番号 c = cnt Mod 5 + 1 '書き込み先の列番号 ws.Cells(r, c).Value = tdTag.innerText cnt = cnt + 1 Next tdTag |
次のページに遷移する
1ページ10件の書き込みが終わったら、「次の10件」のリンクをクリックして次のページに進みます。
リンクのHTMLを確認しましょう。
1 |
<span class="next"><a href="javascript:jv_PageNext1()">次の10件</a></span> |
class名は”next”です。
HTMLのidとclassの違いに注意しましょう!
- id = 同一ページ内で重複不可
- class = 同一ページ内で重複OK
HTMLオブジェクトのgetElementsByClassNameメソッドの引数に”next”を指定して、変数nextPageLinkに格納します。
getElementsByClassNameメソッドの返り値はコレクションなので、何番目の要素であるか、番号を指定します。ここでは1番目なので(0)を指定します。
1 2 |
Dim nextPageLink As IHTMLElement '「次の10件」のリンク Set nextPageLink = htmlDoc.getElementsByClassName("next")(0) 'class名=next |
(参考)class名がページ全体の何番目であるか確認する方法
HTMLのclass名は、同一ページに複数指定することが可能です。目的のclassが全体の何番目であるか、chromeの検証機能で検索しましょう。
Ctrl+Fで検索ダイアログが登場します。class名を検索します。(検索結果では2 of 3となっていますが、1番目のnextは無関係なので、ここでは目的のリンクが1番目 = 0 となっています)
最終ページであるか否かの判定処理
変数nextPageLink(オブジェクト)の中身をチェックし、もしオブジェクトが空でなければ、つまり次のページがあるなら、クリックします。
検索結果の最終ページにはリンクがありません。
クリックするのは、spanタグの中にあるaタグです。
1 |
<span class="next"><a href="javascript:jv_PageNext1()">次の10件</a></span> |
nextPageLinkオブジェクトの、getElementsByTagNameメソッドの引数に”a”タグを指定して、Clickメソッドを実行します。
(spanタグの中にaタグは1つのみなので、要素番号は1番目 の (0) を指定)
1 2 3 4 5 6 7 8 9 10 |
If nextPageLink Is Nothing = False Then 'もし次のページがあるなら nextPageLink.getElementsByTagName("a")(0).Click 'aタグをクリック Call waitIE(objIE) '画面遷移を待機する Else '検索結果の最終ページならフラグを立てる isLastPageFlag = True End If |
次ページ遷移のリンクがない場合はnextPageLinkが空になります。
nextPageLinkが空の場合、つまり最終ページの場合は、「最終ページであるか否か」の判定に使用する変数isLastPageFlagをTrueにします。
変数isLastPageFlagがTrueになると、ループ処理を抜けます。
まとめ:VBAでIE操作してスクレイピング
日興証券の株主優待銘柄の一覧を題材にして、ページをクロールしながらExcelシートにデータを書き出す方法を紹介しました。
スクレイピングとは、WebサイトのHTMLを収集して、目的のデータを抽出することです。
Webサイトのデータ収集で、コピー貼付けで苦戦している人は、この「スクレイピング」という技術を覚えるとラクですよ。
「権利付き最終日」は「権利確定日」の2営業日前なので、「権利確定日=9月末日」の銘柄は9月26日(木)中に買っておけばOKですね。