Что это вообще такое
Readability это радикальное продолжение идеи AdBlock убирать с веб-сайтов лишние элементы. Там, где AdBlock старается снести только самые бесполезные для пользователя вещи (в основном рекламу), Readability удаляет заодно скрипты, стили, навигацию и все остальное ненужное. Раньше такой вид страницы называли «версия для печати», хотя на самом-то деле текст предназначен для чтения (отсюда название Readability – «Удобочитаемость»).
Лирическое отступление про парсеры
Основная характеристика парсера сайтов, или других слабо структурированных форматов – это количество знаний о частных случаях использования формата в дикой природе.
Вырожденный случай обладания всеми знаниями – это парсер какого-то одного сайта. Т.е. если мы хотим воровать статьи с Хабрахабра, например, чтобы распечатывать их ночью на струйном принтере и приносить в жертву Сатане – мы можем посмотреть на существующую верстку и легко определить, что заголовок поста это h1.title
.
Программа, написанная таким способом, почти не будет ошибаться; для каждого отличного от Хабрахабра сайта придется писать новую программу.
Вырожденный идеальный случай: парсер вообще не знает, в каком формате он получил данные. Пример такой программы – strings
(существует в большинстве неигровых операционных систем).
Если применить strings
к какому-то не предназначенному для чтения файлу, можно получить список всего, что похоже на текст внутри этого файла. Например, команда
strings `which ls`
напечатает ворох строк для форматирования внутри бинарника ls
, и справку.
%e %b %T %Y %e %b %R usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
Чем меньше знаний, тем универсальнее парсер.
Что уже есть
Исходники первой версии Readability опубликованы и являют собой леденящий душу клубок регулярных выражений. Это само по себе неплохо, но частные случаи просто аховые. Мне бы хотелось алгоритм, который обладает гораздо меньшими знаниями о популярных сайтах в интернете (см. выше, «лирическое отступление»).
Актуальная версия Readability закрыта и увешана плюшками разнообразной востребованности. Есть API.
Существует форк первой версии Readability компанией Apple (функция Reader в браузере Safari). Исходный код не очень-то открыт, но посмотреть на него можно, там еще больше регулярных выражений и частных случаев (например, есть такая переменная – isWordPressSite
).
Проблемы оригинального скрипта – сложность модификации, аркадная эвристика. В основном работает, но требует нетривиальной доводки напильником. Версия Apple еще и лицензирована непонятно.
Что надо написать
Парсер сайтов, обладающий минимальными знаниями о разметке. Входные данные – одна страница сайта, или фрагмент страницы. Результат – текстовое представление входных данных.
Важный критерий – универсальность: программа будет работать и на клиенте, и на сервере. Поэтому мы не привязываемся к существующим реализациям DOM, а строим свою структуру данных (она еще и работает быстрее, чем полноценный DOM, т.к. данных нам нужно с гулькин, допустим, нос).
По этой же причине программа не будет уметь самостоятельно скачивать страницы из интернета, хранить результаты на диске, обладать пользовательским интерфейсом, вышивать крестиком.
Жизнь и приключения алгоритма
В поисковике нашлось несколько статей на тему алгоритмизации описанного выше процесса. Больше всего мне приглянулись вот эти китайцы PDF.
Формулы у меня получились немного другие, поэтому я расскажу кратенько про свой вариант китайского алгоритма.
Для каждого тега в документе:
- Считаем оценку
Здесь chars – количество текста (символов) внутри тега, hyperchars – количество текста в ссылках, tags – количество вложенных тегов (все метрики рекурсивные). - Считаем сумму оценок
Сумма оценок детей первого поколения (т.е. не рекурсивно). - Нашли тег с максимальной суммой
Это с высокой вероятностью контейнер основного текста. Или самый длинный комментарий. В любом случае там внутри буквы, это классно.
Простор для трудового подвига
Дальше оптимизация. Я опишу несколько случаев, но вообще это самая интересная тема, можно в комментариях поболтать.
Мусор в основном тексте. Всякие горе-блогеры любят прямо в тело поста засунуть многочисленные кнопки своих социальных вконтактов, твиттеров и т.п. ненужные вещи. У таких кнопок оценка (score, см. выше) стремится к нулю, по этому принципу их можно сносить.
Я на всякий случай проверяю еще, что после удаления мусора оценка родителя выросла, если нет (или выросла несущественно) – то не удаляю, мало ли что там.
HTML. В алгоритме не используются знания о структуре документа, их можно теперь добавить, чтобы улучшить (или ускорить) работу программы. Т.е., допустим, пессимизировать заранее <footer>
и <nav>
, или добавлять к невидимым элементам (в браузере) аннотации и пропускать их совсем – тут реально простор для деятельности, я пока ничего не реализовал.
Текстовые сигналы. Если в тексте присутствуют запятые, точки и другие знаки препинания, это с большой вероятностью связный текст (в отличие от навигации, например). Такая эвристика была в Readability.
Тут надо обратить внимание на то, что знаки препинания в разных языках все-таки разные, и запятые в китайском ("," Unicode U+FF0C) отличаются от символа "," (ASCII 44).
Что получилось, как пользоваться
Результат я назвал незатейливо readability2, выложил в npm.
Кратенько про тесты
Тестировать такую штуку надо обязательно, чтобы избежать регрессий (и вообще автоматически тестировать программы – это классно).
Тут возникает некая проблема: тест readability – это сохраненная страница совершенно постороннего сайта, плюс выдранный с нее же руками «эталонный» текст. Я не очень понимаю, как распространять это таким способом, чтобы правоторговцы не стремились меня уничтожить за незаконное копирование сайтов и текстов.
Если кто-то знает правильный ответ, напишите, пожалуйста, в комментариях. Сейчас тесты живут в закрытом репозитории, но им очень хочется на свободу.
Исходники без тестов: GitHib
Пример использования
Для иллюстративных целей я написал страничку demo.html, в которой две строки текста среди всякой навигации.
Текст называется «Название». Содержательная часть:
Весь микрорайон тихонько чудо божье наблюдал:
Поп Игнатий тилибонькал свой церковный причиндал.
Таким должен быть результат прогона программы. Если результат не такой, значит, все сломалось.
А вот исходник программы demo.js с комментариями. Используется парсер sax авторства Isaac Z. Schlueter.
API
Конструктор:
var reader = new Readability
Ничего не принимает.
SAX интерфейс:
reader.onopentag(tagName) // <тег> reader.onattribute(name, value) // атрибут=значение reader.ontext(text) // текст reader.onclosetag(tagName) // </тег>
Тут все аргументы – строки.
Чтобы получить результат:
var res = reader.compute(), text = reader.clean(res)
На выходе: res.heading
– название статьи и text
– основной текст без форматирования.
Вместо reader.clean
можно написать другой форматтер, тогда будет получаться не текст, а простая разметка, например.
Вывод
Программа работает. Ей пока немного страшно пользоваться, т.к. тестов всего около 20 штук, но я работаю над этим. Обновления будут. Патчи приветствуются, кроме всяких глупых. GitHub. Лицензия MIT, я забыл ее залить в репозиторий.
Важное замечание: картинка слева не имеет никакого отношения к посту. Поэтому если она не загрузилась, и вы не видите слева никакой картинки – не расстраивайтесь.
Лучше напишите в комментариях, что вы обо всем этом думаете.
ссылка на оригинал статьи http://habrahabr.ru/company/hola/blog/220983/
Добавить комментарий