Парсинг веб-страниц с помощью C#
Прочитав этот пост, вы узнаете, как создать настоящий веб-парсер 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,"product_main")]/p[@class="price_color"]";
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, мощный и простой в использовании пакет. Это был простой пример, который можно улучшить дальше; например, вы можете попробовать добавить приведенную выше логику для обработки нескольких страниц в этот код.
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.