Blog. Just Blog

Подсветка результатов поиска на странице

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Web-мастеру и не только | Автор: ManHunter
Сложно представить современный web-сайт без поиска. И хорошим тоном считается не просто выводить все страницы, на которых был найден запрошенный результат, но и как-нибудь выделять искомые слова в тексте. Если контент сайта представляет собой обычный текст без html-разметки, то тут все просто, достаточно использовать что-то типа str_replace. А как быть, если на странице имеются html-теги, и надо выделить только тот текст, который не заключен внутри тегов? Например, нам надо выделить слово "поиск" в такой тестовой строке:
  1. Для поиска и просмотра <a href="/search" title="Результаты поиска">результатов
  2. поиска</aпройдите по поисковой ссылке.
После простой замены она превратится в нечто чудовищное:
  1. Для <b>поиск</b>а и просмотра <a href="/search" title="Результаты <b>поиск</b>а">
  2. результатов <b>поиск</b>а</aпройдите по <b>поиск</b>овой ссылке.
Как видите, текст в атрибутах title нарушен. Это простейший пример, на деле это может привести к полному развалу верстки, а то и еще хуже. При правильно сформированных поисковых запросах злоумышленники смогут даже внедрить в код страницы вредоносные скрипты. Для решения этой проблемы воспользуемся регулярными выражениями:
  1. // Искомая строка для подсветки на странице
  2. $search 'html';
  3. // Создаем строку для регулярного выражения
  4. $pattern "/((?:^|>)[^<]*)(".$search.")/si";
  5. // Подсвеченная строка
  6. $replace '$1<b style="color:#FF0000; background:#FFFF00;">$2</b>';
  7. // Заменяем
  8. $html preg_replace($pattern$replace$html);
Код также немного упрощен для удобства восприятия. В реальных проектах при составлении регулярного выражения $pattern надо обязательно экранировать в искомой строке все служебные символы, которые могут быть использованы в регулярных выражениях.

Протестируем скрипт. Для примера возьмем небольшую html-страницу с текстом и html-тегами и попробуем подсветить на ней все вхождения слова "html". Обратите внимание, что эта строка встречается не только в тексте, но и в самой разметке страницы, в адресе ссылки, а также в атрибуте title.
  1. <html>
  2. <head><title>Тестовая страница</title></head>
  3. <body>
  4.   HTML (от англHyperText Markup Language — язык разметки гипертекста) - 
  5.   стандартный язык разметки документов во Всемирной паутинеБольшинство
  6.   веб-страниц создаются при помощи
  7.   <a href="http://ru.wikipedia.org/wiki/HTML" title="Описание языка HTML">языка
  8.   HTML</a> (или XHTML).
  9. </body>
  10. </html>
Вот что у нас получилось после замены. Выглядит очень даже неплохо.

Результат подсветки
Результат подсветки

Теперь посмотрим на исходный код, не поломалось ли чего. Нет, и тут все в порядке. Подсвечен только текст вне тегов, внутри тегов он остался без изменения.

Исходный код подвеченной страницы
Исходный код подвеченной страницы

Если используется релевантный поиск, то регулярное выражение для подсветки можно доработать таким образом, чтобы подсвечивались несколько слов по отдельности и вся поисковая фраза целиком. Это вы можете сделать самостоятельно.

Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 8455 | Комментариев: 7

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Влад (21.03.2015 в 02:22):
Здорово, спасибо! Работает на ура!

Поделюсь, как видоизменил, чтобы можно было выделять несколько слов и выделялось css-классами:

$html_lit = '<html>Text of html page to replace with color lighting. Used html tags "span" and "class"  defined.</html>';
$words_and_classes = array ();
$words_and_classes[] =  array ( 'word' => 'html', 'class' => 'light_1');
$words_and_classes[] =  array ( 'word' => 'with color', 'class' => 'light_2');

foreach ($words_and_classes as $one) {
    $regs_search =  '/'.preg_quote($one['word'], '/').'/usi';
    $regs_replace = '<span class="'.  $one['class']  .'">\\0</span>';

    $html_lit = preg_replace_callback(
                    '/(?<=^|>)([^<>]+)(?=$|<)/usi',
                    function ($string) use ($regs_search, $regs_replace) {
                        return preg_replace(
                                $regs_search,
                                $regs_replace,
                                $string[1]);
                    },               
                   $html_lit);
}

Отличная вещь - regexp!
ManHunter (20.03.2015 в 02:51):
$search = 'html';
function replace($string) {
    global $search;
    return preg_replace('/'.$search.'/is',
         '<b style="color:#FF0000; background:#FFFF00;">\\0</b>', $string[1]);
}

$html=preg_replace_callback('/(?<=^|>)([^<>]+)(?=$|<)/s ', 'replace', $html);

в код сам вставишь.
Влад (20.03.2015 в 01:08):
Все работает круто, спасибо, но вот что я нашел, и не смог изменить regexp самостоятельно:

когда текст, где надо делать замены, такой, например:
<span style="color: black;">This is for REPLACE</span>
При замене REPLACE - все ок
Но есть в строке несколько вхождений заменяемой фразы, то заменяется только ПОСЛЕДНЯЯ:
<span style="color: black;">REPLACE examle - this is for REPLACE</span>

в этом примере заменится только последний REPLACE, а первый останется незамеченным :((

В какую сторону regexp-правил можно смотреть, чтобы учесть ВСЕ вхождения??

Спасибо заранее за совет!
JokerBaD (16.02.2013 в 23:52):
Спасибо за пример.Все разжевано и понятно в копилку знаний может пригодится.Регулярные выражения мощная вещь.
ManHunter (07.02.2013 в 16:59):
Я в статье это для красоты написал что ли? "Код также немного упрощен для удобства восприятия. В реальных проектах при составлении регулярного выражения $pattern надо обязательно экранировать в искомой строке все служебные символы, которые могут быть использованы в регулярных выражениях."
Тем более, что там уязвимость была, насколько я знаю, в параметре lastdate, а не в подсветке.
ЖУК (07.02.2013 в 16:57):
был такой поиск у IPB до версии 2.1.5 включительно.
позволял выполнять произвольный php код при условии поиска по постам юзеров.
фильтруйте $search
Толян (07.02.2013 в 15:42):
Круть!

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2018
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0046 сек.) / Память: 4.75 Mb
Наверх