Pull to refresh

Comments 36

Буквально на прошлой неделе наткнулся на очень милую граблю: HtmlAgilityPack при попытке распарсить одну невалидную html-ину улетел в stack overflow, чем и положил весь процесс =\ Никто не в курсе, как с обработкой кривых данных из реального веба у конкурентов (в основном интересует AngleSharp, как наиболее живой и вкусный на вид)?
Насколько мне известно, HAP — единственная библиотека, которая не дружит с кривым HTML. Что AS, что CQ должны работать нормально. Фактами доказать не могу, но глюки HAP задокументированы, а про обе нормальные библиотеки ничего подобного не слышал. :)
Вот еще один интересный баг фича в HtmlAgilityPack — элемент form не содержит дочерних элементов(если нужно распарсить параметры form для формирования последующего запроса, приходится использовать div вместо form в xPath подробнее)
Глаз зацепился:
Однако, если вам захочется поработать с css-классами, то использование XPath доставит вам много головной боли

//h3[contains(concat(' ', @class, ' '), ' r ')]/a

А что вы делаете этим запросом?
Это эквивалент CSS-запроса «h3.r>a», который корректно обрабатывает атрибуты class вида «r q», «q r», «rq» и другие. Можно было бы написать просто «h3[@class='r']/a», но тогда обработка атрибута была бы не как в CSS.
Славно накопипастили. :) Ну ладно, я не жадный. Впрочем, если подходить формально, то текст на SO лицензирован под CC BY-SA, то есть не помешает указать ссылку на источник (вопрос на SO) и лицензию (CC BY-SA 3.0). Куски кода — под Public Domain, в соответствии с указанием в моём профиле на SO (я юридически не имею права этого делать, но оставим придури законов за рамками).

Что касается производительности, то у CsQuery и HtmlAgilityPack изначально были оптимизации в разные стороны, в результате CQ быстрее обрабатывал сложные запросы за счёт построения всяких индексов, а HAP быстрее искал по всем документы простыми запросами за счёт, собственно, отсутствия индексов. За AngleSharp не отвечаю. Я с автором пообщался, он упёртый как баран.

Что меня напрягает во всех трёх библиотеках — это что все три паршиво следуют Framework Design Guidelines, да и просто хорошим практикам из самого .NET: HAP возвращает null вместо пустых коллекций; CQ имитирует краткие записи а-ля jQuery, нарушая все мыслимые стили именования; AS бездумно копирует интерфейсы из стандартов, наступая на грабли XmlDocument… В результате получается набор «молотков PHP»: вроде, все три работают, всеми можно пользоваться, но у всех трёх какие-то неоправданные странности. Впрочем, это взгляд перфекциониста; полагаю, большинству на такие нюансы наплевать. :)
Много раз приходилось парсить html в .net, все время юзал HtmlAgilityPack. За исключением мелких граблей, все работает очень прилично.
Каждый раз, когда тянет использовать Regex для парсинга HTML, читаю этот ответ, успокаиваюсь и использую как минимум HtmlAgilityPack, чего и всем желаю.
меня всегда интересовало, на чем основано убеждение этого ответа. Ну вот нужно выдрать там какой нить ответ из html и в чем ужас использовать для этого самый эффективный способ?
Ужас в цене поддержки кода. Если сайт часто меняется, то программисты удавятся ковырять регулярки каждый раз. Если код поддерживать не надо, если код сайта стабильный, если программисты дешевле железа и так далее, то регулярки рулят, конечно.
UFO just landed and posted this here
Вопрос в устойчивости парсера к изменениям в коде HTML и в количестве необходимого кода и его читаемости.

1. Если не делать регулярки непомерно сложными, то они будут ломаться из-за добавления атрибута в каком-нибудь элементе или изменения порядка классов в атрибуте. CSS-запросы более устойчивы к подобным изменениям.

2. При написании регулярок очень легко скатиться к написанию write-only кода, когда регулярка пишется в одну строчку, а баги фиксятся костылями («воткну-ка я здесь look-behind, вроде, начинает работать»). После нескольких итераций код будет легче написать заново, чем исправить.

3. При работе с DOM и CSS-запросами кода заметно меньше: работа с атрибутами, каскадами и прочим доступна из коробки, а не требует написания с нуля (или копипасты) ради каждой новой странички.

4. Порог вхождения в CSS-запросы ниже, не нужно знать премудростей регулярок, чтобы сделать простые вещи.

По производительности регулярки рвут полноценные парсеры с построением DOM как тузик грелку, конечно, но далеко не всегда парсинг — самое узкое место. Да и если узкое, не всегда выгоднее оптимизировать код (превращением его в нечитаемое месиво) вместо закидывания железом.

И я говорю как раз про использование селекторов CSS, а не XPath. XPath хоть и лучше подходит для HTML, чем регулярные выражения, но не может сравниться с селекторами, весь смысл существования которых в выборке элементов из HTML.
Если надо вытащить текст из пары нодов, то да, возможно, наверное, не знаю.

Вначале вы пишете регулярки типа ...class=...[^>]*>(?<text>...)<... .

Но вдруг, надо работать с атрибутами, порядок которых неизвестен, и начинаются пляски с опциональными группами, и вы начинаете писать регулярки типа ...(?<attr1>...)?(?<attr2>...)? либо выполняете несколько matches на каждый атрибут.

Потом вы хотите работать с коллекциями, допустим с <ul> или чего еще хуже с таблицами, и вы начинаете понимать что что-то пошло не так, но продолжаете использовать регулярки, чтобы достать значения из td вы вначале захватываете таблицу, потом коллекцию tr, и проходите по коллекции td, регулярки вида ...[^>]*>(?<text>...)<... растут как грибы на каждый элемент/идентификатор/класс/аттрибут.

И вдруг, вам необходимо ходить по DOMу, на этом месте будет боль и холодный пот, т.к. везде уже тонны регулярок. Вы с тоской удаляете регулярки, на которые затрачено много времени и используете xpath.

Это мой печальный опыт, если у вас по-другому, расскажите.
при таком подходе явно xpath проще :-) Просто парсинг обычно самодостаточен. Вот html — дай результат. И там все уже ясно.
в чем ужас использовать для этого самый эффективный способ?
Как раз ровно об этом следующий ответ на странице.
Да, верно.Просто эта цифра «4427» на SO имеет авторитет Бога.
А у «Don't listen to these guys.» всего 760 :-)
Вы только первое предложение из ответа с 760 голосами прочитали?)
Don't listen to these guys. You actually can parse context-free grammars with regex if you break the task into smaller pieces. You can generate the correct pattern with a script that does each of these in order:

Solve the Halting Problem.
Square a circle (simulate the «ruler and compass» method for this).
Work out the Traveling Salesman Problem in O(log n). It needs to be fast or the generator will hang.
The pattern will be pretty big, so make sure you have an algorithm that losslessly compresses random data.
Almost there — just divide the whole thing by zero. Easy-peasy.
Недавно возникла задача написать утилиту, уведомляющую об обновлениях на определенном сайте, где результаты поиска подгружаются динамически.

Использовал PhantonJS + Selenium Webdriver. В плане использования связка оказалась очень удобной, чего не скажешь о производительности.
UFO just landed and posted this here
Положим, XPath (и тем более ручная навигация) — тоже не сахар.
Нужен вариант «использую свой велосипед».
Прямо-таки мучает вопрос, какой можно изобрести велосипед для обработки HTML…
Четыре библиотеки из статьи изобрели же зачем-то. А мне нужен был простой парсер для построения DOM. Без блэкджека и селекторов. Пробовал HtmlAgilityPack — как показал html5test.com, он не совсем корректен. О других не знал, решил написать сам — делов то на пару часов.
И… ваш парсер, написанный за два часа, превзошёл по качеству HAP?.. По-моему, вам стоит написать об этом статью. Я б почитал.
Я не решал задачу «превзойти по качеству X», мне нужен был html парсер для браузера. Хотелось найти более лёгкую замену паре phantomjs+selenium для автоматизированного тестирования. Как-нибудь обязательно напишу об этом статью или несколько.
Ещё можно подключить jsoup через ikvm. Выходит 700 кб библиотека и 50 мб зависимостей, но работает нормально. Я использовал как замену htmlagiltypack когда нужно было быстро сделать парсер., так как CSS селекторы в jsoup работают надежней чем xpath в htmlagilitypack.
На тот момент я о нем ничего не знал и паралелльно узнал о ikvm и имел опыт использования jsoup. Если надергать CSS селекторы из отладчика хрома, то можно создать парсер за 15 минут. Конечно он ломается когда меняют структуру страницы, но это легко и быстро исправить.
Вдруг кому пригодится — используем Goose Parser, очень довольны. Работает с фантомом, позволяет декларативно описать действия пользователя и хранить их в обычном JSON. Расширяемый, если надо добавить своих фишек.
Большое спасибо за обзор. Раньше всегда использовал HAP, который славится своими багами. Но приходилось как-то жить с ним. Но благодаря обзору открыл для себя AngleSharp — это просто сказочная библиотека, бриллиант. Спасибо!
Вкратце это быстрая, относительно удобная библиотека для работы с HTML (если XPath запросы будут несложными). Репозиторий давно не обновляется.

В настоящее время (2020) парсер HtmlAgilityPack вполне живой, правда с 2017 года его разработка ведется на GitHub. Думаю имеет смысл обновить ссылку в статье.

я парсю так на .Net:

  1. загружаю плоский HTML страницы (могут быть разные реализации)

  2. прогоняю через SgmlReader - он выдает валидный ХMLDocument (https://github.com/kekyo/CenterCLR.SgmlReader)

  3. делаю запросы ХPath к ХMLDocument DOM, результаты пишу в предварительно подготовленные таблицы БД

Sign up to leave a comment.

Articles