1. 背景―なぜ HTML5 が課題になるか
HtmlAgilityPack(以下 HAP)は HTML 4.01/XHTML 体系の DTD を前提にしたストリーミング‐トークン型パーサ を核にしています。
一方 HTML5 仕様は “タグ閉じ忘れの許容”“名前空間混在(SVG・MathML)”“新しい暗黙的終了規則” など独自のパース・ツリー生成アルゴリズムを採用しており、DTD 非依存 である点が決定的に異なります。
そのため HTML5 要素を多用するサイト をそのまま HAP に読み込むと、以下のような不整合・情報欠落が起こり得ます。
2. 主な制限事項と典型的な影響
| 分類 | 制限ポイント | 具体的な影響例 |
|---|---|---|
| 新要素の扱い | <section> / <article> / <nav> / <header> / <footer> などに特別な意味づけ無し |
暗黙的終了規則が働かず 意図しない入れ子構造になる場合がある |
| コンポーネント系 | <template>・Web Components(カスタム要素) |
<template> 内部は “ただのテキスト” として解析され、DOM が生成されない |
| メディア関連 | <picture><source srcset="..."> |
<source> は 自己閉じ と判定されず、後続のノードが <picture> 内へ誤って吸収される |
| 名前空間要素 | SVG・MathML | 名前空間属性が脱落し、<svg> 直下の子要素がフラットに並ぶ |
| 属性仕様 | srcset, autocomplete, novalidate 等の HTML5 boolean/複数値属性 |
未知属性として読み込むため文字列取得は出来るが 論理属性 としての真偽判定がアプリ側で必要 |
| 追加実体参照 | ⨯, ¹ など HTML5 Named Character References |
HtmlEntity.DeEntitize() が未定義のため生文字列に戻らず実体参照のまま残る |
3. 代表的な不具合シナリオ
-
ブログ記事 DOM の断裂
<article>に続いてすぐ<section>が開き<section>を閉じ忘れたまま次の<article>が登場すると、HAP は階層を強制修正しないため記事ごとの切れ目が判別不能になる。 -
画像ギャラリーの
srcset消失
レスポンシブ画像を抽出したいのに、<source>要素が正しく閉じられず兄弟要素が子要素化し、srcset属性も取得漏れとなるケース。 -
SVG アイコンが空要素化
<svg><path d="..."/></svg>のpathが 明示的閉じタグ を持たないと “空テキストノード” だけが残り、パスデータが欠落。
4. 回避策・緩和策
| アプローチ | 内容 | 適用指針 |
|---|---|---|
| AngleSharp との二段階パース | csharp<br>var doc = await BrowsingContext.New(Configuration.Default).OpenAsync(req => req.Content(html));<br>var hap = new HtmlDocument();<br>hap.LoadHtml(doc.ToHtml()); |
HTML5 準拠パーサで一度 “正規 DOM” を作ってから HAP へ橋渡し。タグ補完・名前空間保持・既知エンティティ展開が確実 |
| PuppeteerSharp / Playwright での事前レンダリング | JS で生成・変換されるノード(Web Components など)をヘッドレスブラウザ側で innerHTML として取得してから解析 | 画面描画後の “最終 DOM” が欲しいスクレイピングで有効 |
| HTML Tidy / HtmlSanitizer による XHTML 変換 | tidy -asxhtml 相当で明示閉じタグと名前空間宣言を補完 |
オフライン処理で大量文書を一括変換する場合に高速 |
| HAP オプション強化 | csharp<br>var doc=new HtmlDocument{<br>OptionFixNestedTags=true,<br>OptionOutputAsXml=true,<br>OptionWriteEmptyNodes=true}; |
軽度なタグ閉じ忘れや孤立テキストノードの修正なら十分 |
| エンティティ拡張テーブル | HtmlEntity.AddEntity("#x2715", "×"); など自前登録 |
ニッチな新規 Named Entity が必要で AngleSharp を併用しないケース |
5. 実装ベストプラクティス
-
“どこまで HAP に任せるか” を最初に決める
-
静的 HTML → HAP 単独 でも OptionFixNestedTags + 正規表現フォローで済む場合多い
-
動的生成 or Web Components → AngleSharp / ブラウザ自動化 を前提にしたワークフローへ移行
-
-
検証用ユニットテストを必ず用意
HTML5 ページ断片を 最小ケース に分解し、Assert.Equal()でノード階層を比較する。これにより暗黙閉じ規則のズレを早期発見できる。 -
名前空間要素は “除外 or 別ライブラリ” で扱う
SVG 解析が目的なら SvgDocument/Svg.Skia など専門パーサへ分業し、HAP では OuterHtml ごと除去してしまう。 -
長期的には AngleSharp への乗り換えを検討
HAP はメンテナンスコストが低く軽量ですが、HTML5 仕様全体への追随度は限定的です。HTML5 固有要素・属性を主体とするモダンサイトのクローリングが中心業務なら AngleSharp + CSS Selectors & LINQ をメインとし、HAP は XHTML エクスポート/XPath 処理特化ツール として残す構成が保守的にも安全です。
まとめ
HtmlAgilityPack は HTML5 を多用するサイト に対しては 構文修復の限界 と 未知要素の扱い により整合性問題が発生します。
しかし AngleSharp 併用・ヘッドレスブラウザ前処理・XHTML への正規化 などを組み合わせれば、大半のケースで実用上支障のない DOM ツリーを得ることが可能です。
目的に応じて 「軽量・高速」 vs 「HTML5 完全準拠」 のトレードオフを見極め、適切なパーサ選定と前処理フローを設計してください。
ChatGPT4o 生成日:2025/06/23