Парсинг сайтов на C#
Использование Selenium на C#
Самый распостраненный способ автоматизировать любые действия с вебсайтом — использование библиотеки Selenium. Эта библиотека позволяет запустить браузер и управлять всеми действиями в нем. Поддерживаются все распостраненные браузеры.
Подключение Selenium к проекту
В контекстном меню проекта выбираем пункт «Manage NuGet Packages…»
Далее на вкладке «Browse» находим и устанавливаем пакеты Selenium.WebDriver и Selenium.WebDriver.ChromeDriver. Первый пакет — сама библиотека, второй добавляет в папку с собранным проектом драйвер к хрому ChromeDriver.exe.
Автоматизация действий с помощью Selenium
В качестве тестовой задачи попробуем на этом блоге открыть первую страницу с результатами поиска по запросу «C#» и на консоль вывести заголовки и адреса найденных статей.
Начнем с того, что откроем браузер и перейдем на главную страницу сайта.
using OpenQA.Selenium; using OpenQA.Selenium.Chrome; ... IWebDriver driver = new ChromeDriver(); driver.Url = @"http://lsreg.ru";
Теперь нужно найти инпут для ввода критерия поиска и ввести в него текст. Поиск элементов на странице осуществляется с помощью методов IWebDriver.FindElement и IWebDriver.FindElements. Эти методы умеют искать по множеству критериев: тег, css класс, xpath и другие.
Поиск элементов по XPath
XPath — это язык запросов к дереву элементов. Вот как выглядит запрос дива с id=»my_div»:
.//div[@id='my_div']
Здесь точка вначале запроса означает, что поиск осуществляется из корня документа. Без точки поиск осущствлялся бы только в контексте текущего элемента. Двойной слеш означает любое количество элементов. Одинарный слеш означал бы, что див лежит прямо в корне документа. Пара примеров для наглядкости:
.//div[@id='my_div']//a - все ссылки внутри дива .//div[@id='my_div']/a - ссылки на первом уровне вложенности
Кроме id фильтровать можно и по другим атрибутам.
Действия с элементами страницы
Метод FindElement возвращает экземпляр IWebElement. Для ввода текста используется метод SendKeys, для клика есть метод Click.
Находим инпут и вводим в него строку поиска
driver.FindElement(By.XPath(@".//div[@id='search-3']/form/input[@id='s']")).SendKeys("c#");
Кликаем на кнопку поиска
driver.FindElement(By.XPath(@".//input[@id='searchsubmit']")).Click(); Thread.Sleep(3000);
Sleep нужен для того, чтобы результаты поиска успели отобразиться.
Теперь находим все ссылки внутри заголовков и отображаем их текст и href
var links = driver.FindElements(By.XPath(".//h2/a")); foreach (IWebElement link in links) Console.WriteLine("{0} - {1}", link.Text, link.GetAttribute("href"));
Получаем вот такой результат
Плюсы и минусы парсинга с помощью Selenium
Плюсы:
- Сервер не отличит программу от реального пользователя
- Можно работать в контентом, подгружаемым динамически
Минусы
- Использование реального браузера замедляет работу и увеличивает потребление ресурсов
- Не поддерживается .Net Core
Парсинг HTML с помощью HtmlAgilityPack
Если на сайте нет защиты от ботов и весь необходимый контент отдается сразу, то можно обойтись более легковесным решением — использовать библиотеку HtmlAgilityPack. Подключается она так же через NuGet. Эта библиотека строит DOM дерево по HTML. Проблема в том, что загружать код страницы придется самим. Для загрузки страницы я использую вот такую функцию
static string LoadPage(string url) { var result = ""; var request = (HttpWebRequest)WebRequest.Create(url); var response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode == HttpStatusCode.OK) { var receiveStream = response.GetResponseStream(); if (receiveStream != null) { StreamReader readStream; if (response.CharacterSet == null) readStream = new StreamReader(receiveStream); else readStream = new StreamReader(receiveStream, Encoding.GetEncoding(response.CharacterSet)); result = readStream.ReadToEnd(); readStream.Close(); } response.Close(); } return result; }
Загружаем код страницы
using HtmlAgilityPack; .. var pageContent = LoadPage(@"http://lsreg.ru/?s=c%23"); var document = new HtmlDocument(); document.LoadHtml(pageContent);
Здесь сразу указываем урл страницы с результатами поиска. Дальше аналогично по XPath находим нужные ссылки и выводим результат.
HtmlNodeCollection links = document.DocumentNode.SelectNodes(".//h2/a"); foreach (HtmlNode link in links) Console.WriteLine("{0} - {1}", link.InnerText, link.GetAttributeValue("href", ""));
Второй параметр в методе GetAttributeValue — это значение по умолчанию.