Парсинг веб-страниц с помощью C#

  • Михаил
  • 8 мин. на прочтение
  • 35
  • 23 Feb 2024
  • 23 Feb 2024

Прочитав этот пост, вы узнаете, как создать настоящий веб-парсер C#. Имейте в виду, что даже если вы используете C#, вы сможете адаптировать эту информацию для всех языков, поддерживаемых платформой .NET, включая VB.NET и F#.

Создание веб-парсера с помощью C#

Как уже упоминалось, вы узнаете, как написать общедоступный код очистки веб-страниц на C# с помощью пакета HTML Agility Pack. В этом руководстве мы будем использовать .NET 5 SDK с кодом Visual Studio. Этот код был протестирован с .NET Core 3 и .NET 5, и он должен работать с другими версиями .NET.

Мы будем следовать гипотетическому сценарию: обыскать книжный магазин  и собрать названия книг и цены. Давайте настроим среду разработки, прежде чем писать веб-сканер C#.

Настройка среды разработки

Для среды разработки C# установите Visual Studio Code . Обратите внимание, что Visual Studio и Visual Studio Code — это два совершенно разных приложения, если вы используете их для написания кода C#.

После установки кода Visual Studio установите .NET 5.0  или новее. Вы также можете использовать .NET Core 3.1. После завершения установки откройте терминал и выполните следующую команду, чтобы убедиться, что .NET CLI или интерфейс командной строки работают правильно:

dotnet --version

Это должно вывести номер версии установленного .NET.

Структура проекта и зависимости

Код будет частью проекта .NET. Для простоты создайте консольное приложение. Затем создайте папку, в которую вы хотите записать код C#. Откройте терминал и перейдите в эту папку. Теперь введите эту команду:

dotnet new console

Вывод этой команды должен подтвердить, что консольное приложение было успешно создано.

Теперь пришло время установить необходимые пакеты. Если вы хотите использовать C# для очистки общедоступных веб-страниц, хорошим выбором будет HTML Agility Pack. Вы можете установить его для этого проекта с помощью этой команды:

dotnet add package HtmlAgilityPack

Вам следует установить еще один пакет, чтобы можно было легко экспортировать очищенные данные в файл CSV:

dotnet add package CsvHelper

Если вы используете Visual Studio вместо Visual Studio Code, нажмите «Файл», выберите «Новое решение» и нажмите «Консольное приложение». Чтобы установить зависимости, выполните следующие действия:

  • Выберите «Проект»;
  • Нажмите «Управление зависимостями проекта». Откроется окно «Пакеты NuGet»;
  • Найдите HtmlAgilityPack и выберите его;
  • Наконец, найдите CsvHelper, выберите его и нажмите «Добавить пакеты».

Теперь, когда пакеты установлены, вы можете перейти к написанию кода для веб-скрапинга книжного магазина.

Загрузка и анализ веб-страниц

Первым шагом любой программы очистки веб-страниц является загрузка HTML-кода веб-страницы. Этот HTML-код будет строкой, которую вам нужно будет преобразовать в объект, который можно будет обрабатывать дальше. Последняя часть называется синтаксическим анализом. Html Agility Pack может читать и анализировать файлы из локальных файлов, строк HTML, любого URL-адреса или даже из браузера.

В этом случае вам нужно только получить HTML из URL-адреса. Вместо использования собственных функций .NET Html Agility Pack предоставляет удобный класс — HtmlWeb . Этот класс предлагает функцию загрузки  , которая может принимать URL-адрес и возвращать экземпляр класса HtmlDocument  , который также является частью используемого нами пакета. Используя эту информацию, вы можете написать функцию, которая принимает URL-адрес и возвращает экземпляр HtmlDocument .

Откройте файл Program.cs  и введите эту функцию в класс Program :

// Parses the URL and returns HtmlDocument object
static HtmlDocument GetDocument(string url)
{
    HtmlWeb web = new HtmlWeb();
    HtmlDocument doc = web.Load(url);
    return doc;
}

На этом первый шаг кода завершен. Следующий шаг — анализ документа.

Парсинг HTML: получение ссылок на книги

В этой части кода вы извлечете необходимую информацию с веб-страницы. На этом этапе документ теперь является объектом типа HtmlDocument . Этот класс предоставляет две функции для выбора элементов. Обе функции принимают XPath в качестве входных данных и возвращают HtmlNode  или HtmlNodeCollection . Вот сигнатура этих двух функций:

public HtmlNodeCollection SelectNodes(string xpath);
public HtmlNode SelectSingleNode(string xpath);

Давайте сначала обсудим SelectNodes  .

В этом примере — веб-скребок C# — вы соберете все сведения о книге с этой страницы . Во-первых, его нужно разобрать, чтобы извлечь все ссылки на книги. Для этого откройте эту страницу в браузере, щелкните правой кнопкой мыши любую ссылку на книгу и нажмите «Проверить». Откроется Инструменты разработчика.

После некоторого понимания разметки ваш XPath для выбора должен выглядеть примерно так:

//h3/a

Теперь этот XPath можно передать в функцию SelectNodes  .

HtmlDocument doc = GetDocument(url);
HtmlNodeCollection linkNodes = doc.DocumentNode.SelectNodes("//h3/a");

Обратите внимание, что атрибут DocumentNode  объекта HtmlDocument  вызывает функцию SelectNodes  .

Переменная linkNodes  представляет собой коллекцию. Вы можете написать над ним цикл foreach  и получать href  из каждой ссылки одну за другой. Есть одна маленькая проблема, о которой вам нужно позаботиться: ссылки на странице относительны. Следовательно, их необходимо преобразовать в абсолютный URL-адрес, прежде чем вы сможете очистить эти извлеченные ссылки.

Для преобразования относительных URL-адресов вы можете использовать класс Uri  . Вы можете использовать этот конструктор для получения объекта Uri  с абсолютным URL-адресом.

Uri(Uri baseUri, string? relativeUri);

Получив объект Uri  , вы можете просто проверить свойство AbsoluteUri , чтобы получить полный URL-адрес. 

Запишите все это в функции, чтобы код был организован.

static List<string> GetBookLinks(string url)
    {
        var bookLinks = new List<string>();
        HtmlDocument doc = GetDocument(url);
        HtmlNodeCollection linkNodes = doc.DocumentNode.SelectNodes("//h3/a");
        var baseUri = new Uri(url);
        foreach (var link in linkNodes)
        {
            string href = link.Attributes["href"].Value;
            bookLinks.Add(new Uri(baseUri, href).AbsoluteUri);
        }
        return bookLinks;
    }

 В этой функции вы начинаете с пустого объекта List<string> . В цикле foreach  вам нужно добавить все ссылки на этот объект и вернуть его.

Теперь пришло время изменить функцию Main()  , чтобы вы могли протестировать написанный вами код C#. Измените функцию так, чтобы она выглядела так:

static void Main(string[] args)
{
    var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");
    Console.WriteLine("Found {0} links", bookLinks.Count);
}

Чтобы запустить этот код, откройте терминал, перейдите в каталог, содержащий этот файл, и введите следующее:

dotnet run

Вывод должен быть следующим:

Found 20 links

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

Парсинг HTML: получение сведений о книге

На этом этапе у вас должен быть список строк, содержащих URL-адреса книг. Вы можете просто написать цикл, который будет получать документ, используя  уже написанную вами функцию GetDocument . После этого вы воспользуетесь функцией SelectSingleNode  , чтобы извлечь название и цену книги.

Чтобы данные были организованы, начнем с класса. Этот класс будет представлять книгу. У этого класса будет два свойства — Title  и Price . Это выглядит так:

public class Book
{
    public string Title { get; set; }
    public string Price { get; set; }
}

Теперь откройте страницу книги в браузере и создайте XPath для названия — //h1 . Создать XPath для цены немного сложнее, поскольку к дополнительным книгам внизу применяется тот же класс.

XPath цены будет таким:

//div[contains(@class,"product_main")]/p[@class="price_color"]

Обратите внимание, что XPath содержит двойные кавычки. Вам придется экранировать эти символы, поставив перед ними обратную косую черту.

Теперь вы можете использовать функцию SelectSingleNode  для получения узла, а затем использовать свойство InnerText  для получения текста, содержащегося в элементе. Вы можете организовать все в функции следующим образом:

static List<Book> GetBookDetails(List<string> urls)
{
    var books = new List<Book>();
    foreach (var url in urls)
    {
        HtmlDocument document = GetDocument(url);
        var titleXPath = "//h1";
        var priceXPath = "//div[contains(@class,&quot;product_main&quot;)]/p[@class=&quot;price_color&quot;]";
        var book = new Book();
        book.Title = document.DocumentNode.SelectSingleNode(titleXPath).InnerText;
        book.Price = document.DocumentNode.SelectSingleNode(priceXPath).InnerText;
        books.Add(book);
    }
    return books;
}
This function will return a list of Book objects. It's time to update the Main() function as well:
static void Main(string[] args)
{
    var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");
    Console.WriteLine("Found {0} links", bookLinks.Count);
    var books = GetBookDetails(bookLinks);
}

Заключительная часть этого проекта парсинга веб-страниц — экспорт данных в CSV.

Экспорт данных

Если вы еще не установили CsvHelper , вы можете сделать это, выполнив команду dotnet  add package CsvHelper  из терминала.

Функция экспорта довольно проста. Сначала вам нужно создать StreamWriter и  отправить имя CSV-файла в качестве параметра. Далее вам следует использовать этот объект для создания CsvWriter . Наконец, вы можете использовать функцию WriteRecords , чтобы записать все книги всего в одной строке кода. 

Чтобы убедиться, что все ресурсы закрыты правильно, вы можете использовать блок using . Вы также можете обернуть все в функцию следующим образом: 

static void exportToCSV(List<Book> books)
{
    using (var writer = new StreamWriter("./books.csv"))
    using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
    {
        csv.WriteRecords(books);
    }
}
Finally, you can call this function from the Main() function:
static void Main(string[] args)
{
    var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");
    var books = GetBookDetails(bookLinks);
    exportToCSV(books);
}

Вот и все! Чтобы запустить этот код, откройте терминал и выполните следующую команду:

dotnet run

Через несколько секунд у вас будет создан файл book.csv  .

Краткое содержание

Вы можете использовать несколько пакетов для написания веб-скрейпера на C#.  В этом посте мы показали, как использовать HTML Agility Pack, мощный и простой в использовании пакет. Это был простой пример, который можно улучшить дальше; например, вы можете попробовать добавить приведенную выше логику для обработки нескольких страниц в этот код.