Учебное пособие по веб-скрапингу с помощью C#

В этом уроке вы узнаете, как создать веб-скрапер на C#. В нем будет детально показано, как выполнить HTTP-запрос для загрузки веб-страницы, которую вы хотите использовать, выбрать нужные HTML-элементы из ее DOM-дерева, и извлечь из них данные.
5 min read
Web scraping with C# guide featured image

В этом обширном руководстве мы обсудим:

Лучшие библиотеки для веб-скрапинга на C#

Веб-скрапинг становится проще, если использовать правильные инструменты. Давайте рассмотрим лучшие библиотеки NuGet для скрапинга на C#:  

  • HtmlAgilityPack: самая популярная библиотека скраперов на C#. HtmlAgilityPack предоставляет вам возможность загружать веб-страницы, анализировать их HTML-содержимое, выбирать HTML-элементы и извлекать из них нужные данные.  
  • HttpClient: самый популярный HTTP-клиент на языке C#. HttpClient особенно полезен, когда речь идет о веб-краулинге, поскольку позволяет выполнять HTTP-запросы легко и асинхронно.  
  • Selenium WebDriver — библиотека, которая поддерживает несколько языков программирования и позволяет писать автоматизированные тесты для веб-приложений. Вы также можете использовать ее для веб-скрапинга.  
  • Puppeteer Sharp — C#-порт Puppeteer. Puppeteer Sharp предоставляет возможности безголового браузера и позволяет использовать для веб-скрапинга страницы с динамическим контентом.  

В этом руководстве будет показано, как выполнить веб-скрапинг с использованием C#, а также HtmlAgilityPack и Selenium.  

Необходимые предварительные установки для веб-скрапинга с помощью C#

Прежде чем написать первую строчку кода вашего веб-скрапера на C#, вам необходимо выполнить некоторые предварительные условия:

  • Visual Studio: подойдет бесплатная Community-версия Visual Studio 2022.  
  • .NET 6+: подойдет любая версия LTS начиная с шестой.  

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

Теперь вы готовы к созданию проекта веб-скрапинга на языке C# в Visual Studio.

Настройка проекта в Visual Studio

Откройте Visual Studio и нажмите на опцию «Create a new project».

Создание нового проекта в VS
Создание нового проекта в VS

В окне «Create a new project» выберите опцию «C#» из выпадающего списка. После указания языка программирования выберите шаблон «Console App» и нажмите кнопку «Next».

Выбор шаблона приложения Console App
Выбор шаблона приложения Console App

Затем назовите свой проект StaticWebScraping, нажмите «Select» и выберите версию .NET. Если вы установили .NET 6.0, Visual Studio уже должна выбрать ее за вас.  

Выбор версии .NET
Выбор версии .NET

Нажмите кнопку «Create», чтобы инициализировать ваш проект веб-скрапинга на C#. Visual Studio инициализирует папку StaticWebScraping, содержащую файл App.cs. В этом файле будет храниться логика веб-скрапинга на C#:  

namespace WebScraping {
    public class Program {
            public static void Main() {
               // scraping logic...               
            }
    }
}

Пришло время понять, как создать веб-скрапер на C#!

Веб-скрапинг сайтов со статическим контентом с помощью C#

На статических сайтах контент веб-страниц хранится в возвращаемых сервером HTML-документах. Это означает, что веб-страница со статическим содержимым не выполняет XHR-запросы для получения данных и не требует выполнения JavaScript.  

Скрапинг статических сайтов довольно прост. Все, что вам нужно сделать, это:

  1. Установите библиотеку C# для веб-скрапинга
  2. Загрузите целевую веб-страницу и спарсите ее HTML-документ
  3. Используйте библиотеку веб-скрапинга для выбора интересующих вас элементов HTML
  4. Извлеките из них данные

Давайте применим все эти шаги к странице Википедии «List of SpongeBob SquarePants episodes»:  

Список эпизодов «Губка Боб Квадратные Штаны» в Википедии
Список эпизодов «Губка Боб Квадратные Штаны» в Википедии

Цель веб-скрапера на C#, который вы собираетесь создать, — автоматически получить все данные об эпизодах со статической страницы Википедии.

Приступим!

Шаг 1: Установите HtmlAgilityPack

HtmlAgilityPack — библиотека C# с открытым исходным кодом, которая позволяет парсить HTML-документы, выбирать элементы из DOM и извлекать из них данные. В принципе, HtmlAgilityPack предлагает все необходимое для веб-скрапинга данных статического сайта.  

Чтобы установить его, щелкните правой кнопкой мыши на опции «Dependencies» под именем вашего проекта в «Solution Explorer». Затем выберите «Manage NuGet Packages». В окне менеджера пакетов NuGet найдите «HtmlAgilityPack» и нажмите кнопку «Install» в правой части экрана.

Установка HtmlAgilityPack для веб-скрапинга на C#
Установка HtmlAgilityPack для веб-скрапинга на C#

Во всплывающем окне вас спросят, согласны ли вы внести изменения в ваш проект. Нажмите «OK», чтобы установить HtmlAgilityPack. Теперь вы готовы к выполнению веб-скрапинга с помощью C# на статическом сайте.

Теперь добавьте следующую строку в верхнюю часть файла App.cs, чтобы импортировать HtmlAgilityPack:  

using HtmlAgilityPack;

Шаг 2: Загрузка веб-страницы

Вы можете подключиться к целевой веб-странице с помощью HtmlAgilityPack следующим образом:

// the URL of the target Wikipedia page
string url = "https://en.wikipedia.org/wiki/List_of_SpongeBob_SquarePants_episodes";

var web = new HtmlWeb();
// downloading to the target page
// and parsing its HTML content
var document = web.Load(url);

Экземпляр класса HtmlWeb позволяет загрузить веб-страницу благодаря своему методу Load(). За кулисами этот метод выполняет запрос HTTP GET для получения HTML-документа, связанного с URL, переданным в качестве параметра. Затем Load() возвращает экземпляр HtmlAgilityPack HtmlDocument, который можно использовать для выбора элементов HTML на странице.  

Шаг 3: Выберите элементы HTML

Вы можете выбирать элементы HTML на веб-странице с помощью селекторов XPath. XPath позволяет выбрать один или несколько определенных элементов DOM. Чтобы получить селектор XPath, связанный с элементом HTML, щелкните по нему правой кнопкой мышки, откройте инструменты разработчика в браузере, убедитесь, что он относится к интересующему вас элементу DOM, щелкните по нему правой кнопкой мыши и выберите «Copy XPath».  

Цель веб-скрапера на C# — извлечь данные, связанные с каждым эпизодом. Поэтому извлеките селектор XPath, применив описанную выше процедуру к элементу <tr> эпизода.  

Извлечение селектора XPath для веб-скрапера на C#
Извлечение селектора XPath для веб-скрапера на C#

Это вернется:

//*[@id="mw-content-text"]/div[1]/table[2]/tbody/tr[2]

Помните, что вы хотите выбрать все элементы <tr>. Поэтому необходимо изменить индекс, связанный с элементом select row. То есть, вам не нужно извлекать первую строку таблицы, поскольку она содержит только заголовки таблицы. В XPath индексы начинаются с 1, поэтому вы можете выбрать все элементы <tr> таблицы первого эпизода на странице, добавив синтаксис XPath position()>1.  

Кроме того, вы хотите получить данные из таблиц всех сезонов. На странице Википедии таблицы, содержащие данные об эпизодах, находятся со второй по пятнадцатую HTML-таблицу, содержащуюся в HTML-документе. Итак, вот как будет выглядеть окончательная строка XPath:

//*[@id='mw-content-text']/div[1]/table[position()>1 and position()<15]/tbody/tr[position()>1]

Теперь вы можете использовать функцию SelectNodes(), предлагаемую HtmlAgilityPack, чтобы выбрать интересующие вас элементы HTML следующим образом:  

var nodes = document.DocumentNode.SelectNodes("//*[@id='mw-content-text']/div[1]/table[position()>1 and position()<15]/tbody/tr[position()>1]");

Обратите внимание, что метод SelectNodes() можно вызвать только для экземпляра HtmlNode. Поэтому вам нужно получить корневой узел HTML-документа с помощью свойства DocumentNode.  

Также не забывайте, что селекторы XPath — это лишь один из многих методов выбора элементов HTML на веб-странице. Селекторы CSS — еще один популярный вариант.  

Шаг 4: Извлечение данных из элементов HTML

Во-первых, вам нужен пользовательский класс, в котором будут храниться полученные с помощью веб-скрапинга данные. Создайте файл Episode.cs в папке WebScraping и инициализируйте его следующим образом:  

namespace StaticWebScraping {
    public class Episode {
        public string OverallNumber { get; set; }        
        public string Title { get; set; }
        public string Directors { get; set; }
        public string WrittenBy { get; set; }
        public string Released { get; set; }
    }
}

Как вы видите, этот класс имеет четыре атрибута для хранения всей наиболее важной информации об эпизоде. Обратите внимание, что OverallNumber — это строка, потому что номер эпизода в «Губке Бобе» всегда содержит строчный символ.  

Теперь вы можете реализовать логику веб-скрапинга на C# в вашем файле App.cs, как показано ниже:  

using HtmlAgilityPack;
using System;
using System.Collections.Generic;                        

namespace StaticWebScraping {        
    public class Program {
        public static void Main() {
            // the URL of the target Wikipedia page
            string url = "https://en.wikipedia.org/wiki/List_of_SpongeBob_SquarePants_episodes";

            var web = new HtmlWeb();
            // downloading to the target page
            // and parsing its HTML content
            var document = web.Load(url);
          
            // selecting the HTML nodes of interest  
            var nodes = document.DocumentNode.SelectNodes("//*[@id='mw-content-text']/div[1]/table[position()>1 and position()<15]/tbody/tr[position()>1]");
            
            // initializing the list of objects that will
            // store the scraped data
            List<Episode> episodes = new List<Episode>();           
            // looping over the nodes 
            // and extract data from them
            foreach (var node in nodes) {                                
                // add a new Episode instance to 
                // to the list of scraped data
                episodes.Add(new Episode() {
                    OverallNumber = HtmlEntity.DeEntitize(node.SelectSingleNode("th[1]").InnerText),
                    Title = HtmlEntity.DeEntitize(node.SelectSingleNode("td[2]").InnerText),
                    Directors = HtmlEntity.DeEntitize(node.SelectSingleNode("td[3]").InnerText),
                    WrittenBy = HtmlEntity.DeEntitize(node.SelectSingleNode("td[4]").InnerText),
                    Released = HtmlEntity.DeEntitize(node.SelectSingleNode("td[5]").InnerText)
                });
            }

            // converting the scraped data to CSV... 
            // storing this data in a db...
            // calling an API with this data...
        }
    }
}

Этот веб-скрапер на C# перебирает выбранные узлы HTML, создает экземпляр класса Episode для каждого из них и сохраняет его в списке под названием episodes. Имейте в виду, что интересующие вас HTML-узлы являются строками таблицы. Поэтому необходимо выбрать некоторые элементы с помощью метода SelectSingleNode(). Затем с помощью атрибута InnerText извлечь из них нужные данные. Обратите внимание на использование статической функции HtmlEntity.DeEntitize() для замены специальных символов HTML на их естественные представления.  

Шаг 5: Экспорт извлеченных данных в CSV

Теперь, когда вы узнали, как выполнять веб-скрапинг на C#, вы можете делать с полученными данными все, что захотите. Одним из наиболее распространенных сценариев является преобразование полученных данных в удобочитаемый формат, например CSV. Сделав это, любой член вашей команды сможет изучить полученные с помощью веб-скрапинга данные непосредственно в Excel.

Теперь давайте узнаем, как экспортировать полученные данные в CSV с помощью C#.

Чтобы упростить себе задачу, давайте воспользуемся библиотекой. CSVHelper — это быстрая, простая в использовании и мощная библиотека .NET для чтения, а также записи файлов CSV. Чтобы добавить зависимость CSVHelper, откройте раздел «Manage NuGet Packages» в Visual Studio, найдите «CSVHelper» и установите его.  

Вы можете использовать CSVHelper для преобразования наших полученные с помощью веб-скрапинга данных в CSV, как показано ниже:

using CsvHelper;
using System.IO;
using System.Text;
using System.Globalization;

// scraping logic…

// initializing the CSV file
using (var writer = new StreamWriter("output.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
	// populating the CSV file
	csv.WriteRecords(episodes);
}

Если вы не знакомы с ключевым словом using, оно определяет область видимости, в конце жизненного цикла которой находящиеся в ней объекты будут утилизированы. Другими словами, using отлично подходит для работы с файловыми ресурсами. Затем функция WriteRecords() автоматически преобразует собранные данные в CSV и записывает их в файл output.csv.

Как только ваш веб-скрапер на C# завершит работу, вы увидите, что в корневой папке проекта появится файл output.csv. Откройте его в Excel, и вы увидите следующие данные:  

Excel со всеми полученными с помощью веб-скрапинга данными
Excel со всеми полученными с помощью веб-скрапинга данными

Вуаля! Вы только что узнали, как выполнять веб-скрапинг с помощью C# на веб-сайтах со статическим содержимым!

Скрапинг веб-сайтов с динамическим содержимым на C#

Веб-сайты с динамическим контентом используют JavaScript для получения данных с помощью технологии AJAX. HTML-документы, использующие динамический контент, могут быть практически пустыми. В то же время, они содержат сценарии JavaScript, которые отвечают за получение и отображение данных динамически во время рендеринга. Это означает, что если вы хотите извлечь из них данные, вам нужен браузер для рендеринга их страниц, только он может выполнять JavaScript.  

Скрапинг динамических сайтов, безусловно, более сложный, чем в случае статических интернет-ресурсов. В частности, для веб-скрапинга таких сайтов вам понадобится безголовый браузер. Если вы не знакомы с этой технологией, безголовый браузер — это браузер без графического интерфейса. Другими словами, если вы хотите извлечь данные с динамического сайта, используя C#, вам понадобится библиотека, предоставляющая возможности безголового браузера. Например, Selenium.  

Следуя указаниям из параграфа в начале статьи, создайте новый проект C#. На этот раз назовите его DynamicWebScraping.  

Шаг 1: Установите Selenium

Selenium — это фреймворк с открытым исходным кодом для автоматизированного тестирования, который поддерживает несколько языков программирования. Selenium предоставляет возможности безголового браузера и позволяет инструктировать веб-браузер для выполнения определенных действий.

Чтобы добавить Selenium в зависимости вашего проекта, снова перейдите в раздел «Manage NuGet Packages», найдите «Selenium.WebDriver» и установите его.

Импортируйте Selenium, добавив эти две строки в верхнюю часть вашего файла App.cs:  

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

Шаг 2: Подключение к целевому сайту

Поскольку Selenium открывает целевой сайт в браузере, вам не нужно вручную выполнять HTTP GET запрос. Все, что от вас требуется, это использовать Selenium Web Driver следующим образом:

// the URL of the target Wikipedia page
string url = "https://en.wikipedia.org/wiki/List_of_SpongeBob_SquarePants_episodes";

// to initialize the Chrome Web Driver in headless mode
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("headless");
var driver = new ChromeDriver();

// connecting to the target web page
driver.Navigate().GoToUrl(url);

Здесь вы создали экземпляр веб-драйвера Chrome. Если вы используете другой браузер, адаптируйте код соответствующим образом, используя нужный драйвер браузера. Затем, благодаря методу Navigate() переменной драйвера, вы можете вызвать метод GoToUrl() для подключения к целевой веб-странице. Эта функция принимает параметр URL и использует его для посещения соответствующей веб-страницы в безголовом браузере.  

Шаг 3: Веб-скрапинг данных из HTML-элементов

Как и раньше, вы можете использовать селектор XPath для выбора интересующих вас элементов HTML:

//*[@id='mw-content-text']/div[1]/table[position()>1 and position()<15]/tbody/tr[position()>1]

Используйте селектор XPath в Selenium с помощью:

 var nodes = driver.FindElements(By.XPath("//*[@id='mw-content-text']/div[1]/table[position()>1 and position()<15]/tbody/tr[position()>1]"));

В частности, метод Selenium By.XPath() позволяет применить строку XPath для выбора HTML-элементов в DOM-структуре веб-страницы.

Теперь предположим, что вы уже определили класс Episode.cs. Теперь вы можете создать веб-скрапер на C# с помощью Selenium, как показано ниже:  

using System;
using System.Collections.Generic;        
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

namespace DynamicWebScraping {        
    public class Program {
        public static void Main() {
            // the URL of the target Wikipedia page
            string url = "https://en.wikipedia.org/wiki/List_of_SpongeBob_SquarePants_episodes";

            // to initialize the Chrome Web Driver in headless mode
            var chromeOptions = new ChromeOptions();
            chromeOptions.AddArguments("headless");
            var driver = new ChromeDriver();

            // connecting to the target web page
            driver.Navigate().GoToUrl(url);

            // selecting the HTML nodes of interest 
            var nodes = driver.FindElements(By.XPath("//*[@id='mw-content-text']/div[1]/table[position()>1 and position()<15]/tbody/tr[position()>1]"));
                        
            // initializing the list of objects that will
            // store the scraped data
            List<Episode> episodes = new();           
            // looping over the nodes 
            // and extract data from them
            foreach (var node in nodes) {                                
                // add a new Episode instance to 
                // to the list of scraped data
                episodes.Add(new Episode() {
                    OverallNumber = node.FindElement(By.XPath("th[1]")).Text,
                    Title = node.FindElement(By.XPath("td[2]")).Text,
                    Directors = node.FindElement(By.XPath("td[3]")).Text,
                    WrittenBy = node.FindElement(By.XPath("td[4]")).Text,
                    Released = node.FindElement(By.XPath("td[5]")).Text
                });
            }

            // converting the scraped data to CSV... 
            // storing this data in a db...
            // calling an API with this data...
                        
        }
    }
}

Как вы можете видеть, логика веб-скрапинга не сильно изменилась по сравнению с тем, что было сделано с помощью HtmlAgilityPack. Более того, благодаря методам Selenium FindElements() и FindElement() вы можете достичь той же цели, что и раньше. Что действительно меняется, так это то, что Selenium выполняет все эти операции в браузере.  

Обратите внимание, что на сайтах с динамическим содержимым может потребоваться ожидание получения и отображения данных. Этого можно добиться с помощью WebDriverWait.  

Поздравляем! Теперь вы научились выполнять на C# веб-скрапинг на сайтах с динамическим содержимым. Осталось узнать, что делать с полученными данными.

Что делать с полученными данными

  • Сохраните их в базе данных, чтобы получать оттуда, когда понадобятся.
  • Преобразуйте их в JSON и используйте для вызова API.
  • Преобразование его в удобочитаемый формат, например CSV, чтобы иметь возможность открыть в Excel.

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

Но имейте в виду, что веб-скрапинг сопряжен с целым рядом трудностей!

Конфиденциальность данных с помощью прокси

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

В результате прокси-сервис позволяет преодолевать блокировку IP-адресов, анонимно собирать данные и разблокировать контент во всех странах. Существуют различные типы прокси, и все они имеют разные случаи использования и цели. Убедитесь, что вы выбрали правильного прокси-провайдера.  

Давайте теперь разберемся, какие преимущества прокси могут принести вашему процессу веб-скрапинга.

Обход IP-блокировки

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

Ротация IP-адресов

Премиум-прокси обычно предлагают функцию ротации IP-адресов. Это означает, что каждый раз, когда вы обращаетесь к прокси-серверу, вам предоставляется новый IP-адрес из большого пула IP-адресов. Это позволяет избежать отслеживания антискрепинговыми системами.  

Региональный скрапинг

Многие сайты меняют свою информацию в зависимости от того, откуда поступил запрос. Кроме того, некоторые из них доступны только в определенных регионах. Скрапинг таких сайтов для проведения исследований мирового рынка может стать проблемой. К счастью, вы можете использовать анонимные прокси для выбора местоположения выходного IP-адреса. Это отличный способ собрать ценную информацию о товарах с международных сайтов.    

Заключение

Теперь вы знаете, как создать веб-скапер с помощью C#. Как вы могли увидеть, для этого не требуется большого количества строк кода. В то же время, когда целевые веб-страницы будут меняться, вам придется соответствующим образом обновлять скрепер. Некоторые сайты вносят изменения в свою структуру ежедневно. Именно поэтому вам стоит попробовать продвинутый Web Scraper IDE. Скраперы Bright Data всегда обновляются, поэтому вы можете сосредоточиться на анализе получаемых данных, а не на постоянной перенастройке своего скрепера.