Парсеров всем! Анализируем и тестируем существующие HTML парсеры



    Всем привет!

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

    Да, в этой статье я предлагаю выложить все парсеры на стол и измерить!

    Приступим!

    Прежде чем что-то сравнивать, нам нужно понять: что же мы собственно хотим сравнить?! А сравнить мы хотим хтмл парсеры, но вот что такое хтмл парсер?

    Хтмл парсер это:
    1. Токенизатор (Tokenizer) — разбивка текста на токены
    2. Построение дерева (Tree Builder) — размещение токенов «в нужные позиции» в дереве
    3. Последующая работа с деревом

    Человек «с мороза» может сказать: — «Не обязательно строить дерево для парсинга хтмл, ведь достаточно получить токены». И к сожалению, будет не прав.
    Дело в том, что для правильной токенизации хтмл нам необходимо иметь дерево под рукой. Пункт 1 и 2 неразделимы.

    Приведу два примера:

    Первый:
    <svg><desc><math><style><a>
    

    Результат правильной обработки:
    <html>
    	<head>
    	<body>
    		<svg:svg>
    			<desc:svg>
    				<math:math>
    					<style:math>
    						<a:math>
    


    Второй:
    <svg><desc><style><a>
    

    Результат правильной обработки:
    <html>
    	<head>
    	<body>
    		<svg:svg>
    			<desc:svg>
    				<style>
    					<-text>: <a>
     


    После ":" идет namespace, если не указано то хтмл.

    Из двух примеров видно, что элемент STYLE ведет себя по разному в зависимости от того, где находится. В первом варианте есть элемент A, а во-втором — это уже текстовый элемент.
    Здесь же можно привести пример с frameset, script, title… и их разным поведением, но думаю, общий смысл ясен.

    Теперь, мы точно можем сделать вывод о том, что разбивка на токены не может быть верно осуществлена без построения хтмл дерева. А следовательно и парсинг хтмл не может быть осуществлён без, как минимум, двух стадий: токенизация и построение дерева.
    Что касается терминов: «строгий», «не соответствует спецификации», «light», «html 4» и тому подобные… Я уверен, что все эти термины можно смело заменить на один: «обрабатывает не правильно». Всё это абсурд.


    Как и что будем сравнивать?

    А вот тут самое интересное. Носить гордое звание хтмл парсера могут далеко не все, более того даже те кто именует себя токенизаторами хтмл, по сути, не являются таковыми.
    Выложив все парсеры на стол у меня сразу же возник вопрос, — кого с кем сравнить?

    А сравнивать будем правильные парсеры: myhtml, html5lib, html5ever, gumbo.

    Именно они соответствуют последней спецификации, и их результат будет совпадать с тем что мы может увидеть в современных браузерах.
    Неправильные парсеры (не соответствуют спецификации) могут сильно отличаться по скорости/памяти, но толку от этого более чем никакого, они просто неправильно обрабатывают документ.
    Никакие отговорки, вроде «парсер для html 4», приниматься во внимание не будут. Мир постоянно меняется, и за ним надо успевать.

    Стоит отметить, что html5ever не совсем правильный парсер. Авторы пишут, что он не проходит все тесты html5lin-test-tree-builder, то есть тесты на правильность построения дерева. В правильные парсеры он попал за старания быть правильным.
    Так же, на момент написания статьи html5lib не верно строит дерево для некоторого формата хтмл. Но это всё баги, которые я надеюсь, авторы исправят.

    Будем мерить время/память для 466 хтмл файлов — TOP500 alexa. 466, а не 500 потому, что не все сайты работают и не все отдают свой контент.

    Для каждой страницы будет создан fork со стадиями:
    1. Полная инициализация парсера
    2. Парсинг одной страницы
    3. Освобождение ресурсов

    Тек же будет тест «из жизни» — прогнать все страницы, по возможности, одним объектом. Происходить всё это будет последовательно.

    К тестам!

    До тестов у нас дошли: myhtml, html5ever, gumbo.

    К сожалению, html5lib вылетел из тестирования. Предварительный прогон показал, что он заметно медленнее остальных. Сравнивать его нет смысла, он написан на python и он медленный, очень медленный.

    MyHTML и Gumbo написаны на C. html5ever — это Rust. В расте я не силён, пока не силён, и поэтому попросил Алексея Вознюка помочь мне. Алексей согласился (респект и уважуха) и сделал сишную обёртку для тестирования парсера.

    Общий результат тестов времени выполнения:


    Общий результат тестов занимаемых ресурсов:



    Результаты тестов времени выполнения разбитые по 100


    Результаты тестов занимаемых ресурсов разбитые по 100

    Результат тестов «из жизни». Прогон всех (466) страниц в одно процессе:
    myhtml:
        Общее время: 0.50890; Память на старте: 1052672; Память по окончании: 32120832
    

    gumbo:
        Общее время: 6.12951; Память на старте: 1052672; Память по окончании: 29319168
    

    html5ever:
        Общее время: 4.50536; Память на старте: 1052672; Память по окончании: 30715904
    


    Итоги

    Бесспорный лидер по скорости — myhtml. gumbo — лидер по памяти, что не удивительно. html5ever показал себя, мягко говоря, никак. Он не быстр и по памяти не особо проявил себя, и использовать его можно только из Rust.

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

    Что использовалось

    Оборудование:
    Intel® Core(TM) i7-3615QM CPU @ 2.30GHz
    8 ГБ 1600 МГц DDR3

    Программное обеспечение:
    Darwin MBP-Alexander 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64
    Apple LLVM version 7.0.2 (clang-700.1.81)
    Target: x86_64-apple-darwin15.3.0
    Thread model: posix

    Ссылки

    Код Бенчмарка
    Картинки и CSV
    myhtml, gumbo, html5ever
    Обвязка для html5event от Алексея

    Спасибо за внимание!

    P.S. от автора myhtml

    Являясь автором myhtml мне морально сложно было делать подобное тестирование. Но я постарался крайне ответственно подойти к этому делу, и с каждым парсером работать как со своим.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 13

      0
      Для теста "из жизни" сколько времени заняло скачивание этих страниц?
        0
        Привет!

        Страницы были скачены заранее. Каждая страница загружается в память и потом отдается парсерам.
        Если конкретно ответить на вопрос — не могу сказать, замеры не делались.
          0
          Интересно посмотреть на порядок цифр. Предположим что в среднем загрузка страницы происходит за 0,5 сек. Загрузка всех страниц займет 233 сек.

          В целом получилось наглядная иллюстрация подхода: если где-то хотим ускорить, то где-то придется потратить память.
            0
            UPDATE:

            Простите, торопился и неверно прочитал коммент. Не совсем так, по памяти я гуляю и оптимизаций не делал. Там есть куда ужиматься и ускориться. Пока просто нет на это времени.
        0
        Было бы интересно добавить https://metacpan.org/pod/HTML::TreeBuilder раз уж разные языки тестите.
          0
          Привет!

          Я не уверен, что он обеспечивает покрытие спецификации HTML 5, а соответственно и сравнивать его с чем либо смысла нет.
          0
          Извиняюсь за глупый вопрос, но всё же интересно. А Ваш парсер может парсить HTML странички с JavaScript так, чтобы в результате получить дерево с учётом их работы? Т.е. если в HTML есть ссылка на JavaScript файл, который генерит код вставки картинки и прочего контента, то будет ли в дереве тег этого изображение вместе с остальными атрибутами (src, width, height, alt, title, ...) или будет только тег скрипта?
            0
            Привет!

            Вполне нормальный вопрос. Нет, это только парсер с заделом на будущее. То есть он делался с мыслью о связке с JavaScript движком. Этот функционал ожидается в будущем.
              0
              А есть ли такие уже сейчас? Желательно на C/C++
                0
                WebKit.

                Собственно ради этого и был начат проект myhtml. Расчет (быстрый расчёт) DOM на C без зависимостей (Render Tree). У меня не хватит времени и сил сделать JS engine, он будет опционально подключаем.
                  0
                  Из простого для быстрого старта можно использовать классы Qt WebKit(до 5.6) или Qt WebEngine. Из них можно получить конечный html код, который можно распарсить myhtml/gumb.
              0

              Отличный результат!


              Спасибо за исходники бенчмарка, пробую повторить. Подскажите, как вы строили результат? CSV-файлы получились, хочется график.

                0
                Привет!

                А вот графики я, можно так сказать, почти в ручную делал. Не помню точно, вроде на сайте plot.ly

              Only users with full accounts can post comments. Log in, please.