HTMLを保持しながら文章を切る (truncate HTML document without corrupting HTML structure)

HTML文書(またはHTMLの断片)を短くしたい場合、単純に substr とか mb_substr で切ると、切った先のタグまで無くなってしまい、HTML構造が壊れてしまいます。
そこで、HTML構造を保持しながら、本文の長さを任意に短くする方法を考えてみました。

でもこれって絶対誰か考えているよなあ…と思いつつ。

function truncate_html($html, $length, $ellip='...', $refine=true) {
 $text_length = 0; // 得られる本文の長さ
 $truncated = ''; // 得られるHTML
 preg_match_all('/\x3c[^\x3c\x3e]*?\x3e/', $html, $tags); // 対象中のすべてのHTMLタグのリストを作る
 foreach ($tags[0] as $tag) { // すべてのHTMLタグリストに対して
  $re = '/([^\x3c\x3e]*?)('.str_replace('/', "\x5c\x2f", preg_quote($tag)).')/';
  preg_match($re, $html, $match); // HTMLタグではない何かに続くHTMLタグ、を(1度だけ)探す
  $text = trim($match[1]); // 1番目にマッチするのはHTMLではないテキストなので
  $len = mb_strlen($text); // その長さを取得(0またはその長さ)
  if ($text_length < $length) { // 保持している本文の長さ $text_length が、希望の長さより短い場合
   if ($text_length + $len > $length) { // マッチしたテキストを足すと希望の長さを超える場合
    $text = mb_substr($text, 0, $length - $text_length) . $ellip; // 希望の長さになるように詰めて、省略記号を追加
   }
   $text_length += mb_strlen($text); // $text_length に マッチしたテキストの長さを足す
   $truncated .= $text; // 得られるHTMLにそのテキストを加える
  }
  if (!($refine &amp;&amp; preg_match('/\x3c(?:br)\x20?\x2f?\x3e/',$match[2]))) {
   $truncated .= $match[2]; // $refine=余分なタグを削除したい場合、かつタグがbrだった場合、ではない場合はタグを加える
   // 最後お好みで<p></p>とかも削除したりしてください。
  }
  $html = preg_replace($re, '', $html, 1); // 元のHTMLから処理した本文+HTMLタグを(最初の)1回だけ削除
 } // を繰り返す
 return $truncated . ($text_length < $length ? mb_substr($html, 0, $length - $text_length) : ''); // 残ったHTMLタグではないものを付け加える
}
&#91;/php&#93;</pre>
<p>ただし、入力するHTMLが、スタートタグ─エンドタグの組が正しく記述されていることが前提なので、元のHTMLがあやしい場合は最後にphpQueryとかでrefineするといいと思います。</p>
<pre>[php]require_once("phpQuery.php");
echo(mb_convert_encoding(phpQuery::newDocument(truncate_html($a,$len=180))->htmlOuter(), 'UTF-8', 'HTML-ENTITIES'));

Leave a Reply