【PHP実践|実務向け】PHPで実現する柔軟なHTML解析:DOMDocumentとXPathの活用術

Webスクレイピングや外部サイトのデータ取得において、PHPでHTMLを解析する機会は非常に多いものです。しかし、HTMLは構造が不完全であったり、大文字・小文字が混在していたりと、XMLのように単純には扱えないケースが多々あります。

本記事では、PHPの「DOMDocument」クラスと「DOMXPath」クラスを組み合わせ、実務レベルで頻出する「複雑なHTMLからのデータ抽出」をスマートに解決する方法を解説します。

基礎知識:DOMDocumentとXPathとは

PHPでHTMLを解析する際、正規表現を使うのはおすすめしません。HTMLの構造が少し変わるだけで壊れてしまうからです。代わりにDOMDocumentを使用します。これはHTMLをツリー構造としてメモリに展開する仕組みです。

そのツリー構造の中から、特定のノード(要素)を柔軟に検索するための言語がXPathです。「idがcontentである要素の中の、すべてのliタグ」といった指定が可能になります。

実装のポイント

HTML解析における最大の難関は「文字化け」と「構文エラー」です。
PHPのDOMDocumentはXMLベースで動作するため、HTML特有の特殊文字や文字コードの扱いに注意が必要です。

1. 文字コードの変換: `mb_convert_encoding($html, “HTML-ENTITIES”, ‘auto’)` を使い、文字参照形式へ変換することで文字化けを防止します。
2. エラー抑制: `loadHTML()` を実行する際、`@`演算子を付けて警告を抑制します。不完全なHTMLタグがあっても処理を続行させるためです。
3. XPathの活用: `DOMXPath`を利用して、要素名だけでなく、属性やテキスト内容に基づいた検索を行います。

サンプルプログラム

以下のコードは、idを指定して要素を取得し、その中の情報を抽出する実例です。

Sample Text

  • A
  • B

‘;

// 1. DOMDocumentの初期化
$dom = new DOMDocument(‘1.0’, ‘UTF-8’);
$html = mb_convert_encoding($html, “HTML-ENTITIES”, ‘auto’);
@$dom->loadHTML($html);

// 2. XPathオブジェクトの作成
$xpath = new DOMXPath($dom);

// 3. 特定の要素を取得
$sample = $xpath->query(‘//[@id=”sample”]’)->item(0);
if ($sample) {
// nodeValueはタグを除去したテキストのみを返す
echo “テキストのみ: ” . $sample->nodeValue . “\n”;

// タグを含めたHTMLを取得する独自関数
echo “タグ込み: ” . getInnerHtml($sample) . “\n”;
}

/

  • 指定ノード内のHTMLを取得する補助関数

/
function getInnerHtml($node) {
$html = ”;
foreach ($node->childNodes as $child) {
$html .= $node->ownerDocument->saveHTML($child);
}
return $html;
}
?>

応用・注意点:現場で陥りやすい罠

実務で特に注意すべき点は以下の通りです。

・class属性の指定
`//[@class=”btn active”]` と書くと、classが `btn active` のものしかマッチしません。`btn` と `active` を両方持つ要素を狙う場合は、`//[contains(@class, “btn”) and contains(@class, “active”)]` というように、XPathの `contains` 関数を併用してください。

・大文字小文字の区別
XPathは厳格に大文字小文字を区別します。サイトによって `

` だったり `
` だったりする場合、`registerPHPFunctions()` を活用して、PHP側の `strcasecmp` 関数をXPathのクエリ内で呼び出すテクニックが有効です。これにより、大文字小文字を無視した安定した検索が可能になります。

・正規表現への過度な依存を避ける
複雑な条件で抽出したい場合、つい正規表現を使いたくなりますが、ネストされた構造には対応できません。XPathの関数(`starts-with`, `contains`)を駆使することで、ほとんどのケースは解決可能です。

不規則なHTMLデータに対しても、今回紹介した手法をベースに設計すれば、堅牢なデータ取得ロジックを構築できるはずです。ぜひ日々の開発に取り入れてみてください。

タイトルとURLをコピーしました