В этой статье я хотел бы рассказать как написать простой парсер на примере сайтов aimp.ru и geekbrains.ru. Статья предназначена строго для тех, кто уже имеет базовые знания о языке программирования C# и уже написал свой первый «Hello world».

Мне всегда нравился аудиоплеер Aimp (нет, это не реклама), но встроенных скинов у него слишком мало, а заходить на сайт, смотреть скины, скачивать и пробовать как они будут смотреться на деле не было никакого желания. Поэтому я решил написать парсер скинов с данного сайта. Немного посмотрев сайт, я заметил, что скины там хранятся последовательно с присвоенным id. Т.к. до недавнего времени я знал только 1С и немного командную строку, то недолго думая я решил написать его в командной строке. Но при тестировании обнаружил, что если скачивать большое количество файлов, то во-первых часть может просто не скачаться, а во-вторых может произойти переполнение оперативной памяти. В итоге я тогда бросил эту затею.

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

Для разработки нам понадобится только среда разработки, я использовал Visual Studio, вы можете использовать любую другую на ваш вкус.

Я не буду углубляться в базовые понятия C#, для этого написано множество различных книг и отснято бесчисленное количество роликов.

Для начала запустим Visual Studio и создадим консольное приложение (т.к. мне лень делать формы нам не нужен интерфейс). Среда разработки нам сама подготовит шаблон проекта. У нас получится что-то вроде этого:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}


Удаляем директивы которыми мы пользоваться сейчас не будем:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

И добавляем те директивы, которыми будем пользоваться:

using System.Diagnostics;   // Нужна, чтобы запускать внешние процессы
using System.Net;           // Нужна, чтобы работать с Web
using System.Threading;     // Нужна, чтобы скоротать время

После чего в методе Main объявляем переменную:

WebClient wc = new WebClient(); // Она нужна непосредственно для работы с Web

Парсим Aimp скины


Далее мы пишем саму функцию:

static string DownloadSkinsForAimp(WebClient wc)
        {
            Console.WriteLine("Downloading began");
            try
            {
                for (int i = 0; i <= 5; i++)
                {
                    string id = "79" + i;
                    string name = GetNameOfSkin(wc, id);

                    // Путь, откуда мы будем скачивать
                    string path = "aimp.ru/index.php?do=download&sub=catalog&id=" + id;
                                       
                    try
                    {
                        // Запускаем нужный нам браузер и передаем ему в качестве аргумента путь скачивания
                        Process.Start("chrome.exe", path);

                        Console.WriteLine("Download " + name + " is succesfull!");

                        // Ждем 5 секунд, чтобы вкладки успевали закрываться, иначе может быть переполнение памяти
                        Thread.Sleep(5000);
                    }
                    catch
                    {
                        Console.WriteLine("Download" + name + "failed");
                    }
                }
            }
            catch
            {
                return "\nSomething went is wrong";
            }
            return "\nDownloading complete";
            
        }

Все скины скачиваются в директорию, заданную в настройках браузера. Конструкции try/catch нам нужны для того, чтобы программа не «вываливалась» из-за ошибок. Хотя можно было обойтись и без них.

Вы могли заметить функцию GetNameOfSkin. Она нужна для того, чтобы получить название скина, который мы скачиваем. Можно обойтись и без неё, она нужна только для красоты, но раз мы только учимся, то напишем и её:

 static string GetNameOfSkin(WebClient wc, string id)
        {
            // Получаем строку с html разметкой
            string html = wc.DownloadString("http://www.aimp.ru/index.php?do=catalog&rec_id=" + id);
            // Находим в ней первое упоминание нужного нам id и удаляем ненужную левую часть
            // Название скина начинается через 5 символов после этого id
            string rightPartOfHtml = html.Substring(html.IndexOf(id) + 5);
            // Находим конец названия и удаляем оставшуюся правую часть
            string name = rightPartOfHtml.Substring(0, rightPartOfHtml.IndexOf("<")).Replace(" ", "_");
            // В итоге нам возвращается только само название скина
            return name;
        }

Далее в методе Main нужно вызвать скачивание на выполнение:

Console.WriteLine(DownloadSkinsForAimp(wc)); // На консоль нужно выводить потому, что
                                                                              // метод возвращает нам строку с результатом выполнения

Парсим сертификаты Geekbrains


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

Тут нам на помощь приходит класс WebClient, а именно его метод DownloadFile. Ему мы просто передаем путь для скачивания и путь для сохранения и он все делает за нас. Звучит легко, попробуем сделать:

static string DownloadCertificates(WebClient wc)
        {
            // Нужна для того, чтобы определить имя текущего пользователя
            string currentUser = Environment.UserName;

            Console.WriteLine("Downloading began");

            try
            {
                for (int i = 0; i <= 5; i++)
                {
                    try
                    {
                        // Все сертификаты на данном сайте хранятся в формате '.pdf'
                        wc.DownloadFile("https://geekbrains.ru//certificates//7075" + i + ".pdf", "c:\\users\\" + currentUser + "\\downloads\\7075" + i + ".pdf");

                        Console.WriteLine("Download certificate №7075" + i + " is succesfull");
                    }
                    catch
                    {
                        Console.WriteLine("Download certificate №7075" + i + " is failed");
                    }
                }
            }
            catch
            {
                return "\nSomething went is wrong";
            }
            return "\nDownloading certificates are complite!";
        }

И после чего точно также вызываем эту функцию из метода Main.

Вообще обе эти функции ещё есть куда дорабатывать, но я думаю для ознакомления и самых базовых функций парсинга они вполне подойдут. Кому лень все это собирать в один проект — вот ссылка на GitHub.

Спасибо за внимание и надеюсь, кому-нибудь это поможет.

P.S.: Сертификаты с geekbrains можно скачать и изменить имя и фамилию владельца на свою полюбоваться на них.

P.P.S.: Все скины, скачанные с сайта Aimp, хранятся в формате '.zip' и при желании функцию можно доработать, чтобы она сама их разархивировала. Также можно добавить, чтобы они сразу переносились в папку со скинами Aimp.

P.P.P.S.: Статья является исключительно познавательной и не несет рекламный характер.

Комментарии (31)


  1. dymanoid
    21.04.2016 12:51
    +1

    А где парсинг файлов? Вызывать хром через Process.Start() для скачивания файлов и затем Thread.Sleep() — мсье знает толк в извращениях.


  1. forcewake
    21.04.2016 12:59
    +6

    Зачем это здесь? Никакой полезной нагрузки данная статья не несёт.
    Автор, мы очень рады, что вы решили примкнуть к стройному ряду .net разработчкиков, но, поверьте, этот опус не нужен.


    1. Apollon_Diamed
      21.04.2016 13:40
      -2

      Цель данной статьи была в том, чтобы начинающие разработчики тратили меньше времени для поиска подобной информации, ибо я при написании такого простейшего парсера столкнулся с некоторыми проблемами. Но я Вас понял, что нужно побольше опыта, дабы писать статьи на Хабр.


      1. MeGaPk
        21.04.2016 13:48

        А почему нельзя было теми же средствами, скачать файл через код? Зачем еще браузер подключать?
        Кодом будет работать гораздо быстрее.


        1. Apollon_Diamed
          21.04.2016 13:49

          Пытался, потратил пару часов, но в итоге все равно пришел к этому варианту. Буду благодарен, если напишите ссылки, где об этом написано.


          1. forcewake
            21.04.2016 13:56

            Можно посмотреть здесь http://codereview.stackexchange.com/a/18679


            1. Apollon_Diamed
              21.04.2016 13:59

              Спасибо. Надеюсь когда-нибудь наберусь опыта и опубликую что-нибудь действительно полезное.


          1. KvanTTT
            21.04.2016 14:14

            Это вредные советы, не надо таких советов новичкам давать. Если у вас проблемы с UserAgent, то браузер не обязательно запускать. Вот, например, одно из решений.


            1. Apollon_Diamed
              21.04.2016 14:16

              Благодарю. Учту.


      1. forcewake
        21.04.2016 13:51
        +2

        Дело не только в опыте. Не поймите меня привратно, но так лучше не писать. Мне кажется, что все когда-то начинали с такого кода и это прекрасно, что вы не боитесь пробовать. Но выставлять что-то подобное как «введение в C#» мне кажется неправильным.


        1. Apollon_Diamed
          21.04.2016 13:55

          Благодарю, но все же надеюсь, что эта статья кому-нибудь окажется полезной.


  1. arcenciel
    21.04.2016 13:38
    +2

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


    1. Apollon_Diamed
      21.04.2016 13:40

      Благодарю, учту.


  1. Eugeny1987
    21.04.2016 13:54

    Я то думал, здесь будет ПАРСЕР, а тут… (


  1. TocoToucan
    21.04.2016 14:34

    Кстати, а зачем в 2016 году использовать WebClient если есть HttpClient(https://www.nuget.org/packages/Microsoft.Net.Http)?


    1. Apollon_Diamed
      21.04.2016 14:34

      Плохо искал. Спасибо.


    1. MooNDeaR
      21.04.2016 18:09

      Вот и я как-то надеялся наконец-то познать немного теоретической составляющей анализа текстов. Но когда увидел запуск браузера из кода мягко говоря прибалдел :)


      1. Apollon_Diamed
        21.04.2016 19:00

        Ну извините, что не оправдал Ваши ожидания. Надеюсь в следующий раз исправлюсь, если он предоставится. В любом случае спасибо за внимание)


  1. dkukushkin
    21.04.2016 15:40

    Откройте для себя Selenium.


    1. Apollon_Diamed
      21.04.2016 17:18

      Спасибо.


  1. Zapped
    21.04.2016 18:23

    Т.к. до недавнего времени я знал только 1С и немного командную строку, то недолго думая я решил написать его в командной строке

    можно было подумать подольше, и вот что могло бы/должно получиться:
    for i in $(seq 790 795); do wget "http://aimp.ru/index.php?do=download&sub=catalog&id=$i"; done
    

    ах, ну да, Windows же!
    FOR /L %i IN (790,1,795) DO wget "http://aimp.ru/index.php?do=download&sub=catalog&id=%i"
    

    есть инструменты, которые уже давно придуманы для таких вещей

    З.Ы. если бы Вы реально сделали парсер, я бы понял — мол, в образовательных целях… но таак извратиться!..


    1. Apollon_Diamed
      21.04.2016 18:59

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


      1. Zapped
        21.04.2016 19:03

        Причём тут браузер?
        Вы невнимательно читаете. Срочно гуглить wget


        1. Apollon_Diamed
          21.04.2016 19:08
          -1

          А, прошу прощения, не заметил, да, я гуглил по поводу wget, но не хотелось ставить сторонние утилиты. Да и развиваться же нужно в плане C#.


          1. Zapped
            21.04.2016 19:09

            Oook
            *facepalm*


          1. KvanTTT
            22.04.2016 00:28

            А браузер это не "сторонняя утилита"?


            1. Apollon_Diamed
              22.04.2016 05:29

              Сторонняя, но он у меня уже стоял и в принципе всегда стоит по умолчанию, а wget для одного раза скачивать не хотелось. Хотя наверное следовало, дабы упростить задачу.


  1. yarosroman
    21.04.2016 19:24

    А для парсинга HTML есть HtmlAgilityPack.


    1. Apollon_Diamed
      22.04.2016 05:31

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


  1. ForNeVeR
    22.04.2016 18:42

    Дорогой автор, не расстраивайтесь по поводу отрицательных оценок вашей работы, просто она действительно реализована не совсем лучшим или рекомендуемым образом. Вам есть ещё куда расти, и я надеюсь, что теперь вы примерно видите направления для дальнейшего развития навыков. Не опускайте рук!


    1. Apollon_Diamed
      22.04.2016 21:32
      +1

      Я и не расстраивался, я понимаю, что мне ещё долго придется расти. Я ценю и уважаю конструктивную критику, а здесь мне её дали сполна. Спасибо за поддержку!)