【PHP実践|実務向け】PHPで複雑なHTMLを攻略する!DOMDocumentとXPathを活用したスクレイピング手法

1. 導入:なぜDOMDocumentとXPathが重要か

Web開発の現場において、外部サイトや管理対象外のHTMLから特定の情報を抽出する「スクレイピング」は、データ収集や連携において非常に重要なスキルです。正規表現でHTMLをパースしようとすると、タグの入れ子構造や属性の順序変更に弱く、すぐにメンテナンス不能なコードになってしまいます。PHP標準のDOMDocumentとXPathを組み合わせることで、HTMLの構造を論理的かつ堅牢に解析でき、複雑なWebページからも確実に目的のデータを取得できるようになります。

2. 基礎知識:DOMDocumentとXPathとは

DOMDocumentは、HTMLやXMLドキュメントを「木構造(ツリー)」としてメモリ上に展開するクラスです。これにより、タグの親子関係を辿りながら要素を操作できます。
XPathは、XML文書内の特定の要素や属性を探し出すためのクエリ言語です。ファイルシステムでディレクトリを辿るように、HTMLの構造を直感的に指定できます。これらを組み合わせることで「id属性がcontentであるdivタグの中にある、3番目のliタグ」といった細かい指定が可能になります。

3. 実装と解決策

スクレイピングを行う際は、まずHTMLをDOMDocumentに読み込みます。このとき注意が必要なのが、HTMLの文字化けと不正な構文です。mb_convert_encodingを使用して文字コードを適切に変換し、loadHTMLメソッドを実行します。

XPathを用いる際は、クエリのインデックス番号が「1」から始まる点(プログラミング配列は0から始まる点と異なる)に注意してください。また、テキスト取得時にはnodeValueを使用しますが、HTML構造を維持して取得したい場合は、別途saveHTMLを用いたラッパー関数を用意するのが定石です。

4. サンプルプログラム

以下のコードは、特定のHTML構造から要素を抽出し、属性値を取得し、さらにPHP関数をXPath内で実行する実用的な例です。

Sample Text

‘;

// 1. DOMDocumentの初期化とHTMLの読み込み
$dom = new DOMDocument(‘1.0’, ‘UTF-8’);
// 文字化け対策
$html = mb_convert_encoding($html, “HTML-ENTITIES”, ‘auto’);
// 構文エラーを抑制して読み込み
@$dom->loadHTML($html);

$xpath = new DOMXPath($dom);

// 2. 要素の取得
$sample = $xpath->query(‘//[@id=”sample”]’)->item(0);

// nodeValueを使うとタグが消えるため、HTMLを保持して取得する関数
function getInnerHtml($node) {
$html = ”;
foreach ($node->childNodes as $child) {
$html .= $node->ownerDocument->saveHTML($child);
}
return $html;
}

// 結果の出力
echo “テキストのみ: ” . $sample->nodeValue . “\n”;
echo “HTML構造込み: ” . getInnerHtml($sample) . “\n”;

// 3. PHP関数をXPath内で利用する設定
$xpath->registerNamespace(“php”, “http://php.net/xpath”);
$xpath->registerPHPFunctions();

// 大文字小文字を区別せず特定のタグを探す例
$results = $xpath->query(“//[php:functionString(‘strcasecmp’, name(), ‘strong’)=0]”);
foreach ($results as $node) {
echo “見つかったタグ: ” . $node->nodeName . “\n”;
}
?>

5. 応用・注意点

・エラーハンドリング:HTMLは完璧な構造とは限りません。loadHTML実行時にエラーが発生しないよう「@」記号で抑制するか、libxml_use_internal_errors(true)を使用してエラーを取得・ログ出力できるようにしてください。
・クラス検索の罠:class属性はスペース区切りで複数指定されることが多いため、`[@class=”btn”]`と書くと、`class=”btn primary”`の要素がヒットしません。`[contains(@class, “btn”)]`を使用するのが現場での鉄則です。
・PHP関数連携:XPath内から独自のPHP関数を呼び出すことで、後方一致や複雑な条件判定も可能になります。ただし、処理速度には影響するため、可能な限りXPath標準の関数(starts-with, contains等)で済ませるのがパフォーマンス向上のコツです。

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