Пусть за нас все делают боты. Парсинг сайтов на C# и Anglesharp

Изучение технологии для автоматизации процесса который изначально не был сложным.

Пусть за нас все делают боты. Парсинг сайтов на C# и Anglesharp

Проблема

Начало третьего семестра, и новый предмет "Алгоритмы и структуры данных" ведет тот же препод что прошлые два семестра вел у нас предмет "Алгоритмизация и программирование". Так как название последнего не предполагает чего либо конкретного то на нем нас вводили в специальность с помощью паскаля и лабораторных работ на чтение файлов, сортировок и вывода математических графиков на экран. Алгоритмы и структуры данных звучит более конкретно, нас будут учить различным алгоритмам и ничего лишнего быть не должно верно? Почти верно.

На первой лабораторной работе мы изучаем методы сортировки, входные данные даются нам в файле мы должны его отсортировать разными методами и сравнить их, все логично. И тут есть маленькое "но", мы сортируем два вида данных, числа и массив записей, в моем варианте это были фильмы с полями "Название" "Режиссер" "Жанр" "Год выпуска". Препод не понятно по какой причине заставляет нас выполнять дополнительную работу по реализации этих записей и потом их парсинг из файла по не совсем понятной мне причине. К сожалению, задание дано - нужно делать.

Это было небольшое вступление и ворчание о жизни, теперь то из за чего я на самом деле благодарен этому дополнительному геморрою с записями. Нам нужно заполнять файлы самостоятельно. Файл на 5000 тысяч чисел создать не сложно, а файл из 100 записей? Можно было бы написать скрипт и поля будут содержать значения "gdheno", но я решил сделать свою жизнь чуть интересней и заодно выучить полезную технологию. "А что если написать парсер который будет брать эти данные с imdb?"

Подготовка

Пусть за нас все делают боты. Парсинг сайтов на C# и Anglesharp

Так как у меня есть год опыта работы на C# делать парсер я решил на нем. Небольшой гугл поиск и я понял что есть два способа сделать это. Первый - ничего кроме встроенных фич .Net, достать нужную инфу с помощью регулярных выражений и методов System.LINQ. И второй - использовать AngleSharp. Насмотревшись мемов про регулярные выражения и имея нежелание точить колесо и потом собирать велосипеды я остановился на Anglesharp'е. Работать с этой библиотекой оказалось очень просто, я думал что начав работу над проектом я буду изучать технологию дня 3, и потом засяду с кодом еще на 5 так как с вебом раньше я не работал. Но вышло так что сохранение данных в .json файл отняло у меня больше сил.

Приложение в итоге выглядит так:

Пусть за нас все делают боты. Парсинг сайтов на C# и Anglesharp

В этом окне видно текст бокс с превью тех данных что мы достанем с imdb, дроп даун с количеством фильмов которые мы хотим достать, кнопку "Get" по нажатии которой происходит парсинг, и кнопку "Save" которая сохранит это в файл. Я согласен что создавать графический интерфейс для проги которая нужна только для одной простой задачи это лишнее усложнение, но Windows Forms Designer встроенный в Visual Studio делает этот процесс намного проще. К тому же, я решил делать лабораторную с графическим окном, почему бы немного не потренироваться? Итак, я хочу запарсить данные с сайта на Anglesharp, с чего мне начать?

Экшен

Чтобы запарсить страницу, необходимо ее получить, скачать html код этой страницы. В Anglesharp веб страницу представляет интерфейс IDocument который реализует методы необходимые нам для парсинга этой страницы. Чтобы скачать страницу с нужного url в формате этого интерфейса нужно действовать как предлагает нам этот туториал:

using var context = BrowsingContext.New(Configuration.Default); using var doc = await context.OpenAsync(url);

Теперь перейдем к самому вкусному мясу - парсинг этого html кода. Сейчас у нас есть переменная doc реализующая интерфейс IDocument и представляющая веб страницу которую мы только что достали. В этом интерфейсе есть много чего интересного, мы можем достать всю информацию о веб странице, включая head, body, title, url, cookie, мы можем добавить элементы или выполнить команды, это все очень круто, но нас сейчас интересуют методы GetElementsByClassName, GetElementsByName и GetElementsByTagName возвращающие коллекцию html элементов которые содержат переданный класс, имя или тег. Класс имя и тег нужны чтобы идентифицировать элемент на странице и показать его как то по особенному. Я уверен что на веб странице которую вы хотите запарсить практически каждый html элемент будет иметь какой либо из этих атрибутов.

Теперь давайте сделаем шаг назад и поговорим о том как вообще html элементы представлены в Anglesharp. Здесь классические отношения Parent и Child, у каждого элемента страницы который представлен интерфейсом IElement есть свои поля с Id, ClassName, Html и прочим, и также знакомые методы GetElementsByClassName и GetElementsByTagName которые дают нам доступ к детям этого элемента.

Давайте теперь применим эти знания на практике, на том что делал я. Заходим на страницу релизов imdb, нажимаем F12 и высматриваем способ получить список фильмов на этой странице. Замечаем что все фильмы находятся в элементе с классом list detail и в нем у каждый код принадлежит классу list_item odd или list_item even. То есть чтобы иметь лист из фильмов нам достаточно сделать так:

var films = doc.GetElementsByClassName("list detail")[0] .GetElementsByClassName("list_item");

Хочу заметить что когда мы вызываем например метод GetElementsByClassName, то он вернет лист html элементов и они будут отсортированы в порядке "Кого я нашел первым". В моем примере я достаю лист элементов с классом list detail и потом беру детей первого элемента в этом листе, того элемента который в html странице расположен выше.

У нас есть список фильмов, нам нужно достать название, жанр, режиссера и год каждого элемента. Достать имя просто, оно находится внутри h4 атрибута в таблице и его можно достать с помощью метода QuerySelector:

string name = films[i].QuerySelector("h4").TextContent.Trim();

Замечу что в атрибуте h4 у нас джек пот, мы имеем и имя и год выпуска. Делить их будем чуть позже, сейчас у нас чуть более интересная ситуация с жанром. Они раскиданы в span атрибутах класса cert-runtime-genre и в них замешаны разделители |, избавиться от них не сложно:

var genres = films[i].GetElementsByClassName("cert-runtime-genre")[0].QuerySelectorAll("span") .Where(element => element.TextContent != "|");

Достаем все span атрибуты и затем из них отбираем те чей текст не равен |, изи. И тут есть но, может проблема только моя, но мой браузер не показывает мне что там есть возрастной рейтинг и моя строка жанров выглядит как "Sci-Fi Action 13+". Пришлось идти на костыли:

string genre = ""; foreach (var g in genres) genre += g.TextContent + ' '; // filtering out age limit genre = new string(genre.Where(c => c != '+' && (c < '0' || c > '9')).ToArray()).Trim();

До тех пока не появится жанр содержащий в себе плюсы или цифры я в безопасности. С режиссером полегче, это первая ссылка внутри блока с классом txt-block. Можно сказать мы закончили, осталось заполнить наш массив с фильмами:

_films.Add(new Film() { Name = name.Remove(name.Length - 7, 7), Director = films[i].GetElementsByClassName("txt-block")[0].QuerySelector("a").TextContent.Trim(), Genre = genre, Year = name.Substring(name.Length - 5, 4) });

Film это моя структура содержащая поля для названия, режиссера, жанра и года выпуска фильма. Ее я и записывал в .json и позже читал из него, но это не тема данной статьи так что говорить об этом не буду.

Заключение

Вот и все что нужно знать для парсинга сайтов. Dead simple and straightforward. Если знать технологию то можно очень быстро выполнять задания на парс данных и бежать на фриланс)) Хотел бы еще обратить внимание на то что я решил парсить список скорых релизов, там у всех фильмов год релиза 2021 и может будет один с 2020 годом, не очень подходящие данные для сортировки, но препод принял) И конечно логичный вопрос "Оно того стоило?". Однозначно нет, сделать скрипт который писал бы рандомные строки в файл заняло бы минут 10 и для сортировки они подходили бы больше, но я сделал это по фану так что не считается. На этом у меня все, всем удачи и спасибо за внимание ><

Для тех кто хочет покопаться в проекте вот ссылка проекта на гитхабе. Поддерживайте со мной связь на github, вк или пишете на почту languidbasil@gmail.com

Пусть за нас все делают боты. Парсинг сайтов на C# и Anglesharp
44
4 комментария

Комментарий недоступен

Ответить

Я хочу побольше кодить на шарпе чтобы возвыситься в своем познании) Да и питон мне не оч заходит

Ответить

Вроде как не плохо - только отступы в коде поправь (просто кровь из глаз)

Ответить

че там попралять. Все норм выглядит. Классическая табуляция шарпов

Ответить