PHPのXPathをトリッキーに使ってHTMLのテキストノードを取得
以前、PHPのXPathでテキストノード取得したら、期待した順番通りに取得できなかった。と書きました。
ある要素の子ノード群に、エレメントノードとテキストノードがそれぞれ2つ以上あると起きる現象ではないかと推測します。
直下じゃなければいいのでは?
ということで、ダミータグで囲ってみたら、期待した順番通りに取得できました。
PHPソース
<?php $html = <<< EOL <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'> <html> <head> <meta http-equiv='Content-type' content='text/html; charset=utf-8'> <title>test</title> </head> <body> <div> <a href="hoge.php">hoge</a> | <a href="huga.cgi">huga</a> | <a href="piyo.php">piyp</a> </div> </body> </html> EOL; $data = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'); $dom = new DOMDocument; @$dom->loadHTML($data); // nameとidの値が被るとエラーになるので、@で抑制 $xpath = new DOMXpath($dom); echo '<h2>通常</h2>'; $query = '/html/body//text()[string-length(normalize-space()) > 0]'; $text_nodes = $xpath->query($query); foreach ($text_nodes as $n) { var_dump($n->nodeValue); } // テキストノードをダミータグ"<z>"で囲む $query = '/html/body//text()[string-length(normalize-space()) > 0]'; $text_nodes = $xpath->query($query); foreach ($text_nodes as $n) { $dummy_tag = $dom->createElement('z'); $dummy_tag->nodeValue = $n->nodeValue; $n->parentNode->replaceChild($dummy_tag, $n); } echo '<h2>秘策</h2>'; $query = '/html/body//text()[string-length(normalize-space()) > 0]'; $text_nodes = $xpath->query($query); foreach ($text_nodes as $n) { var_dump($n->nodeValue); }
吐かれた結果
通常 string ' | ' (length=3) string 'hoge' (length=4) string ' | ' (length=3) string 'huga' (length=4) string 'piyp' (length=4) 秘策 string 'hoge' (length=4) string ' | ' (length=3) string 'huga' (length=4) string ' | ' (length=3) string 'piyp' (length=4)
キタコレ!
ちなみに
tidyで整形して出力すれば、ダミータグは削除できます。
$tidy = tidy_parse_string($dom->saveHTML()); $html = tidy_get_html($tidy); echo $html->value;
う〜ん冗長