Работа с RSS в C# (Csharp). Создание своего RSSReader

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

  Если говорить в общих чертах, то RSS представляет собой диалект XML. Обязательными элементами которого являются channel, item, title, description, link, pubDate.
  Наша программа будет запрашивать RSS-ленту сайта devoid.com.ua, которая находится здесь и содержит 10 новых статей портала.
  Приступим к программированию. Запускаем Visual Studio 2005 и создаем новый проект под названием, скажем RssReader. На форме кнопка (Button) и Веб-браузер (WebBrowser). Форма выглядит примерно так:

Как видно из дизайнера формы, загружать наш RSS-поток мы будем по нажатию кнопки "Загрузить". Все возможные элементы RSS-ленты мы парсить не будем, ограничимся лишь channel, item и img.
  Следующим шагом и будет написание классов, которые будут отвечать за отпарсенную информацию из RSS-потока.
Класс, который отвечает за настройки канала (channel) будет выглядеть так:

        // Класс который отвечает за настройки канала
        public class ChannelClass
        {
            public string title;
            public string description;
            public string link;
            public string copyright;

            public ChannelClass()
            {
                title = "";
                description = "";
                link = "";
                copyright = "";
            }
        }
  Как видно класс Канала содержит в себе четыре поля, которые говорят сами за себя. Название канала, Описание канала, ссылка, и копирайт (является необязательным).
  Следующим классом который мы создадим будет класс, который будет отвечать за рисунок канала (рисунок является необязательным элементом канала, но в целях получения опыта научимся работать и с ним):
        // Класс рисунка канала
        public class ImageOfChanel
        {
            public string imgTitle;
            public string imgLink;
            public string imgURL;

            public ImageOfChanel()
            {
                imgTitle = "";
                imgLink = "";
                imgURL = "";
            }
        }
Класс довольно простой, и содержит в себе 3 обязательных элемента: Название канала, URL-рисунка, и URL-сайта.
Далее пишем класс, который отвечает за элементы <item> канала:
        // Класс статей
        public class Items
        {
            public string title;
            public string link;
            public string description;
            public string pubDate;

            public Items()
            {
                title = "";
                link = "";
                description = "";
                pubDate = "";
            }
        }
Здесь тоже ничего нового мы не увидели, названия говорят всё за себя.
  Отметим что основным методом нашего RssReadera будет метод считывания данных из RSS-ленты и заполнение данными соответствующих обьектов классов, которые нужно обьявить перед телом метода:
        ImageOfChanel imageChanel = new ImageOfChanel(); // обьект класса рисунка
        Items[] articles; // создаем массив элементов item канала
        ChannelClass channel = new ChannelClass(); // создаем обьект класса ChannelClass
Для ясности я сначала приведу код метода получения данных, а далее подробно распишу:
        bool getNewArticles(string fileSource)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(fileSource);

                XmlNodeList nodeList;
                XmlNode root = doc.DocumentElement;
                articles = new Items[root.SelectNodes("channel/item").Count];
                nodeList = root.ChildNodes;
                int count = 0;

                foreach (XmlNode chanel in nodeList)
                {
                    foreach (XmlNode chanel_item in chanel)
                    {
                        if (chanel_item.Name == "title")
                        {
                            channel.title = chanel_item.InnerText;
                        }
                        if (chanel_item.Name == "description")
                        {
                            channel.description = chanel_item.InnerText;
                        }
                        if (chanel_item.Name == "copyright")
                        {
                            channel.copyright = chanel_item.InnerText;
                        }
                        if (chanel_item.Name == "link")
                        {
                            channel.link = chanel_item.InnerText;
                        }

                        if (chanel_item.Name == "img")
                        {
                            XmlNodeList imgList = chanel_item.ChildNodes;
                            foreach (XmlNode img_item in imgList)
                            {
                                if (img_item.Name == "url")
                                {
                                    imageChanel.imgURL = img_item.InnerText;
                                }
                                if (img_item.Name == "link")
                                {
                                    imageChanel.imgLink = img_item.InnerText;
                                }
                                if (img_item.Name == "title")
                                {
                                    imageChanel.imgTitle = img_item.InnerText;
                                }
                            }
                        }

                        if (chanel_item.Name == "item")
                        {
                            XmlNodeList itemsList = chanel_item.ChildNodes;
                            articles[count] = new Items();

                            foreach (XmlNode item in itemsList)
                            {
                                if (item.Name == "title")
                                {
                                    articles[count].title = item.InnerText;
                                }
                                if (item.Name == "link")
                                {
                                    articles[count].link = item.InnerText;
                                }
                                if (item.Name == "description")
                                {
                                    articles[count].description = item.InnerText;
                                }
                                if (item.Name == "pubDate")
                                {
                                    articles[count].pubDate = item.InnerText;
                                }
                            }
                            count += 1;
                        }


                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
  Не стоит пугаться столь большого обьема кода, на самом деле он довольно прост. Следует отметить также, что так как RSS является диалектом XML, то и работать мы будем с ним как с XML-документом. Для этого нужно в uses добавить XML:
using System.Xml;
using System.IO;
using System.IO; - тоже добавьте, это нам понадобится позже.
  Итак, функция getNewArticles(string fileSource) принимает в качестве параметра ссылку на RSS-поток, и возвращает либо true при успешной работе, либо false при неудачной попытке. Весь код помещен в try...catch для отслеживания исключительных ситуаций.
Далее идем по коду:
  XmlDocument doc = new XmlDocument();
  doc.Load(fileSource);
Создаем обьект класса doc и загружаем в него наш RSS-поток методом Load(fileSource).
                XmlNodeList nodeList;
                XmlNode root = doc.DocumentElement;
                articles = new Items[root.SelectNodes("channel/item").Count];
                nodeList = root.ChildNodes;
                int count = 0;
  Создаем XmlNode root - который будет содержать корневой элемент XML.
  Далее получаем количество элементов item в нашем RSS-потоке, используя SelectNodes() и выражение XPath, которое позволяет это сделать.
  nodeList получает все элементы channel.
  count - используется как индексатор массива articles[].
foreach (XmlNode chanel in nodeList)
{
	...
}
  Этим циклом мы проходим по всем элементам cnannel. Обрабатывая название канала, описание, ссылку - этими строками:
foreach (XmlNode chanel_item in chanel)
                    {
                        if (chanel_item.Name == "title")
                        {
                            channel.title = chanel_item.InnerText;
                        }
                        if (chanel_item.Name == "description")
                        {
                            channel.description = chanel_item.InnerText;
                        }
                        if (chanel_item.Name == "copyright")
                        {
                            channel.copyright = chanel_item.InnerText;
                        }
                        if (chanel_item.Name == "link")
                        {
                            channel.link = chanel_item.InnerText;
                        }
						...
  Когда мы находим элемент <img> или <item>, который содержит тоже элементы, мы просто запускаем еще один цикл, который обрабатывает их:
if (chanel_item.Name == "img")
    {
       XmlNodeList imgList = chanel_item.ChildNodes;
       foreach (XmlNode img_item in imgList)
       {
            if (img_item.Name == "url")
            {
                imageChanel.imgURL = img_item.InnerText;
            }
            if (img_item.Name == "link")
            {
                imageChanel.imgLink = img_item.InnerText;
            }
            if (img_item.Name == "title")
            {
                imageChanel.imgTitle = img_item.InnerText;
            }
        }
    }

if (chanel_item.Name == "item")
   {
      XmlNodeList itemsList = chanel_item.ChildNodes;
      articles[count] = new Items();

      foreach (XmlNode item in itemsList)
      {
          if (item.Name == "title")
          {
               articles[count].title = item.InnerText;
          }
          if (item.Name == "link")
          {
               articles[count].link = item.InnerText;
          }
          if (item.Name == "description")
          {
              articles[count].description = item.InnerText;
          }
          if (item.Name == "pubDate")
          {
              articles[count].pubDate = item.InnerText;
          }
      }
   count += 1;
   }
После выполнения этой функции, мы имеем обьекты классов, заполненные данными. в imageChanel содержатся все данные о рисунке (если он есть), в channel - все параметры канала, а массив articles - содержит все статьи портала devoid.com.ua (или элементы item в случае парсинга другой RSS-ленты).   Для отображения отпарсенных данных нам и пригодится WebBrowser. Все данные из RSS-потока мы сохраним в виде *.html файла, и потом отобразим его. Для этого напишем простую функцию:
bool generateHtml()
{
 try
 {
   using (StreamWriter writer = new StreamWriter("last_articles.html"))
   {
    writer.WriteLine("<html>");
    writer.WriteLine("<head>");
    writer.WriteLine("<meta http-equiv="content-type" content="text/html; charset=utf-8">");
    writer.WriteLine("<title>");
    writer.WriteLine(channel.title);
    writer.WriteLine("</title>");
    writer.WriteLine("<style type='text/css'>");
    writer.WriteLine("A{color:#483D8B; text-decoration:none; font:Verdana;}");
    writer.WriteLine("pre{font-family:courier;color:#000000;");
	writer.WriteLine("background-color:#dfe2e5;padding-top:5pt;padding-left:5pt;");
    writer.WriteLine("padding-bottom:5pt;border-top:1pt solid #87A5C3;");
    writer.WriteLine("border-bottom:1pt solid #87A5C3;border-left:1pt solid #87A5C3;");
    writer.WriteLine("border-right : 1pt solid #87A5C3;	text-align : left;}");
    writer.WriteLine("</style>");
    writer.WriteLine("</head>");
    writer.WriteLine("<body>");

    writer.WriteLine("<font size="2" face="Verdana">");
    writer.WriteLine("<a href="" + imageChanel.imgLink + "">");
    writer.WriteLine("<img src="" + imageChanel.imgURL + "" border=0></a>  ");
    writer.WriteLine("<h3>" + channel.title + "</h3></a>");

    writer.WriteLine("<table width="80%" align="center" border=1>");
   foreach (Items article in articles)
   {
    writer.WriteLine("<tr>");
    writer.WriteLine("<td>");
    writer.WriteLine("<br>  <a href="" + article.link + ""><b>" + article.title + "</b></a>");
    writer.WriteLine("& (" + article.pubDate + ")<br><br>");
    writer.WriteLine("<table width="95%" align="center" border=0>");
    writer.WriteLine("<tr><td>");
    writer.WriteLine(article.description);
    writer.WriteLine("</td></tr></table>");
    writer.WriteLine("<br>  <a href="" + article.link + "">");
    writer.WriteLine("<font size="1">читать дальше</font></a><br><br>");
    writer.WriteLine("</td>");
    writer.WriteLine("</tr>");
    }
    writer.WriteLine("</table><br>");

    writer.WriteLine("<p align="center">");
	writer.WriteLine("<a href="" + channel.link + "">" + channel.copyright + "</a></p>");

    writer.WriteLine("</font>");
    writer.WriteLine("</body>");
    writer.WriteLine("</html>");
    return true;
   }
 }
 catch (Exception ex)
 {
    MessageBox.Show(ex.Message);
    return false;
 }
}
Функция не сложная и не требует глубоких пояснений, стоит только отметить что мы создаем StreamWriter, который записывает все данные в файл last_articles.html Теперь осталось лишь добавить обработчик нажатия кнопки, который выглядит вот так:
            if (getNewArticles("http://devoid.com.ua/rss.php") == true && generateHtml() == true)
            {
                Browser.Navigate(Environment.CurrentDirectory + "\last_articles.html");
            }
После всего вышесказанного, осталось только нажать F5 и получить новые статьи сайта www.devoid.com.ua.
Стоит заметить также что наш парсер является уникальным, и сможет работать со всеми RSS-лентами стандарта 2.0.
Исходники этого RSS-ридера можно скачать отсюда.
Спасибо за внимание, успехов!

Макагон Сергей, http://devoid.com.ua  



Опубликовал admin
21 Окт, Воскресенье 2007г.



Программирование для чайников.