【ExcelVBA】QueryTablesでスクレイピング

Yahoo!知恵袋に回答した中から、SeleniumBasicを使わずに、ExcelのVBAだけでWeb上のtableタグの情報をとってくる方法を紹介します。

この方法はWebサイトのタグがTableである必要がありますので、使い道は限定的です。

しかし、SeleniumBasicを使えない(インストールできない)職場環境では、一つの選択肢となるだろうと思いましたので、今回取り上げます。

質問

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11281819081

マクロのスクレイピングについて勉強中です。
一括でサイトのリストを取得する方法をご教示ください。
下記urlは内閣府のサイトで、練習として、祝日一覧をSheetに落としているのですが、このサイトは一覧が二つあるため、このようなコードを書きました。
データの取得は出来るのですが、祝日リストが二つだから良いものの、もっとあったらwith ~ end with を書き足さなければなりません。
ループ処理で読み込んだり出来ないでしょうか?

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11281819081

この方の提示してあったコードはこちらです(適宜インデントを付与してあります)。

'祝日一覧を読み込むシート名
Const SHEET_PUBLIC_HOLIDAY As String = "祝日一覧"
'取得したい表を持つURL(今年の祝日一覧)
Const TARGET_URL As String = "https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html"

Dim sheet As Worksheet
Dim sheetPublicHoliday As Worksheet

With Worksheets(SHEET_PUBLIC_HOLIDAY).QueryTables.Add(Connection:="URL;" + TARGET_URL, _
    Destination:=Range("B2"))
    '列幅を元の表と同じにする
    .AdjustColumnWidth = True
    '書式は設定しない
    .WebFormatting = xlWebFormattingNone
    '指定したテーブルのみ取得する
    .WebSelectionType = xlSpecifiedTables
    '★ 1つ目の表(今年の祝日一覧)を取得する
    .WebTables = 1
    'データ取得を実行する
    .Refresh BackgroundQuery:=False
    '作成される「クエリと接続」を削除
    .Delete
End With

Dim LstRow As Long
LstRow = Worksheets(2).Cells(Rows.Count, "B").End(xlUp).Row

With Worksheets(SHEET_PUBLIC_HOLIDAY).QueryTables.Add(Connection:="URL;" + TARGET_URL, _
    Destination:=Range("B" & LstRow).Offset(1, 0))
    .AdjustColumnWidth = True
    .WebFormatting = xlWebFormattingNone
    .WebSelectionType = xlSpecifiedTables
    '★ 2つ目の表(今年の祝日一覧)を取得する
    .WebTables = 2
    .Refresh BackgroundQuery:=False
    .Delete
End With

回答

私がした回答はこちら↓

.WebTables = 1
となっているところの1を変数にしてしまえばよいと思います。

ご指定のURLを見たところ、Tableタグが6つあったので、次のようなコードにしてみましたところ、画像のような結果となりました。

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11281819081

コードはこちらです。

Option Explicit

Public Sub 祝日取込()

    '祝日一覧を読み込むシート名
    Const SHEET_PUBLIC_HOLIDAY As String = "祝日一覧"
    '取得したい表を持つURL(今年の祝日一覧)
    Const TARGET_URL As String = "https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html"
    
    Dim sheet As Worksheet
    Dim sheetPublicHoliday As Worksheet
     
    Dim LstRow As Long
    LstRow = Worksheets(2).Cells(Rows.Count, "B").End(xlUp).Row
    
    Dim ii As Long
    For ii = 1 To 6

        With Worksheets(SHEET_PUBLIC_HOLIDAY).QueryTables.Add(Connection:="URL;" + TARGET_URL, _
            Destination:=Range("B" & LstRow).Offset(1, 0))
            .AdjustColumnWidth = True
            .WebFormatting = xlWebFormattingNone
            .WebSelectionType = xlSpecifiedTables
            '★ 2つ目の表(今年の祝日一覧)を取得する
            .WebTables = ii
            .Refresh BackgroundQuery:=False
            .Delete
        End With
    Next ii
End Sub

解説

質問者さんが示したURLはこちらです。

https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html

内閣府のサイトで、祝日の一覧があります。

F12で開発者コンソールを開き、タグtableで検索すると、6件ヒットしました。

tableで検索したところ6件ヒットした

とても素直なつくりで、表はtableで作成するという方針のサイトのようです。

tableタグの表をとってくるのに、QueryTablesというメソッドが使えます。

QueryTablesはCSVを高速に読み込むときに使うことが多いのですが、WebのTableをとってくるのにも使えます。

使い方は、質問者さんのコードにある通り、

ワークシートオブジェクト.QueryTables.Add(引数….)

です。

引数は、

Connection:=サイトのURLを文字列で(Variant型)。

Destination:=とってきた情報をセットする先の範囲をRange型で指定

です。F2のオブジェクトブラウザーでみると、三つ目の省略可能な引数にsqlとありますが、WebサイトをConnectionに指定した場合はこの三つ目の引数は使えません(ODBCデータソースを指定したときに使う)。

オブジェクトブラウザーで見たQueryTables.Addの引数。

今回この回答を作ったときはSheet2の仕様が不明だったので、LstRowの値が常に1となり、結果、実行した後の表がすべて横並びになる(右側にあるものがサイトで一番上にあった表)という状態になりました。

この点、質問者さんから追加の情報がありまして、

LstRowですが、B列が抽出した休日の名称列で二つの表を縦に繋げて表示させたくて使ってます。

ということでしたので、Worksheets(2)というのは、”祝日一覧”というシート名のシートと同一のシートを指すようですね。^^;

回答時点ではその辺の仕様が不明だったので、Worksheets(2)と祝日一覧シートとは別物という前提で作ってあります。