Очистка текста от лишних html-тегов

С задачей очистки html от лишних тегов сталкиваются абсолютно все.

Первое, что приходит на ум, это использовать php-функцию strip_tags():

string strip_tags (string str [, string allowable_tags])

Функция возвращает строку, очищенную от тегов. В качестве аргумента allowable_tags передаются теги, которые не надо удалять. Функция работает, но, мягко говоря, неидеально. По ходу, там нет проверки на валидность кода, что может повлечь за собой удаление текста, не входящего в тэги.

Инициативные разработчики сложа руки не сидели — в сети можно найти доработанные функции. Хорошим примером является strip_tags_smart.

Применять или не применять готовые решения — личный выбор программиста. Так сложилось, что мне чаще всего не требуется «универсального» обработчика и бывает удобнее почистить код регулярками.

От чего зависит выбор того или иного способа обработки?

1. От исходного материала и сложности его анализа.
Если вам нужно обрабатывать достаточно простые htmp-тексты, без какой-либо навороченной верстки, ясные, как день :), то можно использовать стандартные функции.
Если в текстах есть определенные особенности, которые надо учесть, то тут-то и пишутся специальные обработчики. В одних может использоваться просто str_replace. Например:

$s = array('’' => '’',         // Right-apostrophe (eg in I'm)
  '“' => '“',                  // Opening speech mark
  '–' => '—',                  // Long dash
  'â€' => '”',                         // Closing speech mark
  'é' => 'é',                       // e acute accent
  chr(226) . chr(128) . chr(153) => '’',          // Right-apostrophe again
  chr(226) . chr(128) . chr(147) => '—',          // Long dash again
  chr(226) . chr(128) . chr(156) => '“',          // Opening speech mark
  chr(226) . chr(128) . chr(148) => '—',          // M dash again
  chr(226) . chr(128) => '”',                     // Right speech mark
  chr(195) . chr(169) => 'é',                    // e acute again
  );

foreach ($s as $needle => $replace)
{
$htmlText = str_replace($needle, $replace, $htmlText);
}

Другие могут быть основаны на регулярных выражениях. Как пример:

function getTextFromHTML($htmlText)
{
    $search = array ("']*?>.*?'si",  // Remove javaScript 
       "']*?>.*?

‘si», // Remove styles
«‘<xml[^>]*?>.*?’si», // Remove xml tags
«‘<[/!]*?[^<>]*?>’si», // Remove HTML-tags
«‘([rn])[s] ‘», // Remove spaces
«‘&(quot|#34);’i», // Replace HTML special chars
«‘&(amp|#38);’i»,
«‘&(lt|#60);’i»,
«‘&(gt|#62);’i»,
«‘&(nbsp|#160);’i»,
«‘&(iexcl|#161);’i»,
«‘&(cent|#162);’i»,
«‘&(pound|#163);’i»,
«‘&(copy|#169);’i»,
«‘&#(d );’e»); // write as php

$replace = array («»,
«»,
«»,
«»,
«\1»,
«»»,
«&»,
«<«,
«>»,
» «,
chr(161),
chr(162),
chr(163),
chr(169),
«chr(\1)»);

return preg_replace($search, $replace, $htmlText);
}

(В такие минуты как никогда радует возможность preg_replace работать с массивами в качестве параметров). Массив вы при необходимости дополняете своими регулярками. Помочь в их составлении вам может, например, этот конструктор регулярных выражений. Начинающим разработчикам может быть полезной статья «All about HTML tags. 9 Regular Expressions to strip HTML tags». Посмотрите там примеры, проанализируйте логику.

2. От объемов.
Объемы напрямую связаны со сложностью анализа (из предыдущего пункта). Большое количество текстов увеличивает вероятность, что, пытаясь предусмотреть и почистить все регулярками, вы можете что-нибудь да упустить. В этом случае подойдет метод «многоступенчатой» очистки. То есть очистить сначала, допустим, функцией strip_tags_smart (исходники на всякий случай не удаляем). Потом выборочно просматриваем некоторое количество текстов на выявление «аномалий». Ну и «зачищаем» аномалии регулярками.

3. От того, что надо получить в результате.
Алгоритм обработки может быть упрощен разными способами в зависимости от ситуации. Случай, описанный мной в одной из предыдущих статей, хорошо это демонстрирует. Напомню, текст там находился в div-е, в котором кроме него был еще div с «хлебными крошками», реклама адсенс, список похожих статей. При анализе выборки статей обнаружилось, что статьи не содержат рисунков и просто разбиты на абзацы с помощью