Программирование на языке Ruby.

15.2.1. Стандартная библиотека rss.

Формат RSS основан на XML, поэтому разбирать его можно как обычный XML-документ. Но, поскольку это все-таки специализированный вариант, для него имеет смысл разработать специальный анализатор. Кроме того, запутанность стандарта RSS уже стала притчей во языцех — некорректно написанные программы могут генерировать такие RSS-документы, которые будет очень трудно разобрать.

Ситуация осложняется еще и тем, что существуют несовместимые версии стандарта; чаще всего используются 0.9,1.0 и 2.0. В общем, подобно производству колбасы, RSS — такая вещь, в детали которой лучше не вникать.

В дистрибутив Ruby входит стандартная библиотека, понимающая версии стандарта 0.9,1.0 и 2.0. Даже если вы не укажете версию входного документа явно, библиотека попытается определить ее самостоятельно.

Рассмотрим пример. Мы загрузили канал с сайта http://marsdrive.com и распечатали заголовки нескольких статей из него:

Require 'rss'.

Require 'open-uri'.

URL = "http://www.marstoday.com/rss/mars.xml".

Open(URL) do |h|

 resp = h.read.

 result = RSS::Parser.parse(resp,false).

 puts "Канал: #{result.channel.title}".

 result.iterns.each_with_index do |item,i|

  i += 1.

  puts "#{i} #{item.title}".

 end.

End.

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

В этой программе мы для удобства воспользовались библиотекой open-uri. Подробно мы рассмотрим ее в главе 18, а пока достаточно знать, что она позволяет вызывать метод open для URI, как для обычного файла.

Отметим, что канал извлекает из документа анализатор RSS, а наша программа печатает название канала. Кроме того, метод доступа items формирует список элементов канала, то есть статей, а мы распечатываем их заголовки.

Понятно, что результат меняется со временем; когда я запускал эту программу, она напечатала вот что:

Title: Mars Today Top Stories.

1 NASA Mars Picture of the Day: Lava Levees.

2 NASA Mars Global Surveyor TES Dust And Temperature Maps 25 June - 2 July 2006.

3 Mars Institute Core Team Arrives at the HMP Research Station on Devon Island.

4 Assessment of NASA's Mars Architecture 2007-2016.

5 NASA Mars Picture of the Day: Rush Hour.

Есть также возможность генерировать документы в формате RSS (листинг 15.5). Для этого нужно инвертировать показанную выше процедуру.

Листинг 15.5. Создание RSS-канала.

Require 'rss'.

Feed = RSS::Rss.new("2.0").

Chan = RSS::Rss::Channel.new.

Chan.description = "Feed Your Head".

Chan.link = "http://nosuchplace.org/home/".

Img = RSS::Rss::Channel::Image.new.

Img.url = "http://nosuchplace.org/images/headshot.jpg".

Img.title = "Y.T.".

Img.link = chan.link.

Chan.image = img.

Feed.channel = chan.

I1 = RSS::Rss::Channel::Item.new.

I1.title = "Once again, here we are".

I1.link = "http://nosuchplace.org/articles/once_again/".

I1.description = "Don't you feel more like you do now than usual?".

I2 = RSS::Rss::Channel::Item.new.

I2.title = "So long, and thanks for all the fiche".

I2.link = "http://nosuchplace.org/articles/so_long_and_thanks/".

I2.description = "I really miss the days of microfilm...".

I3 = RSS::Rss::Channel::Item.new.

I3.title = "One hand clapping".

I3.link = "http://nosuchplace.org/articles/one_hand_clapping/".

I3.description = "Yesterday I went to an amputee convention...".

Feed.channel.items << i1 << i2 << i3.

Puts feed.

Большая часть этой программы понятна без слов. Мы создаем канал в формате RSS 2.0 (с пустыми элементами channel и image), а потом с помощью методов доступа добавляем данные. Элемент image ассоциируется с элементом channel, а последний — с самим RSS-каналом.

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

Feed.channel. items = [i1,i2,i3].

Но такое решение работать не будет. Почему-то в классе Channel нет акцессора items=. Можно было бы написать items[0] = i1 и т.д., или то же самое в цикле. Наверное, есть и другие способы добиться нужного результата, но представленное выше решение вполне годится.

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

Многие предпочитают не RSS, a Atom. Библиотека rss не умеет работать с этим форматом, но есть прекрасная (хотя и не стандартная) библиотека feedtools. Мы рассмотрим ее в следующем разделе.