HtmlAgilityPack для C# (.NET): полное руководство с примерами

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

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

Начать курс

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",                    

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