Monday, November 15, 2010

Как извлечь полезный текст из HTML. Часть 1

Задача на первый взгляд может показаться тривиальной: извлечь полезный текст из HTML страниц с различных сайтов, например новости с новостных лент. На практике, однако, реализация подобной функциональности, как правило, оканчивается написанием кучи парсеров заточенных под конкретные сайты. Поддерживать такие парсеры – сущий кошмар, особенно если система должна работать в автономном режиме долгое время. Хотелось бы иметь универсальное решение. Сегодня я опишу один из возможных вариантов решения этой проблемы.

Веб страница состоит из многих частей: полезный текст, некоторым образом размеченный, меню, заголовки, метатекст, скрипты итп. Можно было бы просто убрать всю HTML-разметку, но, к сожалению, это не работает, т.к. остается слишком много мусора. Кроме того, иногда мы хотим сохранить некоторые элементы разметки, например ссылки или изображения относящиеся к тексту, слова и предложения выделенные другим шрифтом итп.

Суть предлагаемого подхода состоит в том, чтобы разбить документ на большое количество частей (это могут быть строки либо параграфы) и для каждой части посчитать количество HTML разметки. Оказывается, что количество HTML разметки в участках с осмысленным текстом существенно меньше чем в других участках документа.

Для нашего примера я взял статью с rbcdaily.ru Можно было бы использовать версию для печати, но в целях чистоты эксперимента, остановимся на стандартной версии. Текст был разбит на строки и для каждой строки посчитано количество HTML-тегов разметки и количество прочего текста.

  
boolean isMarkup = false;
int markupCnt = 0;
for (int i = 0; i < sline.length(); i++) {
char ch = sline.charAt(i);
if (ch == '<') {
isMarkup = true;
}
if (isMarkup) {
markupCnt++;
}
if (ch == '>') {
isMarkup = false;
}
}

Красной линией на графике обозначается длина строки, синей линией количество не-HTML текста. В целях улучшения визуализации, показаны данные усредненные по 5 строк.



Не слишком понятная картина, правда? Некий пик между 89 и 100, а в остальном, много пиков и провалов. Беглый взгляд в HTML и становится понятно, что в нашем случае он очень сильно засорен JavaScipt-ом и HTML-комментариями. Для улучшения результатов, стоит их удалить. Это сделать достаточно просто, предварительно обработав HTML. После этого, результаты становятся более очевидными:



У нас четко нарисовался пик с основным текстом и куча мусора, который можно смело выбрасывать в корзину. Простейшим способам выбрать где здесь текст, а где мусор, это отобрать строки в которых величина отношения длины HTML разметки к длине строки меньше определенного порогового значения. В нашем случае я взял 0.3 и получил на удивление хорошие результаты



У такого подхода, однако, есть недостаток: несмотря на то, что все заголовки убраны, в тексте все еще сохраняется подпись. Кроме того, могут быть пропущены часть строк реального текста если, по некоторому стечению обстоятельств, там оказалось много разметки или сама строчка была слишком короткой. Для того чтобы улучшить фильтрацию следует рассматривать не только текущую строку но и соседние с ней. Как это сделать, я расскажу в следующий раз.

Протестировать результаты такого фильтра можно тут, введите адрес страницы которую бы вы хотели отфильтровать и нажмите старт.




1 comment: