Парсинг HTML в C++: Обзор библиотек Gumbo, MyHTML и htmlcxx

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Парсинг HTML в C++: Обзор лучших библиотек

Разбор (парсинг) HTML-документов — одна из ключевых задач при разработке парсеров, веб-скраперов и инструментов анализа данных. Несмотря на то, что C++ не является самым популярным языком для таких задач (уступая Python или JavaScript), существуют мощные и высокопроизводительные библиотеки, позволяющие эффективно работать с HTML. В этой статье мы рассмотрим три наиболее зрелых решения: Gumbo-parser от Google, MyHTML и htmlcxx. Разберём их API, особенности и приведём практические примеры кода.

1. Gumbo-parser (Google)

Gumbo — это библиотека для парсинга HTML5, разработанная инженерами Google. Она полностью соответствует спецификации WHATWG HTML5 и отличается высокой устойчивостью к "грязному" HTML-коду. Основные преимущества:

  • Чистый C API без внешних зависимостей.
  • Поддержка всех элементов HTML5, включая SVG и MathML.
  • Возвращает полное DOM-дерево с корректной вложенностью.
  • Высокая скорость работы (нативные бенчмарки показывают до 50 МБ/с).

Пример использования Gumbo-parser

В этом примере мы извлечём все ссылки (<a>) из HTML-документа:

#include <iostream>
#include <string>
#include "gumbo.h"

void extract_links(GumboNode* node) {
    if (node->type != GUMBO_NODE_ELEMENT) return;

    GumboAttribute* href = nullptr;
    if (node->v.element.tag == GUMBO_TAG_A) {
        href = gumbo_get_attribute(&node->v.element.attributes, "href");
        if (href) {
            std::cout << "Найдена ссылка: " << href->value << std::endl;
        }
    }

    // Рекурсивный обход дочерних элементов
    const GumboVector* children = &node->v.element.children;
    for (unsigned int i = 0; i < children->length; ++i) {
        extract_links(static_cast<GumboNode*>(children->data[i]));
    }
}

int main() {
    std::string html = "<html><body><a href='https://example.com'>Пример</a></body></html>";
    GumboOutput* output = gumbo_parse(html.c_str());
    extract_links(output->root);
    gumbo_destroy_output(&kGumboDefaultOptions, output);
    return 0;
}

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

2. MyHTML — быстрый и лёгкий парсер

MyHTML — это библиотека, написанная на C с прицелом на максимальную производительность. Она не строит полное DOM-дерево, а использует SAX-подобный подход (последовательный обход токенов). Это делает её идеальной для задач, где нужно быстро извлечь данные без построения сложной иерархии.

Ключевые особенности MyHTML

  • Модульная архитектура: парсер + сериализатор + потоковый API.
  • Поддержка кодировок (UTF-8, Windows-1251, KOI8-R и др.).
  • Возможность работы с фрагментами HTML (без обязательного <html>).
  • Низкое потребление памяти — не хранит всё дерево.

Пример: подсчёт всех тегов <div> с классом "content"

#include <iostream>
#include "myhtml/api.h"

int main() {
    // Инициализация парсера
    myhtml_t* myhtml = myhtml_create();
    myhtml_init(myhtml, MyHTML_OPTIONS_DEFAULT, 1, 0);

    // Парсинг HTML
    const char* html = "<div class='content'>Блок 1</div><div>Блок 2</div>";
    myhtml_tree_t* tree = myhtml_tree_create();
    myhtml_tree_init(tree, myhtml);
    myhtml_parse(tree, MyENCODING_UTF_8, html, strlen(html));

    // Поиск элементов по CSS-селектору
    myhtml_collection_t* collection = myhtml_collection_create(1, 1);
    myhtml_css_match_selector(tree, tree->node, "div.content", collection, NULL);

    std::cout << "Найдено элементов: " << collection->length << std::endl;

    // Очистка
    myhtml_collection_destroy(collection);
    myhtml_tree_destroy(tree);
    myhtml_destroy(myhtml);
    return 0                    

Рекомендаций