Парсинг HTML в C++: обзор инструментов и практические примеры
Парсинг HTML на C++ — задача, с которой сталкиваются разработчики при создании парсеров веб-страниц, анализаторов контента или инструментов для автоматизированного сбора данных. В отличие от Python или JavaScript, в C++ нет встроенной библиотеки для работы с HTML-документами, однако существует несколько зрелых и производительных решений. В этой статье мы рассмотрим основные библиотеки для парсинга HTML в C++, их особенности и приведём примеры кода.
Основные библиотеки для парсинга HTML в C++
Выбор библиотеки зависит от ваших задач: нужна ли поддержка CSS-селекторов, XPath, работа с повреждённым HTML или максимальная скорость. Рассмотрим самые популярные варианты.
- Gumbo-parser — библиотека от Google, реализующая парсинг HTML5 по спецификации. Она предоставляет DOM-дерево после разбора, но не включает встроенных средств для навигации по селекторам. Требует ручного обхода узлов.
- MyHTML — форк Gumbo с улучшенной поддержкой CSS-селекторов и более удобным API. Позволяет искать элементы по классам, тегам и атрибутам.
- Lexbor — современная библиотека с фокусом на скорость и соответствие стандартам. Поддерживает как HTML, так и CSS-селекторы, а также XPath (через дополнительный модуль).
- htmlcxx — простая библиотека для разбора HTML в дерево. Подходит для небольших проектов, но не поддерживает HTML5.
- Boost.PropertyTree (с XML-парсером) — может использоваться для разбора XHTML, но не предназначен для невалидного HTML.
Установка и подключение
Рассмотрим установку двух наиболее популярных библиотек: Gumbo-parser и MyHTML. Для проектов на C++17 рекомендуется использовать менеджер пакетов vcpkg или Conan.
// Установка через vcpkg
vcpkg install gumbo-parser
vcpkg install myhtml
// Подключение в CMakeLists.txt
find_package(gumbo REQUIRED)
target_link_libraries(your_project PRIVATE gumbo)
find_package(myhtml REQUIRED)
target_link_libraries(your_project PRIVATE myhtml)
Пример 1: Парсинг HTML с Gumbo-parser (ручной обход DOM)
В этом примере мы разберём простой HTML-документ и извлечём все тексты из тегов <p>.
#include <iostream>
#include <string>
#include <gumbo.h>
void extract_text(GumboNode* node) {
if (node->type == GUMBO_NODE_TEXT) {
std::cout << node->v.text.text;
return;
}
if (node->type == GUMBO_NODE_ELEMENT &&
node->v.element.tag == GUMBO_TAG_P) {
// Обрабатываем дочерние узлы
GumboVector* children = &node->v.element.children;
for (unsigned int i = 0; i < children->length; ++i) {
extract_text(static_cast<GumboNode*>(children->data[i]));
}
std::cout << std::endl;
return;
}
// Рекурсивный обход для других элементов
if (node->type == GUMBO_NODE_ELEMENT) {
GumboVector* children = &node->v.element.children;
for (unsigned int i = 0; i < children->length; ++i) {
extract_text(static_cast<GumboNode*>(children->data[i]));
}
}
}
int main() {
std::string html = R"(<html><body><p>Первый параграф</p><p>Второй параграф</p></body></html>)";
GumboOutput* output = gumbo_parse(html.c_str());
extract_text(output->root);
gumbo_destroy_output(&kGumboDefaultOptions, output);
return 0;
}
Пример 2: Использование MyHTML с CSS-селекторами
MyHTML предоставляет более удобный интерфейс для поиска элементов. В этом примере мы найдём все ссылки с классом link и выведем их атрибут href.
#include <iostream>
#include <myhtml/api.h>
int main() {
// Инициализация
myhtml_t* myhtml = myhtml_create();
myhtml_init(myhtml, MyHTML_OPTIONS_DEFAULT, 1, 0);
myhtml_tree_t* tree = myhtml_tree_create();
myhtml_tree_init(tree, myhtml);
std::string html = R"(
<html><body>
<a href="https://example.com" class="link">Example< Также в библиотеке
Работа с изображениями в Kotlin: библиотеки, инструменты и примеры
goose для Go: полное руководство с примерами
UUID для JavaScript (Node.js): полное руководство с примерами
Логирование в Go (Golang): лучшие библиотеки и решения для продакшена