Парсинг и скрапинг данных на C#: когда Python не единственный выбор
Если вы привыкли, что парсинг — это прерогатива Python с его BeautifulSoup и Scrapy, то у меня для вас сюрприз. C# в мире веб-скрапинга — это как швейцарский нож: мощный, надёжный и с кучей лезвий, о которых вы даже не подозревали. Парсинг на C# открывает двери к интеграции с экосистемой .NET, высокопроизводительным приложениям и строгой типизации, которая спасает от глупых ошибок во время сборки.
В этой статье я покажу, как использовать C# для сбора данных, разберу реальные примеры кода и расскажу, почему ваш следующий парсер стоит писать именно на C#, даже если вы раньше об этом не задумывались.
Почему C#, а не Python? Три железных аргумента
Да, Python — король прототипирования. Но когда речь заходит о промышленном скрапинге данных на C#, вы получаете:
- Производительность: C# компилируется в машинный код (через JIT или AOT), что даёт прирост скорости в 2-5 раз на парсинге больших объёмов данных.
- Типобезопасность: Вы никогда не передадите число туда, где ожидается строка. Компилятор поймает ошибку до запуска.
- Экосистема .NET: Парсер на C# легко встраивается в ASP.NET, WPF, консольные утилиты и даже в Unity (да, можно парсить данные из игры).
А главное — C# отлично справляется с задачами, где нужен многопоточный парсинг, работа с базами данных и сложная логика извлечения информации.
Инструментарий: выбираем библиотеку для парсинга на C#
Парсинг сайтов на C# начинается с выбора правильного инструмента. Я выделю три главных подхода, которые покрывают 95% задач веб-скрапинга.
HtmlAgilityPack: классика парсинга HTML
Это самая популярная библиотека для парсинга на C#. Она превращает HTML в древовидную структуру, с которой удобно работать через XPath или LINQ. Идеально подходит для статических страниц.
using HtmlAgilityPack;
var web = new HtmlWeb();
var doc = web.Load("https://example.com/articles");
// Выбираем все заголовки h2 с классом "title"
var titles = doc.DocumentNode
.SelectNodes("//h2[@class='title']")
.Select(node => node.InnerText.Trim())
.ToList();
foreach (var title in titles)
{
Console.WriteLine($"Найден заголовок: {title}");
}
Этот код загружает страницу, парсит её и собирает заголовки. HtmlAgilityPack умеет работать с кривым HTML, исправляя незакрытые теги — что часто встречается на реальных сайтах.
AngleSharp: современная альтернатива
AngleSharp — это более свежая библиотека, которая поддерживает CSS-селекторы (как в jQuery) и работает с DOM-моделью. Парсинг данных на C# с AngleSharp выглядит естественно для тех, кто знаком с JavaScript.
using AngleSharp;
using AngleSharp.Html.Parser;
var context = BrowsingContext.New(Configuration.Default);
var parser = context.GetService<IHtmlParser>();
var document = await parser.ParseDocumentAsync(htmlString);
var prices = document.QuerySelectorAll(".product-price")
.Select(el => el.TextContent.Trim())
.ToList();
AngleSharp отлично подходит, когда нужно не просто собрать данные, но и выполнить манипуляции с DOM — например, подменить атрибуты перед сохранением.
Selenium WebDriver: для динамических сайтов
Когда сайт грузит контент через JavaScript (React, Vue, Angular), обычный парсер не поможет. Тут в игру вступает Selenium. Он эмулирует работу браузера и позволяет скрапить данные после полной загрузки страницы.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using (var driver = new ChromeDriver())
{
driver.Navigate().GoToUrl("https://example.com/dynamic-page");
// Ждём, пока элемент появится
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElement(By.ClassName("loaded-data")));
var data = driver.FindElement(By.ClassName("loaded-data")).Text;
Console.WriteLine(data);
}
Selenium — тяжёлая артиллерия. Он запускает полноценный браузер, поэтому потребляет больше ресурсов, но зато гарантированно получает данные из любого SPA-приложения.
Практический пример: парсинг интернет-магазина на C#
Давайте соберём реальный проект. Допустим, нам нужно спарсить названия и цены товаров с каталога. Скрапинг данных на C# мы реализуем с помощью HtmlAgilityPack и добавим обработку ошибок.
Шаг 1: Загрузка и парсинг страницы
public class ProductParser
{
private static readonly HtmlWeb Web = new HtmlWeb();
public List<Product> ParsePage(string url)
{
var doc = Web.Load(url);
var products = new List<Product>();
var productNodes = doc.DocumentNode.SelectNodes("//div[@class='product-item']");
if (productNodes == null) return products;
foreach (var node in productNodes)
{
var name = node.SelectSingleNode(".//h3/a")?.InnerText.Trim() ?? "Нет названия";
var priceText = node.SelectSingleNode(".//span[@class='price']")?.InnerText.Trim();
decimal price = 0;
decimal.TryParse(priceText?.Replace("₽", "").Replace(" ", ""), out price);
products.Add(new Product { Name = name, Price = price });
}
return products;
}
}
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
Шаг 2: Сохранение результатов в CSV
using System.IO;
using CsvHelper;
using System.Globalization;
var parser = new ProductParser();
var products = parser.ParsePage("https://shop.example.com/catalog");
using (var writer = new StreamWriter("products.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(products);
}
Console.WriteLine($"Сохранено {products.Count} товаров.");
Этот код загружает страницу, извлекает данные и сохраняет их в структурированный файл. Парсинг на C# становится простым и предсказуемым.
Как обойти блокировки и не сломать сайт
Веб-скрапинг на C# требует уважения к целевым ресурсам. Вот три правила, которые спасут вас от бана:
- Добавьте User-Agent: Многие сайты блокируют запросы без заголовка браузера. Укажите реальный User-Agent от Chrome или Firefox.
- Используйте задержки: Не долбите сервер 100 запросами в секунду. Добавьте Thread.Sleep(1000) между запросами.
- Прокси и ротация IP: Для серьёзных проектов используйте пул прокси. Библиотека
Pollyпоможет с