【HTMLデータ構造化抽出】MSXML2.DOMDocumentとXPathを活用したWebスクレイピングデータ抽出テクニック
開発現場において、Web上のデータを自動収集するタスクに直面した時、あなたはいまだに `InternetExplorer.Application` を起動していないだろうか?
「画面が見えない状態で動かしたいから」「Headless Chromeのセットアップが面倒だから」という理由で、不安定で重いIEオートメーションや、巨大なブラウザ制御ライブラリを導入するのは、業務自動化エンジニアの恥だ。
特にVBScriptを扱う環境(レガシーなWindows Serverやクライアント端末)において、リソースを極限まで削ぎ落とし、かつ極めて高速に動作させるための正解は一つしかない。`MSXML2.ServerXMLHTTP`(または `XMLHTTP`)による非同期レスポンスの取得と、`MSXML2.DOMDocument.6.0` によるXPathベースのHTML構造化抽出である。
今回は、GUIレスで爆速かつ堅牢にWebからデータを抜き出す、プロフェッショナルなスクレイピングアーキテクチャを伝授する。
—
なぜIEや粗雑な文字列置換スクレイピングは現場で破綻するのか?
多くの初学者や古いコードを書くプログラマは、HTMLの解析に `InStr` 関数や `Mid` 関数、あるいは正規表現(RegEx)を使いたがる。しかし、HTMLは「正規表現でパースしてはならない」というプログラミングの鉄則がある通り、タグのネスト、属性の順序変更、閉じタグの省略など、揺らぎの多いテキストに対して力技で挑むと、サイトのわずかなレイアウト変更で簡単に破綻する。
また、IEオートメーションは以下のような致命的な実務リスクを抱えている。
- プロセスがメモリ上に残存し、ゾンビプロセスと化す。
- 現代のモダンなセキュリティポリシーやTLSバージョン(TLS 1.2/1.3)のハンドシェイクでコケる。
- そもそも表示速度が遅く、並列処理の概念がない。
これらをすべて解決するのが、DOMツリーの構築とXPath(XML Path Language)によるクエリ指定だ。
—
アーキテクチャの全体像と設計指針
今回のスクレイピングエンジンは、以下の3ステップで完全にカプセル化する。
1. HTTPリクエストの送信とエンコーディングの制御
`MSXML2.ServerXMLHTTP` を使用し、タイムアウトやプロキシを考慮した堅牢な通信を行う。
2. DOMDocumentへのパース
取得したHTML文字列をXML/HTMLパーサーに流し込み、メモリ上にDOMツリーを展開する。
3. XPathによるピンポイント抽出
CSSセレクタよりも強力なXPath表現を使い、必要なノード群を一網打尽にする。
ここで一つ重要な注意点がある。世の中のWebページの多くは厳密なXMLではない(HTML5)。そのままではDOMDocumentが構文エラーを起こすため、適切なパースモードの指定とエラーハンドリングが生命線となる。
—
プロダクションコード:堅牢なHTMLスクレイピングスクリプト
以下のコードは、実務の現場でそのままコピペして利用できる、エラーハンドリングと文字コード対策を網羅したVBScriptの完成形である。今回は例として、適当なニュースサイトや価格表のテーブル構造を想定し、XPathでデータを抽出しCSVに出力するシナリオとする。
‘ ==============================================================================
‘ Script Name : HtmlScraperWithXPath.vbs
‘ Description : MSXML2とXPathを用いた高速・軽量HTMLデータ構造化抽出エンジン
‘ Author : チーフアーキテクト
‘ ==============================================================================
Option Explicit
‘ 定数の定義
Const URL = “https://example.com/target-table.html” ‘ 抽出対象のURL
Const OUTPUT_FILE = “C:\Data\extracted_data.csv”
Const HTTP_TIMEOUT = 10000 ‘ 10秒
Sub Main()
Dim objHTTP, htmlText, xmlDoc, nodes, node, results
Dim fso, ts, i
WScript.Echo “INFO: スクレイピング処理を開始します。”
‘ 1. HTTP通信によるHTMLの取得
htmlText = HttpGet(URL)
If htmlText = “” Then
WScript.Echo “ERROR: HTMLの取得に失敗したか、コンテンツが空です。”
Exit Sub
End If
‘ 2. MSXML2.DOMDocumentによるDOM構築
Set xmlDoc = CreateObject(“MSXML2.DOMDocument.6.0”)
xmlDoc.async = False
xmlDoc.resolveExternals = False
xmlDoc.setProperty “ProhibitDTD”, True
xmlDoc.setProperty “SelectionLanguage”, “XPath” ‘ クエリ言語としてXPathを指定
‘ 注意: 不完全なHTMLを許容するため、loadXMLにそのまま渡す
‘ ※ 厳密なHTML5の場合は、事前にSgmlReader等を通すか、XML準拠のパースが必要
If Not xmlDoc.loadXML(htmlText) Then
WScript.Echo “WARN: 標準のXMLパーサーでパースエラーが発生しました。テキストクレンジングを試行します。”
‘ ここで必要に応じてHTMLのタグ修復処理を入れる(今回は省略)
End If
‘ 3. XPathを用いたデータ抽出
‘ 例: 特定のidを持つテーブル内の、すべての行(tr)からデータを取得するXPath
‘ XPath構文: //table[@id=’target-table’]/tr
Set nodes = xmlDoc.selectNodes(“//table[@id=’target-table’]//tr”)
If nodes.length = 0 Then
WScript.Echo “WARN: 指定されたXPathに一致するノードが見つかりませんでした。”
Exit Sub
End If
‘ 4. ファイル出力準備 (FSO)
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set ts = fso.CreateTextFile(OUTPUT_FILE, True, True) ‘ Unicode(UTF-16)で出力
‘ ヘッダー書き込み
ts.WriteLine “Col1,Col2,Col3”
‘ ループ処理による構造化データの抽出
For Each node in nodes
Dim col1, col2, col3
‘ 各セル(td)への相対XPathでデータを取得
‘ ※ 存在しないノードへのアクセスでエラーにならないよう安全に取得する
col1 = GetNodeText(node, “td[1]”)
col2 = GetNodeText(node, “td[2]”)
col3 = GetNodeText(node, “td[3]”)
‘ 空行でなければCSVに出力
If col1 <> “” Or col2 <> “” Then
ts.WriteLine “””” & Replace(col1, “”””, “”””””) & “””,””” & _
Replace(col2, “”””, “”””””) & “””,””” & _
Replace(col3, “”””, “”””””) & “”””
End If
Next
ts.Close
WScript.Echo “SUCCESS: データの抽出と保存が完了しました -> ” & OUTPUT_FILE
‘ クリーンアップ
Set ts = Nothing
Set fso = Nothing
Set xmlDoc = Nothing
End Sub
‘ ——————————————————————————
‘ HTTP GETリクエスト関数 (ServerXMLHTTP使用)
‘ ——————————————————————————
Function HttpGet(targetUrl)
Dim http
On Error Resume Next
Set http = CreateObject(“MSXML2.ServerXMLHTTP.6.0”)
‘ 接続タイムアウトの設定 (resolve, connect, send, receive)単位 [ミリ秒]
http.setTimeouts HTTP_TIMEOUT, HTTP_TIMEOUT, HTTP_TIMEOUT, HTTP_TIMEOUT
http.Open “GET”, targetUrl, False
http.setRequestHeader “User-Agent”, “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AutomationClient”
http.Send
If Err.Number <> 0 Then
WScript.Echo “ERROR [HttpGet]: 通信エラーが発生しました (” & Err.Description & “)”
HttpGet = “”
Exit Function
End If
If http.Status <> 200 Then
WScript.Echo “ERROR [HttpGet]: HTTPステータス異常 -> ” & http.Status
HttpGet = “”
Exit Function
End If
‘ レスポンスのテキスト返却 (必要に応じて文字コード変換を行うこと)
HttpGet = http.responseText
Set http = Nothing
On Error GoTo 0
End Function
‘ ——————————————————————————
‘ ヌル安全にノードのテキストを取得するヘルパー関数
‘ ——————————————————————————
Function GetNodeText(parentNode, xpathQuery)
Dim targetNode
On Error Resume Next
Set targetNode = parentNode.selectSingleNode(xpathQuery)
If Not targetNode Is Nothing Then
GetNodeText = Trim(targetNode.text)
Else
GetNodeText = “”
End If
On Error GoTo 0
End Function
‘ エントリーポイントの実行
Main()
—
現場のエンジニアへ:実装上の重要な注意点とベストプラクティス
このコードを実際のプロジェクトに組み込む際、アーキテクトとして以下のポイントを厳守してほしい。
1. 文字コード(エンコーディング)の壁
現代のWebサイトの多くは `UTF-8` だが、いまだに社内システムや古いポータルサイトでは `Shift_JIS (cp932)` や `EUC-JP` が使われている。
`MSXML2.ServerXMLHTTP` はレスポンスヘッダーの `Content-Type` から文字コードを自動判別しようとするが、HTML内の `` しか定義されていない場合などに文字化けを起こす。
文字化けが発生する場合は、`http.responseBody`(バイト配列)を取得し、`ADODB.Stream` を使って明示的にテキストへデコードするロジックを挟むこと。
2. HTMLの厳密性とDOMDocumentの限界
`DOMDocument` は本来XMLパーサーであるため、閉じタグのないHTML(例: `
` や `` がそのままになっているものなど)を読み込むとパースエラーで落ちることがある。
対象サイトのHTMLが極端に崩れている場合は、事前に正規表現や置換処理で自閉タグ(`
`)に補正するか、あるいは事前にデータを整形するフィルターを通す必要がある。
3. XPathの強力な武器を使いこなせ
CSSセレクタに慣れたエンジニアも多いが、VBScriptの標準機能(MSXML)で最も相性が良いのはXPathだ。以下のような強力なクエリを使いこなせば、複雑なDOM構造から一発で目的のデータを射抜くことができる。
- `//div[@class=’content-area’]//a/@href` (特定のクラスを持つdivの中にあるすべてのリンクURLを一発取得)
- `//tr[contains(@class, ‘highlight’)]/td[2]` (クラス名の一部に特定の文字列が含まれる行の2番目のセル)
—
まとめ
レガシーな技術と侮られがちなVBScriptおよびWSH環境であっても、適切なオブジェクト(`MSXML2`)とモダンなクエリ言語(`XPath`)を組み合わせれば、最新のスクレイピングフレームワークに匹敵する軽量で高速な自動化ツールを構築できる。
無駄なリソースを消費せず、背後で静かに、しかし確実に仕事をやり遂げる――これこそが、プロフェッショナルな業務自動化エンジニアの美学だ。ぜひ自身の現場のシステムへと取り入れ、自動化の精度を極限まで高めてほしい。
