DOM Parser для выделения ключевых слов не работает
Этот вопрос связан с тем, который я задавал ранее, но поскольку тема сейчас закрыта, и мне нужно задать что-то еще, я начну новый вопрос, надеясь, что это нормально.
В моем предыдущем ответе я достаточно упростил задачу и привел к простым, но не полностью работающим решениям. Я понял это в те дни, когда реализовывал свой код.
Проблема с решениями в предыдущем посте заключается в том, что теги HTML нарушаются замещающими функциями. Я прочитал во многих постах этого сайта, что мне нужно использовать DOM Parser. Я очень незнаком с этим, и я попробовал код, предложенный пользователем "ircmaxell" в этом посте, но он не работает для меня.
Вот пример того, что я сделал:
echo '<style type="text/css">
.ht{
background-color: yellow;
}
</style>';
/* taken from user ircmaxell at https://stackru.com/questions/4081372/highlight-keywords-in-a-paragraph
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.
И только первые два слова, "Это" и "книга" выделены.
Обычно он должен был выводить всю исходную строку с выделенными ключевыми словами.
Я много искал в stackru и google и не нашел простой в использовании код для достижения моей цели, даже если есть много людей, которые спрашивали то же самое раньше.
Мне действительно нужна помощь здесь. Заранее спасибо!
1 ответ
Вам повезло, что мне было очень скучно, когда я увидел этот вопрос.;)
Код, который вы получили в качестве ответа, похоже, не был проверен - я не знаю, как он мог бы работать правильно. В любом случае, я исправил все проблемы и представил вам рабочую версию, протестированную на моем локально установленном 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();
}
Вышеуказанное решение не сработало... вот действительно хакерский, но надежный обходной путь, чтобы избежать выделения и нарушения html.
function highlight_fancy($string, $keywords=array()) {
$dom = new DOMDocument();
$dom->loadHtml($string);
// Search for all text blocks containing the keyword
$xpath = new DOMXpath($dom);
foreach($keywords as $keyword){
$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', 'hl');
$fragment->appendChild($highlight);
$text = substr($text, $pos + strlen($keyword));
}
if (!empty($text))
$fragment->appendChild(new DOMText($text));
$textNode->parentNode->replaceChild($fragment, $textNode);
}
}
$html= $dom->saveHTML();
$e=explode("<body><p>",$html);
$e=explode("</p></body>",$e[1]);
return $e[0];
}