1.h2,h3.h4タグ,ul,liの一番深いものではないタグ
2.一番深いタグ
に分けて表示させたいのですが、プログラムが書けません。
うまく説明ができないので、説明用のファイルをアップしてあります。
http://1811way.com/work008/sample09.html
のhtmlファイルがあります。
このファイルに対して、simple_html_dom.php
を使って、
http://1811way.com/work008/sample09kekka.html
のように表示させたいです。
一番深いliタグの内容を[Contents]、それ以外を[Subject]としてひとまとめに表示させたいのですが、
できません。
できないというのは、アルゴリズムが組み立てられない、という事です。
参考までに、僕が書いたプログラムです。
これを元に、
sample09kekka.html
のように表示させるプログラムのアドバイス、考え方、もしくはソースを書いてくれると
大変助かります。
$html = file_get_html('sample09.html');
$i = 0;
foreach ($html->find('ul,h4,h3,h2') as $ul) {
$i++;
$test01 = $ul->find('h4',0);
echo $i . '番目の' . 'h4です:' . $test01 . '<br />';
}
以上、よろしくお願いします。
考え方でも良いということなので、javascript でやってみました(php は不得手です :-)。
jsFiddle で試したのがこちらです。
https://jsfiddle.net/a_kuma3/Lvm5vey1/
元になる HTML では SubjectNo2 と No3 の見出しタグの閉じタグが開始タグと一致していなかったので、そこは修正しています。
探索しているコードはこんな感じです。
// 探索ロジックはここから var ul_list = document.body.querySelectorAll("UL"); Array.prototype.forEach.call(ul_list, function(ul) { if (! ul.querySelector("UL")) { var li_list = ul.querySelectorAll("LI"); Array.prototype.forEach.call(li_list, function(item) { var bottom_content = item; var route = []; var LIMIT = 100; // 無限ループが恐いので... while (item.tagName == "LI" && LIMIT > 0) { LIMIT = LIMIT - 1; // LI の親要素 var p = item.parentNode; // 多分 UL if (p.tagName == "BODY") { // 念のため break; } // UL よりも前にある要素を探す e = p.previousSibling; while (e.nodeType != 1) { e = e.previousSibling; } // 見出し要素ならパンくずの道筋 if (/^H[1234]/.test(e.tagName)) { route.push(e); } // 見出しの親要素で続ける item = e.parentNode; // 多分 LI } // 逆にしたのがパンくず var breadcrumb = route.reverse(); // printout printout(breadcrumb, bottom_content); n += 1; }); } }); // 表示用 var n = 1; function printout(breadcrumb, bottom_content) { var bc_text = breadcrumb.map(function(e) { return e.textContent; }).join(" > "); console.log(n + ".[Subect]:" + bc_text); console.log(n + ".[Contents]:" + bottom_content.textContent); var msg = document.getElementById("output"); msg.value += n + ".[Subect]:" + bc_text + "\n"; msg.value += n + ".[Contents]:" + bottom_content.textContent + "\n"; }
「一番深い LI タグ」は、「一番深い UL を見つけて、その下にある LI」という探し方をしています。
「一番深い UL」とは、「全ての UL のうち、その子供に UL がない UL」です。
パンくずは、HTML の構造に依存します。
「一番深い LI」のそれぞれについて、以下のロジックで探します
・LI の親要素を見つける(UL のはず)
・その UL と同じ階層にあって、それよりも前にある要素を探す
・その要素のタグが見出しタグなら、パンくずの候補に入れる
・その見出しタグの親(LI のはず)をターゲットにして繰り返す
・見出しタグの親要素が LI じゃなかったら探索の終了
・パンくずの候補は深い方から浅い方向の順番なので、配列をひっくり返す
javascript と php の Simple HTML DOM の差異について書いておきます。
ほとんどはメソッド・プロパティの読み替えだけで済むと思いますが、一点だけ大きく違うところがあります。
タグで要素を探すのに querySelectorAll というメソッドを使っています。
Simple HTML DOM では、find メソッドに相当します。
親の要素を取得します。
その要素と同じ階層で、ひとつ前の要素を取得します。
ここが大きく違います。
javascript(というか、普通の DOM)では、このメソッドは Node を返します。
Node は、HTML のタグを表すもの(要素:Element)だけではなく、テキストやコメントなども表します。
先のコードで e.nodeType != 1 というループがあるのは、要素を探しています。
// UL よりも前にある要素を探す e = p.previousSibling; while (e.nodeType != 1) { e = e.previousSibling; }
タグの間にある空白や改行も javascript の DOM では Node として取得されるので、nodeType プロパティを見て、要素が見つかるまで前に前にと探します。
Simple HTML DOM ではドキュメントに以下のように記載されています。
element $e->prev_sibling () Returns the previous sibling of element, or null if not found.
リファレンスを見ても、Text Node に関する記載がないので、多分、要素:Element がいきなり返ります。
タイプを見ながらさかのぼる必要はないと思います。
ある要素の下位にあるテキスト要素だけを取り出します。
見出しタグを含めて取り出したければ、innertext を使います。
<?php require "simple_html_dom.php"; $html = ... // 表示用 function text_content($e) { return $e->plaintext; } function printout($breadcrumb, $bottom_content, $n) { echo $n . '.[Subject]:' . implode(' > ', array_map("text_content", $breadcrumb)) . '<br>'; echo $n . '.[Contents]:' . $bottom_content->plaintext . '<br>'; } $n = 1; // 探索ロジックはここから $ul_list = $html->find('UL'); foreach ($ul_list as $ul) { $ul_child = $ul->find('UL'); if (count($ul_child) == 0) { $li_list = $ul->find('LI'); foreach ($li_list as $item) { $bottom_content = $item; $route = array(); $limit = 100; // 無限ループが恐いので while ($item->tag == 'li') { $limit = $limit - 1; // LI の親要素 $p = $item->parent(); // 多分 UL if ($p->tag == "body") { // 念のため break; } // UL の前にある要素 $e = $p->prev_sibling(); // 見出し要素ならパンくずの道筋 if (preg_match('/^H[1234]/i', $e->tag)) { $route[] = $e; } // 見出しの親要素で続ける $item = $e->parent(); // 多分 LI } $breadcrumb = array_reverse($route); printout($breadcrumb, $bottom_content, $n); $n = $n + 1; } } } ?>
Phpfiddle で試してみたのがこちらです。
http://phpfiddle.org/lite/code/qz9p-b0t6
できました。というかa-kuma3さんのコピペですがしっかり結果出ました。僕が実際考えても、できないか、かなりな時間がかかりましたね。 ありがとうございました。
2015/11/02 12:17:37ideone.com がときどきこけるので、その押さえくらいにと思ってた phpfiddle が file_get_html で別サイトをアクセスできたり、MySQL や SQLite が使えるっぽいということが分かって、ぼくも収穫ありでした :-)
2015/11/02 13:08:34