DOM Parser для выделения ключевых слов не работает - PullRequest
4 голосов
/ 18 февраля 2012

Этот вопрос связан с вопросом, который я задал до , но поскольку тема закрыта, и мне нужно еще кое-что задать, я начну новый вопрос, надеясь, что это нормально.

В моем предыдущем ответе я достаточно упростил задачу и привел к простым, но не полностью работающим решениям. Я понял это в наши дни, когда реализовывал свой код.

Проблема с решениями из предыдущего поста заключается в том, что теги HTML нарушаются замещающими функциями. Я прочитал во многих постах этого сайта, что мне нужно использовать DOM Parser. Я очень незнаком с этим, и я попробовал код, предложенный пользователем «ircmaxell» в этом сообщении , но он не работает для меня.

Вот пример того, что я сделал:

echo '<style type="text/css">
       .ht{
         background-color: yellow;
       }
     </style>'; 


/* taken from user ircmaxell at /3764819/vydelite-klychevye-slova-v-abzatse

I just modified line $highlight->setAttribute('class', 'highlight') to $highlight->setAttribute('class', 'ht') and commented the first 2 lines   */

function highlight_paragraph($string, $keyword) {
  //$string = '<p>foo<b>bar</b></p>';
  //$keyword = 'foo';
  $dom = new DomDocument();
  $dom->loadHtml($string);
  $xpath = new DomXpath($dom);
  $elements = $xpath->query('//*[contains(.,"'.$keyword.'")]');
  foreach ($elements as $element) {
   foreach ($element->childNodes as $child) {
     if (!$child instanceof DomText) continue;
     $fragment = $dom->createDocumentFragment();
     $text = $child->textContent;
     $stubs = array();
     while (($pos = stripos($text, $keyword)) !== false) {
       $fragment->appendChild(new DomText(substr($text, 0, $pos)));
       $word = substr($text, $pos, strlen($keyword));
       $highlight = $dom->createElement('span');
       $highlight->appendChild(new DomText($word));
       $highlight->setAttribute('class', 'ht');
       $fragment->appendChild($highlight);
       $text = substr($text, $pos + strlen($keyword));
     }
     if (!empty($text)) $fragment->appendChild(new DomText($text));
     $element->replaceChild($fragment, $child);
   }
 }
 $string = $dom->saveXml($dom->getElementsByTagName('body')->item(0)->firstChild);
 return $string;
}


$string = '<p>This book has been written against a background of both reckless optimism and reckless despair.</p>
<p>It holds that Progress and Doom are two sides of the same medal; that both are articles of superstition, not of faith. It was written out of the conviction that it should be possible to discover the hidden mechanics by which all traditional elements of our political and spiritual world were dissolved into a conglomeration where everything seems to have lost specific value, and has become unrecognizable for human comprehension, unusable for human purpose.</p>
<p> Hannah Arendt, The Origins of Totalitarianism (New York: Harcourt Brace Jovanovich, Inc., 1973 ed.), p.vii, Preface to the First Edition.</p>';

$keywords = array('This', 'book', 'has', 'been', 'written', 'background', 'reckless', 'optimism', 'despair.', 'holds', 'Progress', 'Doom ', 'two', 'sides', 'medal;', 'articles', 'superstition,', 'faith.', 'lost', 'Arendt,', 'Totalitarianism');

foreach ($keywords as $kw) {
  $string = highlight_paragraph($string, $kw);
}

echo $string;

echo $ string возвращает только:

This book has been written against a background of both reckless optimism and reckless despair.

И только первые два слова, «Эта» и «книга» выделены.

Обычно он должен был выводить всю исходную строку с выделенными ключевыми словами.

Я много искал в stackoverflow и google и не нашел простого в использовании кода для достижения моей цели, даже если многие люди уже спрашивали об этом раньше.

Мне действительно нужна помощь здесь. Заранее спасибо!

1 Ответ

7 голосов
/ 01 марта 2012

Вам повезло, что мне было очень скучно, когда я увидел этот вопрос.;)

Код, полученный вами в качестве ответа, похоже, не был проверен - я не знаю, как он мог бы работать правильно.В любом случае, я исправил все проблемы и представил вам рабочую версию - протестирован на моем локально установленном Apache Server с PHP 5.3:

function highlight_paragraph($string, $keyword) {
  $dom = new DOMDocument();
  $dom->loadHtml($string);

  // Search for all text blocks containing the keyword
  $xpath = new DOMXpath($dom);
  $textNodes = $xpath->query('//*[contains(.,"'.$keyword.'")]/text()');

  foreach ($textNodes as $textNode) {
    $fragment = $dom->createDocumentFragment();
    $text = $textNode->nodeValue;
    $stubs = array();

    while (($pos = stripos($text, $keyword)) !== false) {
      $fragment->appendChild(new DOMText(substr($text, 0, $pos)));
      $word = substr($text, $pos, strlen($keyword));

      $highlight = $dom->createElement('span');
      $highlight->appendChild(new DOMText($word));
      $highlight->setAttribute('class', 'ht');
      $fragment->appendChild($highlight);

      $text = substr($text, $pos + strlen($keyword));
    }

    if (!empty($text))
      $fragment->appendChild(new DOMText($text));

    $textNode->parentNode->replaceChild($fragment, $textNode);
 }

 return $dom->saveHTML();
}
...