Pull to refresh
0

Парсер в Nimbus Note, или как мы решали проблему «чистого» HTML

Reading time 3 min
Views 2.9K
Одна из ключевых возможностей Nimbus Note — это сохранение и/или редактирование заметок в виде html-документа. И заметки эти создаются/редактируются в браузере или на мобильных устройствах. После чего — отправляются на сервер. А как подсказывает профессиональная паранойя — информации пришедшей от пользователя доверять нельзя. Т.к. там может быть всё что угодно: XSS, документ, превращающий вёрстку в мечту абстракциониста или вообще ни разу не текст. Следовательно, данные пришедшие от пользователя нуждаются в предварительной обработке. В этой статье я опишу некоторые особенности нашего решения данной проблемы.



Казалось бы что тут сложного? Добавить какой-либо html-purifyer перед сохранением и всё. Да, верно, так можно было бы сделать, если бы не некоторые обстоятельства:

  • в одной заметке текста может быть много (несколько мегабайт);
  • предполагается значительное число одновременных запросов на сохранение изменений;
  • запросы на сохранение предположительно будут производится из разных частей, написанных на разных языках;
  • после обработки текста и перед сохранением возможны дополнительные проверки;
  • после обработки нужно сохранить внешний вид заметки максимально близким к изначальному (в идеале — внешний вид не должен совсем измениться);
  • вёрстка страницы при отображении сохранённой заметки не должна «страдать»;
  • невозможно использовать iframe.


Первые три пункта однозначно требуют, наличия решения, работающего отдельно от основного кода. Четвёртый же исключает использование очередей (RabbitMQ к примеру) или, что равноценно, приводит к необходимости нетривиальных решений при их использовании.

И, наконец, последние три пункта требуют глубокой обработки вёрстки с учётом того, что изначально она скорее всего не валидна («левые» и/или незакрытые теги, атрибуты, значения). К примеру если ширина любого элемента выставлена в 100500, то это значение не попадает в определение «допустимого» и должно быть удалено либо заменено (в зависимости от настроек).

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

Дабы не писать совсем уж всё с нуля, было принято решение упростить себе жизнь и задействовать какой-либо лёгкий фреймворк. Выбор пал на tornado, т. к. с ним у нас уже был опыт работы.

Исходя из соображений масштабируемости добавили в систему nginx в качестве балансировщика нагрузки. Такая структура позволяет в довольно широких пределах наращивать обрабатывающие мощности простым добавлением инстансов парсера. А наличие timeout-а у клиента на время ожидания ответа от парсера позволяет задать максимальное время ожидания, которое всё ещё не выйдет из зоны комфорта для пользователей (не вызовет ощущения, что «всё висит»).

В качестве движка html-парсера поначалу выбрали lxml. Хороший, быстрый, написанный на C парсер. И всё бы с ним хорошо, если бы не пара «сюрпризов».

Во-первых в процессе работы во всей «красе» проявился такой известный факт, как интерпретация lxml библиотекой html-докуметов как «битых» xml-ок. Эта особенность, поначалу не вызывавшая опасений, начала продуцировать всё возрастающее количество «костылей». Так, к примеру, lxml настойчиво считал, что «» — одинарный тег и исправно проводил следующее преобразование « => <span />».

Впрочем, с «костылями» можно было бы мириться, если бы не вылезло «во-вторых». При тестовом прогоне на копии реальных данных парсер стабильно вылетал по «Segmentation Fault». Что было этому причиной — неизвестно. Т.к. «вылет» гарантированно происходил после обработки примерно полутысячи записей. Вне зависимости от их содержимого (выборку производили из разных мест в таблице).

Таким образом, набив некоторое количество «шишек», остановились на связке «Beautiful Soup», «html5lib» плюс свои костыли наработки.

После этого решения уже почти начало казаться «вот оно, счастье». И длилось это счастье ровно до того момента, пока не попалась на глаза обработанная парсером страничка msn.com. Примечательными особенностями этой странички оказались активное, с выдумкой, использование атрибута «type» для тега «input» и любовь их верстальщиков к «position: absolute;».Так как проблема была локализирована, то решить её было сравнительно несложно — поправить конфиги, чуток код и, конечно же, написать тесты, покрывающие найденные тонкие места.

Теперь мы не просто абстрактно уверены, что множество страниц в сети содержат невалидный html, но ждём когда же придёт новый «сюрприз». Ждём, пытаемся принимать превентивные меры и знаем, что однажды мы увидим её, прошедшую все фильтры, все ухищрения. Увидим страницу являющуюся продуктом горячечного бреда абстракциониста…
Tags:
Hubs:
+5
Comments 2
Comments Comments 2

Articles

Information

Website
nimbusweb.me
Registered
Founded
2014
Employees
2–10 employees
Location
США