HtmlAgilityPack для C# (.NET): полное руководство с примерами
1. Что это и зачем нужна
HtmlAgilityPack — это мощная библиотека для парсинга HTML-документов в экосистеме .NET (C#, VB.NET, F#). Она предоставляет разработчикам удобный API для навигации, поиска, извлечения и модификации HTML-кода. В отличие от стандартных XML-парсеров, HtmlAgilityPack специально спроектирована для работы с "грязным" HTML, который часто встречается в реальном вебе: незакрытые теги, неправильные атрибуты, вложенные ошибки и т.д. Библиотека автоматически исправляет такие дефекты, создавая корректное DOM-дерево.
Зачем она нужна? Основные сценарии использования включают: веб-скрапинг (извлечение данных с сайтов), автоматизацию тестирования веб-интерфейсов, миграцию контента между CMS, анализ HTML-отчетов, очистку и нормализацию HTML-кода, а также создание собственных HTML-генераторов. Благодаря поддержке XPath и CSS-селекторов (через расширение), библиотека позволяет гибко и точно выбирать нужные элементы.
HtmlAgilityPack активно поддерживается сообществом, имеет открытый исходный код и совместима с .NET Framework 4.5+, .NET Core 2.0+, .NET 5/6/7/8, а также с Xamarin и Unity. Это делает её универсальным инструментом для любого .NET-разработчика, работающего с HTML.
2. Установка
Установка библиотеки осуществляется через NuGet Package Manager. Выполните одну из следующих команд в консоли диспетчера пакетов (Package Manager Console) или в терминале .NET CLI:
// Через .NET CLI
dotnet add package HtmlAgilityPack
// Через Package Manager Console
Install-Package HtmlAgilityPack
// Или через NuGet UI: ищем "HtmlAgilityPack" и устанавливаем последнюю стабильную версию
После установки добавьте using-директиву в начало файла:
using HtmlAgilityPack;
3. Быстрый старт — минимальный рабочий пример
Рассмотрим простейший пример: загрузим HTML-строку, найдём все ссылки и выведем их текст и URL.
using System;
using HtmlAgilityPack;
class Program
{
static void Main()
{
// 1. Создаём HTML-документ
var htmlDoc = new HtmlDocument();
// 2. Загружаем HTML (в реальности может быть из файла или веба)
string html = @"
";
htmlDoc.LoadHtml(html);
// 3. Выбираем все теги <a> с помощью XPath
var links = htmlDoc.DocumentNode.SelectNodes("//a");
// 4. Проходим по каждому и выводим текст и href
if (links != null)
{
foreach (var link in links)
{
string text = link.InnerText.Trim();
string href = link.GetAttributeValue("href", "");
Console.WriteLine($"Текст: {text}, URL: {href}");
}
}
}
}
Результат выполнения:
Текст: Example, URL: https://example.com
Текст: PythonLib, URL: https://pythonlib.ru
4. Основные классы и методы
Ниже представлены ключевые классы и методы HtmlAgilityPack с подробным описанием и примерами.
4.1. HtmlDocument — основной класс для работы с документом
Сигнатура: public class HtmlDocument
Описание: Представляет целый HTML-документ. Позволяет загружать HTML из строки, файла или потока, а также выполнять поиск и модификацию узлов.
var doc = new HtmlDocument();
doc.LoadHtml("<html><body><p>Hello</p></body></html>");
var root = doc.DocumentNode; // Получаем корневой узел
4.2. HtmlNode — узел HTML-дерева
Сигнатура: public class HtmlNode
Описание: Представляет отдельный HTML-элемент (тег) или текстовый узел. Содержит свойства для доступа к родительскому узлу, дочерним узлам, атрибутам и текстовому содержимому.
var doc = new HtmlDocument();
doc.LoadHtml("<div class='container'><p>Text</p></div>");
var div = doc.DocumentNode.SelectSingleNode("//div");
Console.WriteLine(div.Name); // div
Console.WriteLine(div.GetAttributeValue("class", "")); // container
Console.WriteLine(div.InnerText); // Text
4.3. SelectNodes — выбор нескольких узлов по XPath
Сигнатура: public HtmlNodeCollection SelectNodes(string xpath)
Описание: Выполняет XPath-запрос и возвращает коллекцию узлов, соответствующих выражению. Если узлов нет, возвращает null.
var doc = new HtmlDocument();
doc.LoadHtml("<ul><li>A</li><li>B</li><li>C</li></ul>");
var items = doc.DocumentNode.SelectNodes("//li");
if (items != null)
{
foreach (var item in items)
Console.WriteLine(item.InnerText);
}
4.4. SelectSingleNode — выбор одного узла по XPath
Сигнатура: public HtmlNode SelectSingleNode(string xpath)
Описание: Возвращает первый узел, соответствующий XPath-выражению. Если узел не найден, возвращает null.
var doc = new HtmlDocument();
doc.LoadHtml("<div id='main'><p>First</p><p>Second</p></div>");
var firstParagraph = doc.DocumentNode.SelectSingleNode("//p");
Console.WriteLine(firstParagraph?.InnerText); // First
4.5. GetAttributeValue — получение значения атрибута
Сигнатура: public string GetAttributeValue(string name, string def)
Описание: Возвращает значение указанного атрибута узла. Если атрибут отсутствует, возвращает значение по умолчанию def.
var doc = new HtmlDocument();
doc.LoadHtml("<a href='https://pythonlib.ru' class='link'>PythonLib</a>");
var link = doc.DocumentNode.SelectSingleNode("//a");
string href = link.GetAttributeValue("href", "");
string target = link.GetAttributeValue("target", "_self");
Console.WriteLine($"href: {href}, target: {target}"); // href: https://pythonlib.ru, target: _self
4.6. InnerText — получение текстового содержимого
Сигнатура: public string InnerText { get; }
Описание: Возвращает объединённый текст всех дочерних текстовых узлов, без HTML-тегов.
var doc = new HtmlDocument();
doc.LoadHtml("<p>Hello <b>World</b>!</p>");
var p = doc.DocumentNode.SelectSingleNode("//p");
Console.WriteLine(p.InnerText); // Hello World!
4.7. InnerHtml — получение HTML-содержимого узла
Сигнатура: public string InnerHtml { get; set; }
Описание: Возвращает или задаёт HTML-код, содержащийся внутри узла (без самого узла).
var doc = new HtmlDocument();
doc.LoadHtml("<div><p>Text</p></div>");
var div = doc.DocumentNode.SelectSingleNode("//div");
Console.WriteLine(div.InnerHtml); // <p>Text</p>
4.8. OuterHtml — получение полного HTML узла
Сигнатура: public string OuterHtml { get; }
Описание: Возвращает полный HTML-код узла, включая его самого и всё содержимое.
var doc = new HtmlDocument();
doc.LoadHtml("<div id='test'>Content</div>");
var div = doc.DocumentNode.SelectSingleNode("//div");
Console.WriteLine(div.OuterHtml); // <div id="test">Content</div>
4.9. Load/ LoadHtml — загрузка HTML
Сигнатура: public void Load(string path) / public void LoadHtml(string html)
Описание: Load загружает HTML из файла или URL (через WebClient). LoadHtml загружает из строки.
// Из строки
var doc1 = new HtmlDocument();
doc1.LoadHtml("<html><body><p>Hello</p></body></html>");
// Из файла
var doc2 = new HtmlDocument();
doc2.Load("C:\\temp\\page.html");
// Из URL (требуется using System.Net)
using (var webClient = new WebClient())
{
string html = webClient.DownloadString("https://pythonlib.ru");
var doc3 = new HtmlDocument();
doc3.LoadHtml(html);
}
4.10. Save — сохранение HTML
Сигнатура: public void Save(string path) / public void Save(Stream stream)
Описание: Сохраняет текущее состояние документа в файл или поток.
var doc = new HtmlDocument();
doc.LoadHtml("<html><body><p>Modified</p></body></html>");
doc.Save("output.html"); // Сохраняем в файл
4.11. Descendants — получение всех потомков
Сигнатура: public IEnumerable<HtmlNode> Descendants(string name = null)
Описание: Возвращает все узлы-потомки текущего узла. Можно указать имя тега для фильтрации.
var doc = new HtmlDocument();
doc.LoadHtml("<div><span>1</span><span>2</span></div>");
var spans = doc.DocumentNode.Descendants("span");
foreach (var span in spans)
Console.WriteLine(span.InnerText); // 1, 2
4.12. CreateElement — создание нового узла
Сигнатура: public HtmlNode CreateElement(string name)
Описание: Создаёт новый HTML-элемент с указанным именем тега. Затем его можно добавить в дерево документа.
var doc = new HtmlDocument();
var newDiv = doc.CreateElement("div");
newDiv.SetAttributeValue("class", "new-class");
newDiv.InnerHtml = "<p>New content</p>";
doc.DocumentNode.AppendChild(newDiv);
Console.WriteLine(doc.DocumentNode.OuterHtml); // <div class="new-class"><p>New content</p></div>
5. Реальный практический пример (100-200 строк кода)
Представим задачу: нам нужно спарсить страницу каталога книг с сайта-агрегатора (например, books.toscrape.com). Извлечём название книги, цену, рейтинг и ссылку на детальную страницу. Результат сохраним в CSV-файл.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using HtmlAgilityPack;
class BookScraper
{
public class BookInfo
{
public string Title { get; set; }
public decimal Price { get; set; }
public int Rating { get; set; } // от 1 до 5
public string DetailUrl { get; set; }
}
static void Main()
{
string url = "https://books.toscrape.com/catalogue/page-1.html";
var books = ScrapeBooks(url);
SaveToCsv(books, "books.csv");
Console.WriteLine($"Спарсено {books.Count} книг. Результат сохранён в books.csv");
}
static List ScrapeBooks(string url)
{
var books = new List();
using (var client = new WebClient() { Encoding = Encoding.UTF8 })
{
string html = client.DownloadString(url);
var doc = new HtmlDocument();
doc.LoadHtml(html);
// XPath для всех книг на странице
var bookNodes = doc.DocumentNode.SelectNodes("//article[@class='product_pod']");
if (bookNodes == null) return books;
foreach (var node in bookNodes)
{
var book = new BookInfo();
// Название книги (в атрибуте title у ссылки)
var titleNode = node.SelectSingleNode(".//h3/a");
book.Title = titleNode?.GetAttributeValue("title", Также в библиотеке
Отправка email на Kotlin: библиотеки, примеры и настройка SMTP
charm/bubbletea для Go: полное руководство с примерами
Планировщик задач PHP: полное руководство по cron-задачам и Task Scheduler
GraphQL библиотека для Go (Golang) — полное руководство
Elasticsearch REST клиент для Kotlin: руководство по использованию