Comments 69
- через HttpWebResponse получаю первые 5000 байт
- регуларкой получаем charset encoding
- регуляркой получаем нужние данные + иногда indexOf
С title, description и favicon ничего предполагать не надо, так как я все же вижу результаты и это все пишу не просто «выпендриться», а посоветовать, что бы люди не списывали со счетов Regex. Если вас устраивают другие варианты, то я только рад;). Меня же тот, который описал!
var link = Regex.Match(document, "(<([^>]*link[^>]+(rel[\\s]*=[\\s]*('|\"|)(shortcut|icon))[^>]+)>)", RegexOptions.IgnoreCase).Value;
var favicon = Regex.Match(link, "(href[\\s]*=[\\s]*('|\"|)([^'\"\\s]+)('|\"|))", RegexOptions.IgnoreCase).Value;
(С) Jamie Zawinski
Названия методов заимствованы из JavaScript + плюшки: GetElementbyId(), CreateAttribute(), CreateElement() и т.д.
Справедливости ради отмечу, что это не заимствование, а интерфейсы DOM. В JS они просто взяты оттуда же:)
А при работе уже с HtmlNode такого метода нет. И это крайне не удобно, если делаешь не поиск 1 div в огромном документа, а когда делаешь довольно большой разбор
DOM-методы — не самая удобная вещь на свете.
Я тут парсил вики на выходных, и дубляж обнаружил даже там где-то.
По этому стандарт стандартом, а парсер должен работать.
Все-таки искать по id внутри ноды это одно, а его неуникальность — другое. Если мы предположим, что атрибут id может быть не уникальным, то нам придется из метода, название которого говорит, что он возвращает один элемент, возвращать коллекцию.
Если вам действительно, на практике, необходимо такое поведение, то можно отнаследоваться и поступить так, как в js-библиотеке MooTools:
function (id) {
var founded = document.getElementById(id);
if(!founded) {
return null;
}
for (var parent = founded.parentNode; parent != this; parent = parent.parentNode) {
if (!parent) {
return null;
}
}
return founded
}
Такую проблему решал распараллеливанием. На некоторых сайтах 25 потоков спокойно работают. Хотя хабр, как понимаю, ограничения накладывает на кол-во запросов. Получится около 1-2секунды на стр
хотя один вывод пожалуй сделать можно — серьезные отечественные компании (например Rambler, СКБ Контур, QIWI Кошелек, Parallels) требуют наличия высшего образования.
впрочем, вы можете и сами взглянуть на отчете, который приложен в результатах топика
По сравнению с регулярками медленней?
в любом случае, разбирать DOM-деревья регулярными выражениями — плохой выбор (см. комментарий выше)
Ну там «tr.hot» и дальше разбор полетов…
Не понимаю, почему у этой библиотеки такая популярность, хотя сейчас есть достойные конкуренты. Я разобрал некоторые на StackOverflow:
Как распарсить HTML в .NET?
Короче, я бы советовал переходить на CsQuery или AngleSharp, а не пользоваться этим ископаемым глюкалом (некоторые ещё и XPath умудряются пользоваться, хотя даже для HAP есть Fizzler, я уж молчу про хождение по ChildNodes индексами — ещё одна загадка природы).
P. S. Пишу комментарий к старой статье, потому что она в топе гугла, и, подозреваю, многие ей доверятся. Про недостатки HAP надо знать.
А сейчас то, в 2020, т.е. еще через 5 лет, этот баг пофикшен наконец? :) Ссылка ваша теперь не валидна, поэтому не понял о каком баге идет речь.
В 2020 году нет смысла использовать что-то, что не называется AngleSharp. Автор даже сдался и нарушил "стандарты" во имя адекватного API. Это лучшая библиотека для парсинга HTML, точка.
CsQuery умер как бесполезный на фоне AngleSharp.
HtmlAgilityPack существует как легаси. Он переехал на GitHub, трекер забит багами про неправильный парсинг, в роадмапе невыполненные планы лежат годами, всем пофиг. После смерти CodePlex — внезапно! — ничего не поменялось.
Пора просто забыть про существование HAP. Это такое же говнище внутри и снаружи, как пакет CommandLine, который тоже используют чисто за счёт популярности. При этом любой, кто хоть раз заглядывал внутрь, больше никогда к этому не притронется. Особенно к CommandLine, но это отдельная история для вечера у костра в день всех святых.
Ну вот я попробовал AngleSharp и вот что мне не понравилось:
- При парсинге частичного HTML, парсер всегда оборачивает результат в
<html><body></body></html>
, хотя мне это не нужно. К счастью, это решается с помощью фрагментного парсинга. - При парсинге неизвестных тегов парсер вставляет какую-то несуществующую хрень. Пример: парсим хабровский
<cut/>
, получаем<cut></cut>
. А при таком коде<cut/><!--<spoiler1>--> test</spoiler1>
парсер изгаляется как может:<cut><!--<spoiler1>--> test</spoiler1></cut>
(пришлось заменить spoiler на spoiler1, потому что хабровский парсер ломается). - Парсер никуда не сохраняет тип кавычек у атрибутов и позиции элементов в исходном тексте.
Это пункты делают невозможным конвертацию html файлов с кастомными тегами.
При этом все это нормально работает в HtmlAgilityPack. Так что рано ставить точку — до идеала далеко.
При этом любой, кто хоть раз заглядывал внутрь, больше никогда к этому не притронется.
Ну мне, как конечному потребителю библиотеки, не особо важна чистота реализации.
AngleSharp парсит так, как будет парсить любой полноценный браузер. Из невалидного HTML он сгенерирует точно такой же мусор, который сгенерирует браузер. HAP в этом плане гораздо хуже, потому что мало того, что он сгенерирует мусор по-своему, так ещё и валидный код распарсит не так, как полноценный браузер.
Вы парсите что угодно, но не HTML, да и и не для обработки и отображения. Вы бы цели как-то обозначили, потому что совершенно непонятно, чего вы хотите добиться.
По пунктам:
Итого, проблема отсутствует, если использовать корректный API.
Выполните код
div.innerHTML = "<p><cut/></p>"; alert(div.innerHTML)
. Это к вопросу о том, почему так сделано. Если парсер Хабра не состоянии обработать полученный текст (там что, регулярка?), то это проблема Хабра. Если вы ориентируетесь на неправильное поведение, то вы заранее подписываетесь на проблемы, независимо от библиотеки.
А вот реальные кастомные теги, которые будут обрабатываться реальными браузерами — будут работать корректно. И вы таки не поверите: несуществующий тег cut таки должен оборачивать происходящее дальше согласно стандарту, что все браузеры и делают.
Вам зачем кавычки сохранять? Вы используете библиотеку в редакторе? Тогда при использовании HAP у вас будет гораздо больше проблем, потому что пока AngleSharp изменит сериализованный формат, HAP испортит и сериализацию, и представление: пробелы полетят, вложенность тегов изменится и т.п., причём без всяких кастомных тегов.
Итого:
Если мне надо предсказуемо распарсить HTML так, как сделает нормальный браузер, то выбора нет — только AngleSharp. HAP сделает что-то своё и непредсказуемое.
Если писать плагин для редактора или ещё что-нибудь такое, то обе библиотеки совершенно не подходят. Там нужен совершенно другой уровень представления.
Если для работы с текстом для Хабра вам идеально подходит HAP (например, потому что на Хабре используется та же библиотека, поэтому многочисленные баги аккуратно нкладываются друг на друга), то вам просто сильно повезло, но в общем случае HAP остаётся ужасно кривым парсером, несовместимым с окружающим миром.
Вы бы цели как-то обозначили, потому что совершенно непонятно, чего вы хотите добиться.
Согласен — надо было с этого начинать. Я разрабатываю конвертер MarkConv. И дело в том, что сам Markdown может включать в себя HTML блоки, например так:
<details>
<summary>title</summary>
content
</details>
Сейчас используются регулярки, но сейчас переписываю на нормальные парсеры: Markdig и HTML.
Markdig парсит этот так: html block; paragraph; html block. И эти html блоки уже можно парсить с помощью html парсера. Но вот незадача — они невалидные, т.е. в конце, например, получится закрывающий тег </details>. С таким парсингом нормально не справляются ни HAP ни AngleSharp.
Тогда я пришел к другой более сложной стратегии: обрабатывать сначала markdown блоки, затем сцеплять их в единый html и парсить уже валидный HTML. Схема работает с использованием HAP, а с AngleSharp возникли проблемы, указанные выше.
Короче мне нужен парсер, из которого можно получить максимально точный исходный вариант MD или HTML. Причем HTML вряд ли будет особо навороченным.
Если парсер Хабра не состоянии обработать полученный текст (там что, регулярка?), то это проблема Хабра.
Про Хабра сказал лишь с целью того, что через комментарий не смог нормально выразить свою мысль. А так-то да — у Хабра есть проблемы с парсингом.
И вы таки не поверите: несуществующий тег cut таки должен оборачивать происходящее дальше согласно стандарту, что все браузеры и делают.
Может быть, но меня-то интересует хабровский тег, который обрабатывается по-другому. И я хочу оставить его в исходном md документе неизменным, а не додуманным до закрывающегося тега.
Вам зачем кавычки сохранять? Вы используете библиотеку в редакторе?
Ну ок — это в принципе небольшая некритичная вещь, без которой можно обойтись.
Если писать плагин для редактора или ещё что-нибудь такое, то обе библиотеки совершенно не подходят. Там нужен совершенно другой уровень представления.
Да, примерно такой кейс. Ну я бы не сказал, что совершенно — HAP более мене справляется. Писать свое я сейчас не хочу.
Если для работы с текстом для Хабра вам идеально подходит HAP (например, потому что на Хабре используется та же библиотека, поэтому многочисленные баги аккуратно нкладываются друг на друга)
И для Хабра тоже. Разве используется? Я думал Хабр на PHP написан.
Я разрабатываю конвертер MarkConv.
Я правильно понял, что это конвертация между диалектами, плюс какая-то обработка картинок и генерация содержания? Было бы неплохо обозначить в ридми суть: зачем конкретно оно надо, что конкретно оно делает по пунктам.
Тогда я пришел к другой более сложной стратегии: обрабатывать сначала markdown блоки, затем сцеплять их в единый html и парсить уже валидный HTML. Схема работает с использованием HAP, а с AngleSharp возникли проблемы, указанные выше.
Подход звучит логичным, потому что это то, что будет делать конвертер MD в HTML. С фрагментами проблем нет, как я понял. Если вам нужна поддержка тегов со специфическим поведением, я бы форкнул AngleSharp (для ката скопипастить код какого-нибудь только-самозакрывающегося тега типа img и убрать лишнее). Кавычки значения не должны иметь для ваших целей.
Я правильно понял, что это конвертация между диалектами, плюс какая-то обработка картинок и генерация содержания? Было бы неплохо обозначить в ридми суть: зачем конкретно оно надо, что конкретно оно делает по пунктам.
Я пишу и храню статьи на гитхабе и использую мягкие переносы, чтобы делать удобные дифы. А хабр разрывает их, вот баг. Первоначальная идея конвертера была в фиксе этого бага — это вполне реализовать с помощью регулярных выражений.
Потом захотелось конвертировать теги <details>
в хабровский <!--<spoiler>-->
, проверять валидность ссылок, мапить локальные картинки на habrastorage, генерировать содержание и делать другие вещи. На самом деле по маркдауну у хабра есть еще проблемы. До какой-то степени это реализовать удалось, но сейчас код выглядит уже довольно костыльно, к тому же я столкнулся с проблемами при обработки вложенных узлов. И вот недавно решил переписать проект на нормальные парсеры.
В итоге MarkConv сейчас представляет из себя не только конвертер маркдаунов (помимо habr там теперь еще поддерживается формат https://dev.to/), но и чекер.
В целом я продвигаю идею написания текста как исходного кода, с использованием системы контроля версий, пулл-риквестов, непрерывной интеграции и других привычных для программиста вещей. Думаю написать об этом статью на хабр, продвинуть MarkDown.
Если вам нужна поддержка тегов со специфическим поведением, я бы форкнул AngleSharp (для ката скопипастить код какого-нибудь только-самозакрывающегося тега типа img и убрать лишнее). Кавычки значения не должны иметь для ваших целей.
Я пожалуй пока что останусь на HAP — вроде работает нормально, к тому же такие HTML вставки намного легче полноценного кода веб-страницы. Если обнаружу, что он не справляется с парсингом валидного, задумаюсь об альтернативах. Форком сейчас не хочу заниматься.
В итоге решил написать свой лексер и парсер на ANTLR — так получилось обойтись без костылей с преобразованием markdown фрагментов в html текст — для них просто используются специальные токены MARKDOWN_FRAGMENT
. Грамматика HTML небольшая и легко кастомизируемая (в частности добавил поддержку хабровского тега cut): HtmlLexer, HtmlParser.
Подробней о технических деталях парсинга HTML внутри Markdown я планирую написать статью в скором времени.
<ошибся веткой>
Html Agility Pack — удобный .NET парсер HTML