Html Agility Pack — удобный .NET парсер HTML

    Всем привет!
    Как-то раз мне пришла в голову идея проанализировать вакансии размещенные на Хабре. Конкретно интересовало, есть ли зависимость между размером зарплаты и наличия высшего образования. А еще сейчас у студентов идет сессия (в том числе и у меня), то возможно кому-то уже надоело трепать нервы на экзаменах и этот анализ будет полезен.
    Так как я программист на .Net, то и решать эту задачу — парсить объявления на Хабре я решил на C#. Вручную разбирать строки html мне не хотелось, поэтому было придумано найти html-парсер, который помог бы осуществить задачу.
    Забегая вперед скажу, что из анализа ничего интересного не вышло и сессию придется сдавать дальше :(
    Но зато немножко расскажу про весьма полезную библиотеку Html Agility Pack

    Выбор парсера


    Вышел на эту библиотеку я через обсуждение на Stackoverflow. В комментариях предлагались еще решения, например библиотека SgmlReader, которая переводит HTML в XmlDocument, а для XML в .Net инструментов полный набор. Но почему-то меня это не подкупило и я пошел качать Html Agility Pack.

    Беглый осмотр Html Agility Pack


    Справку по библиотеке можно скачать на странице проекта. Функционал на самом деле очень радует.
    Всего нам доступно двадцать основных классов:



    Названия методов соответствуют интерфейсам DOM (замечание k12th) + плюшки: GetElementbyId(), CreateAttribute(), CreateElement() и т.д., так что работать будет особенно удобно, если приходилось сталкиваться с JavaScript
    Похоже, что html все же перегоняется в Xml, а HtmlDocument и др. классы это обертка, ну и ничего страшного в этом, ввиду этого доступны такие возможности как:
    • Linq to Objects (via LINQ to Xml)
    • XPATH
    • XSLT

    Парсим хабр!


    Вакансии на хабре представлены в виде таблицы, в строках дана информация о требуемой специальности и зарплате, но так как нам нужна информация об образовании, то придется переходить на страницу вакансии и разбирать ее.
    Итак, начнем, нам нужна таблица, чтобы вытащить оттуда ссылки и инфу о позиции с зарплатой:
    1. static void GetJobLinks(HtmlDocument html)
    2. {
    3.     var trNodes = html.GetElementbyId(«job-items»).ChildNodes.Where(=> x.Name == «tr»);
    4.  
    5.     foreach (var item in trNodes)
    6.     {
    7.         var tdNodes = item.ChildNodes.Where(=> x.Name == «td»).ToArray();
    8.         if (tdNodes.Count() != 0)
    9.         {
    10.             var location = tdNodes[2].ChildNodes.Where(=> x.Name == «a»).ToArray();
    11.  
    12.             jobList.Add(new HabraJob()
    13.             {
    14.                 Url = tdNodes[0].ChildNodes.First().Attributes[«href»].Value,
    15.                 Title = tdNodes[0].FirstChild.InnerText,
    16.                 Price = tdNodes[1].FirstChild.InnerText,
    17.                 Country = location[0].InnerText,
    18.                 Region = location[2].InnerText,
    19.                 City = location[2].InnerText
    20.             });
    21.         }
    22.  
    23.     }
    24.  
    25. }

    А после осталось пройти по каждой ссылке и вытащить инфу об образовании и заодно еще и занятость — здесь есть небольшая проблема в том, что если таблица с ссылками на вакансию лежала в div-е с известным id, то информация о вакансия лежит в таблице без всяких id, поэтому пришлось немножко поизвращаться:
    1. static void GetFullInfo(HabraJob job)
    2. {
    3.     HtmlDocument html = new HtmlDocument();
    4.     html.LoadHtml(wClient.DownloadString(job.Url));
    5.     // html.LoadHtml(GetHtmlString(job.Url));
    6.  
    7.     // так делать нельзя :-(
    8.     var table = html.GetElementbyId(«main-content»).ChildNodes[1].ChildNodes[9].ChildNodes[1].ChildNodes[2].ChildNodes[1].ChildNodes[3].ChildNodes.Where(=> x.Name == «tr»).ToArray();
    9.  
    10.     foreach (var tr in table)
    11.     {
    12.         string category = tr.ChildNodes.FindFirst(«th»).InnerText;
    13.  
    14.         switch (category)
    15.         {
    16.             case «Компания»:
    17.                 job.Company = tr.ChildNodes.FindFirst(«td»).FirstChild.InnerText;
    18.                 break;
    19.             case «Образование:»:
    20.                 job.Education = HabraJob.ParseEducation(tr.ChildNodes.FindFirst(«td»).InnerText);
    21.                 break;
    22.             case «Занятость:»:
    23.                 job.Employment = HabraJob.ParseEmployment(tr.ChildNodes.FindFirst(«td»).InnerText);
    24.                 break;
    25.             default:
    26.                 continue;
    27.         }
    28.     }
    29. }

    Результаты


    Ну а дальше, сохраняем результаты в XML и смотрим в Excel-e, что же получилось… и видим, что ничего хорошего не получилось, потому что большинство компаний либо не указывают размер зарплаты, либо не указывают информацию об образовании (забывают, указывают в теле вакансии, или действительно неважно), либо не указывают все сразу.
    Кому интересно, вот результаты в xlsx и xml, а здесь исходник

    P.S.


    При парсинге возникла такая проблема — страницы скачивались очень медленно. Поэтому я сначала попробовал WebClient, а потом WebRequest, но разницы не было. Поиск в гугле указал на то, что следует явно отключать Proxy в коде, и тогда все будет хорошо, однако это тоже не помогло.

    Комментарии 68

      –1
      Когда мне надо было парсить html, я пользовался регулярными выражениями. А можно было проще…
        +12
          0
          Спасибо. Теперь я тоже это знаю.
            +2
            как мне кажется, через это проходили если не все, то многие :-)
            года с полтора назад я писал качалку музыки с известного ресурса — ссылки парсились регулярками, а когда появилась идея про вакансии на хабре, мысль с регулярками как-то даже и не пришла в голову — все приходит с опытом
              +1
              Я ненормальный — когда все использовали регулярные выражения, я использовал конечные автоматы :)
                0
                Надеюсь это не патология, ибо нас таких минимум двое)
            +4
            «asking regexes to parse arbitrary HTML is like asking Paris Hilton to write an operating system» © от туда (-:
              0
              Всему надо знать меру. Если у вас есть html и надо получить, к примеру, title, не ужели будете подключать сторонние библиотеки и парсить весь html? Regex в данном случае продуктивнее. А то что парень по ссылке с пеной у рта отстаивает свою правоту, так это, извините, диагноз.
                0
                Это не пена у рта, это сатира.
                  0
                  Если бы актеру пришлось переиграть это обращение к народу, ему бы пришлось использовать «пену», иначе получилось бы не правдоподобно)
                  Юмор, и сатира в частности, служит высмеиванию чего то не правильного, а, как говорилось, Regex все же уместен в некоторых случаях, поэтому как то не очень смешно.
                  0
                  Посмотрите второй ответ.
                    0
                    Не загоняйте меня в цикл) Я всего лишь хочу сказать, что не стоит так насмехаться над регулярками. Они в сотнях случаях эффективнее любых парсеров, поэтому сравнивать с пэрис это кощунство!)
                      0
                      Регулярки лучше только в одном случае — нужно очень быстро вытащить относительно небольшой кусок простых данных в неизменяемых и известных исходных данных. Я сам был большой любитель регулярных выражений, пока не столкнулся с необходимостю вытаскивать title(в том числе) из тысяч страниц — вот тут то и стало понятно, что весь код надо нафиг переделать.
                        0
                        А вот у меня такая ситуация: В одном проекте надо получать title, favicon и description страницы:
                        • через HttpWebResponse получаю первые 5000 байт
                        • регуларкой получаем charset encoding
                        • регуляркой получаем нужние данные + иногда indexOf
                        Согласен, что немного спартанские методы, но зато уже длительное время все работает шустро и стабильно. И только исxодя из этого, я и говорю, что иногда не стоит отказываться от регулярныx выражений, потому что у самого нету с ним негативного опыта. Конечно, весь документ парсить таким образом мне и в голову не приxодило.
                          0
                          регуларкой получаем charset encoding

                          Могу предположить что у вас либо только английский язык, либо постоянные кракозябры в результатах.

                          Мне приходится парсить русско-/англо-/украино- язычные сайты и «простая» процедура получения кодировки сводится к тонне кода и собственному бранчу chardet.

                          В одном проекте надо получать title, favicon и description

                          Опять-же, могу предположить что часть фавиконок у вас не находится, потому что люди прописывают фавикон как минимум десятком неправильных, но понимаемых браузером способов(если вообще прописываются).
                            0
                            На вxод, в массе, кириллица — проблем ни разу не было! Если в коде указан charset encoding его и используем, если нет, тогда — Ude.

                            С title, description и favicon ничего предполагать не надо, так как я все же вижу результаты и это все пишу не просто «выпендриться», а посоветовать, что бы люди не списывали со счетов Regex. Если вас устраивают другие варианты, то я только рад;). Меня же тот, который описал!
                              0
                              Покажите пожалуйста для образовательных целей регэксп для фавикона.
                                +1
                                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;
                                  +1
                                  Извините убирал лишнее и пропустил: в получении урл используем 3 найденую группу.
                                0
                                Если в коде указан charset encoding его и используем

                                Цифра по памяти, но у меня процента 3 страниц указывает кодировку одну, а использует другую и процентов 6 указывают кодировку почти правильно, т.е. неправильно пишут название кодировки.
                                  0
                                  6% отпадают, так как Encoding.GetEncoding(encodingName) не обработает не верное имя -> заюзаем тогда Ude. За 3% ваша правда, но пока что все в пределаx нормы, если что, так перейду полностью на Ude.
                  +3
                  Some people, when confronted with a problem, think «I know, I'll use regular expressions.» Now they have two problems

                  (С) Jamie Zawinski
                    +1
                    Эта цитата обязательно появляется в любом месте, где упоминались регулярные выражения.
                    И самое интересное, что она не надоедает.
                      0
                      Потому что это правда.
                        0
                        Это точно.
                  +3
                  Очень хороший dom-парсер, долго использую его. Особенно удобно, что умеет dom-модель достраивать и XPath выполнять.
                    +1
                    из минусов я выделил только то, что нет родных методов GetElement(s)ByTag / ByName / ByClass — хотя это все легко реализуется с помощью LINQ
                      0
                      Хм… а можно же extension написать.
                      • НЛО прилетело и опубликовало эту надпись здесь
                      +1
                      Единственная проблема, автор не спешит принимать патчи, да и вообще как-то затерялся. Так что многое приходится патчить вручную.
                        +3
                        Названия методов заимствованы из JavaScript + плюшки: GetElementbyId(), CreateAttribute(), CreateElement() и т.д.

                        Справедливости ради отмечу, что это не заимствование, а интерфейсы DOM. В JS они просто взяты оттуда же:)
                          0
                          спасибо за замечание, сейчас поправлю :-)
                            0
                            GetElementbyId() вещь конечно хорошая, только у нее есть косяк в том, что этот метод есть только у HtmlDocument…
                            А при работе уже с HtmlNode такого метода нет. И это крайне не удобно, если делаешь не поиск 1 div в огромном документа, а когда делаешь довольно большой разбор
                              0
                              Согласно стандарту, id должен быть уникальным в пределах документа. Так что как бы и нет смысла искать по id в пределах определенной ноды (хоть это и не совсем так). В JS, например, у нод тоже нет этого метода — всё согласно стандарту.
                              DOM-методы — не самая удобная вещь на свете.
                                0
                                Стандарты стандартами, к ним очень вольно относятся разработчики.
                                Я тут парсил вики на выходных, и дубляж обнаружил даже там где-то.
                                По этому стандарт стандартом, а парсер должен работать.
                                  0
                                  Вики? Википедию? Неуникальные id'ы? плохо:(

                                  Все-таки искать по 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
                                  }
                                  
                            +1
                            Я этот парсер использую для фильтрации того хлама, что пишут в форму написания поста/комментария пользователи. Достаточно хорошая штука, ни разу не подводила.
                              0
                              >возникла такая проблема — страницы скачивались очень медленно.

                              Такую проблему решал распараллеливанием. На некоторых сайтах 25 потоков спокойно работают. Хотя хабр, как понимаю, ограничения накладывает на кол-во запросов. Получится около 1-2секунды на стр
                                –4
                                Ничего полезного — в смысле те кто имеют высшее получают больше или как?
                                  0
                                  в смысле, что недостаточно данных для того чтобы вести речь о какой-либо зависимости.
                                  хотя один вывод пожалуй сделать можно — серьезные отечественные компании (например Rambler, СКБ Контур, QIWI Кошелек, Parallels) требуют наличия высшего образования.
                                  впрочем, вы можете и сами взглянуть на отчете, который приложен в результатах топика
                                  0
                                  Зачем такие страшные конкструкции, если заявлена поддержка xpath?
                                    0
                                    сам с xpath никогда не сталкивался и узнал про него когда страшная конструкция уже была написана. поэтому, оставлю на будущее
                                      0
                                      Год назад были проблемы с поддержкой xpath — простые конструкции он обрабатывал верно, но на что-нибудь более-менее сложное возвращал пустой результат.
                                    0
                                    Как со скоростью обработки тегов?
                                    По сравнению с регулярками медленней?
                                      0
                                      документ слишком маленький, пожалуй, на нем серьезного сравнения не провести.
                                      в любом случае, разбирать DOM-деревья регулярными выражениями — плохой выбор (см. комментарий выше)
                                        0
                                        Да, если планируете использовать его на продакшене, под нагрузкой — не советую. Будет медленнее раз в 5-10, чем регулярки. Но это недостаток всех dom-парсеров.
                                          0
                                          Так это смотря на «продакшене» ЧЕГО.
                                          Если не большого массива информации за короткое время, а обычных сайтов, периодически обновляемой информации, то удобство окупается.
                                          Вспоминаю парсинг html регулярками как страшный сон.
                                            0
                                            ну я написал, под нагрузкой. предполагал, что то, о чём вы говорите, будет понятно
                                        0
                                        В HtmlAgilityPack есть класс HtmlWeb, с помощью которого можно сразу получить HtmlDocument, вызвав метод экземпляра Load и передав в него в качестве параметра url страницы, так что можно было обойтись возможностями лишь HtmlAgilityPack в рамках этой задачи. У вас на скриншоте этот класс, кстати, есть.
                                          0
                                          спасибо за наводку, я это пропустил
                                            0
                                            Спасибо
                                            0
                                            Я для парсинга субтитров с ted.com использовал эту штуку, удобно очень.
                                              +2
                                              Тоже пытались использовать. Удобно бесспорно, но у нас были очень большие объемы (много страниц парсились одновременно), а он к сожалению выжирает очень много памяти, так как хранит на момент работы всю Dom структуру документа. И к сожалению пришлось вернуться к регулярным выражениям. Т.е. на больших объемах обрабатываемых параллельно, готовьтесь к большим утечкам памяти. Но, на мой взгляд, лучшее решение на небольших объемах или если нужно выдирать много различных данных, и регулярки использовать накладно в плане нагрузки процессора, так как операции поиска по dom отрабатывают быстрее (проверенно на парсинге ссылок из документа) и есть возможность жертвовать памятью.
                                                –1
                                                а вам было критично знать всю структуру? или последовательного чтения достаточно?
                                                если да, то по идее можно использовать SAX (Simple Api XML) — в .net это обеспечивает класс XmlReader.
                                                написать обертку для HTML и вперед
                                                0
                                                Конечно лучше всего использовать xpath, а не городить такой монструозный код. В файерфоксе есть куча плагинов, облегчающих работу с xpath, т.е. достаточно кликнуть на нужный элемент, скопировать выражение и вставить в код. Только нужно знать некоторые особенности, например когда файерфокс создает DOM, после тэга table добавляется tbody, при верстке страницы его как правило опускают. Я написал для себя маленькую утилитку, позволяющую тестить xpath именно для HtmlAglityPack и статического html, получилось очень удобно.
                                                  –1
                                                  А аналогов nokogiri для .net нет?

                                                  Ну там «tr.hot» и дальше разбор полетов…
                                                    +1
                                                    А как библиотека справляется с неправильным html (нет закрытия тега и т.д.)?
                                                      +2
                                                      Вполне отлично — закрывает их! :)
                                                      0
                                                      Я в свое время для разбора сайтов SgmlReader юзал. Всех плюшек уже не помню, но то что он «неправильный» html делал «правильным» и спокойно работал с xpath, точно было.
                                                        0
                                                        Забавно, что по прошествии уймы лет, критический баг Incorrect parsing of HTML4 optional end tags до сих пор не пофиксен. Это означает, что совершенно валидный документ HTML будет неверно обработан, если он пользуется опциональностью закрытия тегов. Это же курам на смех! Пять лет! Пять лет прошло!

                                                        Не понимаю, почему у этой библиотеки такая популярность, хотя сейчас есть достойные конкуренты. Я разобрал некоторые на StackOverflow:

                                                        Как распарсить HTML в .NET?

                                                        Короче, я бы советовал переходить на CsQuery или AngleSharp, а не пользоваться этим ископаемым глюкалом (некоторые ещё и XPath умудряются пользоваться, хотя даже для HAP есть Fizzler, я уж молчу про хождение по ChildNodes индексами — ещё одна загадка природы).

                                                        P. S. Пишу комментарий к старой статье, потому что она в топе гугла, и, подозреваю, многие ей доверятся. Про недостатки HAP надо знать.
                                                          0

                                                          А сейчас то, в 2020, т.е. еще через 5 лет, этот баг пофикшен наконец? :) Ссылка ваша теперь не валидна, поэтому не понял о каком баге идет речь.

                                                            0

                                                            В 2020 году нет смысла использовать что-то, что не называется AngleSharp. Автор даже сдался и нарушил "стандарты" во имя адекватного API. Это лучшая библиотека для парсинга HTML, точка.


                                                            CsQuery умер как бесполезный на фоне AngleSharp.


                                                            HtmlAgilityPack существует как легаси. Он переехал на GitHub, трекер забит багами про неправильный парсинг, в роадмапе невыполненные планы лежат годами, всем пофиг. После смерти CodePlex — внезапно! — ничего не поменялось.


                                                            Пора просто забыть про существование HAP. Это такое же говнище внутри и снаружи, как пакет CommandLine, который тоже используют чисто за счёт популярности. При этом любой, кто хоть раз заглядывал внутрь, больше никогда к этому не притронется. Особенно к CommandLine, но это отдельная история для вечера у костра в день всех святых.

                                                              0

                                                              Ну вот я попробовал AngleSharp и вот что мне не понравилось:


                                                              1. При парсинге частичного HTML, парсер всегда оборачивает результат в
                                                                <html><body></body></html>, хотя мне это не нужно. К счастью, это решается с помощью фрагментного парсинга.
                                                              2. При парсинге неизвестных тегов парсер вставляет какую-то несуществующую хрень. Пример: парсим хабровский <cut/>, получаем <cut></cut>. А при таком коде <cut/><!--<spoiler1>--> test</spoiler1> парсер изгаляется как может: <cut><!--<spoiler1>--> test</spoiler1></cut> (пришлось заменить spoiler на spoiler1, потому что хабровский парсер ломается).
                                                              3. Парсер никуда не сохраняет тип кавычек у атрибутов и позиции элементов в исходном тексте.

                                                              Это пункты делают невозможным конвертацию html файлов с кастомными тегами.


                                                              При этом все это нормально работает в HtmlAgilityPack. Так что рано ставить точку — до идеала далеко.


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

                                                              Ну мне, как конечному потребителю библиотеки, не особо важна чистота реализации.

                                                                0

                                                                AngleSharp парсит так, как будет парсить любой полноценный браузер. Из невалидного HTML он сгенерирует точно такой же мусор, который сгенерирует браузер. HAP в этом плане гораздо хуже, потому что мало того, что он сгенерирует мусор по-своему, так ещё и валидный код распарсит не так, как полноценный браузер.


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


                                                                По пунктам:


                                                                1. Итого, проблема отсутствует, если использовать корректный API.


                                                                2. Выполните код div.innerHTML = "<p><cut/></p>"; alert(div.innerHTML). Это к вопросу о том, почему так сделано. Если парсер Хабра не состоянии обработать полученный текст (там что, регулярка?), то это проблема Хабра. Если вы ориентируетесь на неправильное поведение, то вы заранее подписываетесь на проблемы, независимо от библиотеки.


                                                                  А вот реальные кастомные теги, которые будут обрабатываться реальными браузерами — будут работать корректно. И вы таки не поверите: несуществующий тег cut таки должен оборачивать происходящее дальше согласно стандарту, что все браузеры и делают.


                                                                3. Вам зачем кавычки сохранять? Вы используете библиотеку в редакторе? Тогда при использовании HAP у вас будет гораздо больше проблем, потому что пока AngleSharp изменит сериализованный формат, HAP испортит и сериализацию, и представление: пробелы полетят, вложенность тегов изменится и т.п., причём без всяких кастомных тегов.



                                                                Итого:


                                                                1. Если мне надо предсказуемо распарсить HTML так, как сделает нормальный браузер, то выбора нет — только AngleSharp. HAP сделает что-то своё и непредсказуемое.


                                                                2. Если писать плагин для редактора или ещё что-нибудь такое, то обе библиотеки совершенно не подходят. Там нужен совершенно другой уровень представления.


                                                                3. Если для работы с текстом для Хабра вам идеально подходит HAP (например, потому что на Хабре используется та же библиотека, поэтому многочисленные баги аккуратно нкладываются друг на друга), то вам просто сильно повезло, но в общем случае HAP остаётся ужасно кривым парсером, несовместимым с окружающим миром.


                                                                  0
                                                                  Вы бы цели как-то обозначили, потому что совершенно непонятно, чего вы хотите добиться.

                                                                  Согласен — надо было с этого начинать. Я разрабатываю конвертер 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 написан.

                                                                    0
                                                                    Я разрабатываю конвертер MarkConv.

                                                                    Я правильно понял, что это конвертация между диалектами, плюс какая-то обработка картинок и генерация содержания? Было бы неплохо обозначить в ридми суть: зачем конкретно оно надо, что конкретно оно делает по пунктам.


                                                                    Тогда я пришел к другой более сложной стратегии: обрабатывать сначала markdown блоки, затем сцеплять их в единый html и парсить уже валидный HTML. Схема работает с использованием HAP, а с AngleSharp возникли проблемы, указанные выше.

                                                                    Подход звучит логичным, потому что это то, что будет делать конвертер MD в HTML. С фрагментами проблем нет, как я понял. Если вам нужна поддержка тегов со специфическим поведением, я бы форкнул AngleSharp (для ката скопипастить код какого-нибудь только-самозакрывающегося тега типа img и убрать лишнее). Кавычки значения не должны иметь для ваших целей.

                                                                      0
                                                                      Я правильно понял, что это конвертация между диалектами, плюс какая-то обработка картинок и генерация содержания? Было бы неплохо обозначить в ридми суть: зачем конкретно оно надо, что конкретно оно делает по пунктам.

                                                                      Я пишу и храню статьи на гитхабе и использую мягкие переносы, чтобы делать удобные дифы. А хабр разрывает их, вот баг. Первоначальная идея конвертера была в фиксе этого бага — это вполне реализовать с помощью регулярных выражений.


                                                                      Потом захотелось конвертировать теги <details> в хабровский <!--<spoiler>--> , проверять валидность ссылок, мапить локальные картинки на habrastorage, генерировать содержание и делать другие вещи. На самом деле по маркдауну у хабра есть еще проблемы. До какой-то степени это реализовать удалось, но сейчас код выглядит уже довольно костыльно, к тому же я столкнулся с проблемами при обработки вложенных узлов. И вот недавно решил переписать проект на нормальные парсеры.


                                                                      В итоге MarkConv сейчас представляет из себя не только конвертер маркдаунов (помимо habr там теперь еще поддерживается формат https://dev.to/), но и чекер.


                                                                      В целом я продвигаю идею написания текста как исходного кода, с использованием системы контроля версий, пулл-риквестов, непрерывной интеграции и других привычных для программиста вещей. Думаю написать об этом статью на хабр, продвинуть MarkDown.


                                                                      Если вам нужна поддержка тегов со специфическим поведением, я бы форкнул AngleSharp (для ката скопипастить код какого-нибудь только-самозакрывающегося тега типа img и убрать лишнее). Кавычки значения не должны иметь для ваших целей.

                                                                      Я пожалуй пока что останусь на HAP — вроде работает нормально, к тому же такие HTML вставки намного легче полноценного кода веб-страницы. Если обнаружу, что он не справляется с парсингом валидного, задумаюсь об альтернативах. Форком сейчас не хочу заниматься.

                                                          0

                                                          <ошибся веткой>

                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                          Самое читаемое