关闭

ruby way之处理RSS和Atom

标签: rubyrss
968人阅读 评论(0) 收藏 举报
分类:
1 rss 标准库 

RSS 是基于xml的,因此你能简单的将它作为一个xml来进行解析.可是,事实上他有一个更高级别的专有的解析器来解析他.此外RSS的标准也很混乱。 

他有很多不便,就是标准的版本的不兼容,RSS有0.9, 1.0, 和2.0的版本.RSS的版本,就像制造热狗,就是一些除非你一定要知道,否则你不想要知道的细节. 

ruby有一个标准RSS库,它包含0.9,1.0和2.0版本标准.不同的版本之间尽可能做到了无缝处理.如果你没有指定版本,这个库他会自己尽可能地检测版本. 

看下面的例子,这里我们是从http://marsdrive.com 得到feed: 

Java代码  收藏代码
  1. require 'rss'  
  2. require 'open-uri'  
  3.   
  4. URL = "http://www.marstoday.com/rss/mars.xml"  
  5. open(URL) do |h|  
  6.   resp = h.read  
  7.   result = RSS::Parser.parse(resp,false)  
  8.   puts "Channel: #{result.channel.title}"  
  9.   result.items.each_with_index do |item,i|  
  10.     i += 1  
  11.     puts "#{i}  #{item.title}"  
  12.   end  
  13. end  


这个程序很简单,一直要看一下mars.xml的结构就很清晰了. 

下面我们来创建一个RSS Feed 

Java代码  收藏代码
  1. require 'rss'  
  2.   
  3. feed = RSS::Rss.new("2.0")  
  4.   
  5. chan = RSS::Rss::Channel.new  
  6. chan.description = "Feed Your Head"  
  7. chan.link = "http://nosuchplace.org/home/"  
  8.   
  9. img = RSS::Rss::Channel::Image.new  
  10. img.url = "http://nosuchplace.org/images/headshot.jpg"  
  11. img.title = "Y.T."  
  12. img.link = chan.link  
  13.   
  14. chan.image = img  
  15. feed.channel = chan  
  16.   
  17. i1 = RSS::Rss::Channel::Item.new  
  18. i1.title = "Once again, here we are"  
  19. i1.link = "http://nosuchplace.org/articles/once_again/"  
  20. i1.description = "Don't you feel more like you do now than usual?"  
  21.   
  22. i2 = RSS::Rss::Channel::Item.new  
  23. i2.title = "So long, and thanks for all the fiche"  
  24. i2.link = "http://nosuchplace.org/articles/so_long_and_thanks/"  
  25. i2.description = "I really miss the days of microfilm..."  
  26.   
  27. i3 = RSS::Rss::Channel::Item.new  
  28. i3.title = "One hand clapping"  
  29. i3.link = "http://nosuchplace.org/articles/one_hand_clapping/"  
  30. i3.description = "Yesterday I went to an amputee convention..."  
  31.   
  32. feed.channel.items << i1 << i2 << i3  
  33.   
  34. puts feed  


这个代码也很简单,就是先创建了一个空的RSS 2.0 feed ,然后给他的一些节点开始赋值. 

这里要注意我们想要把一些Item对象付给feed.channel.items时,我们不能使用 
Java代码  收藏代码
  1. feed.channel.items = [i1,i2,i3]  
这样的代码,我们只能使用items[0] = i1 这样的代码.或者用我们上面代码中的使用<<方法. 

很多人可能更喜欢用Atom,比起RSS,可是rss库不支持Atom,但是非标准的feedtools 库支持Atom.我们下面就要介绍它. 

2 feedtools Library 

feedtools他以无缝的方式工作在Atom和RSS中,它存储所有的feeds作为一个共有的内部的格式.并且它有它自己的url处理代码,因此你不需要显示的使用net/http 或者open-uri. 

下面这段代码是用feedtools来处理前面的那个例子: 

Java代码  收藏代码
  1. require "feed_tools"  
  2.   
  3. URL = "http://www.marstoday.com/rss/mars.xml"  
  4. feed = FeedTools::Feed.open(URL)  
  5. puts "Description: #{feed.title}\n"  
  6.   
  7. feed.entries.each_with_index {|x,i| puts "#{i+1} #{x.title}" }  
  8.    


这段代码比起上面的那段代码更为简洁和清晰.可以看到这里并没有显式的channel 方法,可是你却可以直接在feed对象上调用title等方法,这是因为一个feed就是一个channel. 

下面的是存取Atom的例子: 

Java代码  收藏代码
  1. require "feed_tools"  
  2.   
  3. URL = "http://www.atomenabled.org/atom.xml"  
  4. feed = FeedTools::Feed.open(URL)  
  5. puts "Description: #{feed.title}\n"  
  6.   
  7. feed.entries.each_with_index {|x,i| puts "#{i+1}  #{x.title}" }  


注意,对比一下这两个代码,不同点只有一个,那就是URL的不同,这就说明我们处理feeds时,不需要知道他被存储为哪种格式. 

现在让我们加下面的代码到上面的例子中: 

Java代码  收藏代码
  1. str = feed.build_xml("rss",2.0)  
  2. puts str  


我们所做的就是将Atom feed 转换为RSS2.0的feed.你也可以转换成0.9或者1.0版本.因此我们能读一个RSS的feed然后制造出一个Atom的feed.这个就是这个库的强大之处. 

这里要强调一下,我们上面的例子都只是一个小测试,如果你要真正做一个应用程序的话,你要注意处理你的缓存. 


ruby 1.8.7 + rails 2.1.0

打开 http://www.google.cn/finance?q=600001 这个网址 可以看到 谷歌财经的 右侧 有个新闻区。。。这个新闻区就是从别的地方抓取来的
截图:


现在我们也来仿照它来实现一个,首先rails解析rss有两种方式,一种是用封装好的类库,一种是用原始的解析xml的方式,或者利用别人封装好的库 例如feedtools, rubyrss 等
用类库的方法:
    require 'rss/2.0'
    require 'open-uri'
    url = "http://news.google.cn/news?pz=1&ned=ccn&hl=zh-CN&topic=b&output=rss"
    @feed = RSS::Parser.parse(open(url).read, false)
    @feed.items.each do |item| 
      puts item.title
      puts item.link
      puts  item.description
    end

解析xml的方法:
在lib下建立一个RssParser的类,这样在任何地方都可以调用
class RssParser
  require 'rexml/document'
  def self.run(url)
    xml = REXML::Document.new Net::HTTP.get(URI.parse(url))
    data = {
      :title    => xml.root.elements['channel/title'].text,
      :home_url => xml.root.elements['channel/link'].text,
      :rss_url  => url,
      :items    => []
    }
    xml.elements.each '//item' do |item|
      new_items = {} and item.elements.each do |e|
        new_items[e.name.gsub(/^dc:(\w)/,"\1").to_sym] = e.text
      end
      data[:items] << new_items
    end
    data
  end
end

action中使用:
  def test
    feed = RssParser.run("http://news.google.cn/news?pz=1&ned=ccn&hl=zh-CN&topic=b&output=rss")
    feed1 = feed[:items][0]
    feed2 = feed[:items][0]
    feed3 = feed[:items][0]
    # combine the feeds into an array
    @feeds = [feed1, feed2, feed3]
    # parse the pubDate strings into a DateTime object
    @feeds.each {|x| x[:pubDate] = DateTime.parse(x[:pubDate].to_s)}
    # iterate through each feed, sorting by pubDate
    @feeds.sort! {|a,b| a[:pubDate] <=> b[:pubDate]}
    # reverse the array to sort by descending pubDate
    @feeds.reverse!
    @feeds.each do |feed|
      puts feed[:title]
      puts feed[:link]
      puts feed[:pubDate]
    end
  end

那么上面的title link description 是从哪里来的呢。。。这个是rss2.0的xml结构,一般情况下是这样的:
<?xml version="1.0" encoding="utf-8"?>

<rss version="2.0">

<channel>

         <title>Example Feed</title>

<description>Insert witty or insightful remark here</description>

<link>http://example.org/</link>

<lastBuildDate>Sat, 13 Dec 2003 18:30:02 GMT</lastBuildDate>

<managingEditor>johndoe@example.com (John Doe)</managingEditor>

<item>

<title>Atom-Powered Robots Run Amok</title>

<link>http://example.org/2003/12/13/atom03</link>

<guid isPermaLink="false">urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</guid>

<pubDate>Sat, 13 Dec 2003 18:30:02 GMT</pubDate>

<description>Some text.</description>

</item>

</channel>

</rss>



或者你可以查看rss的页面源代码,或者puts下  @feed = RSS::Parser.parse(open(url).read, false)的结果都可以看到上面的这中xml文档结构



好,下面我们开始实现上面图的新闻:

我们可以把这个部分放在partial里,所以只需要helper和partial文件

helper:

def feed_collection(param)

require 'rss/2.0'

require 'open-uri'

# from news.google.cn

urlhot = "http://news.google.cn/news?pz=1&ned=ccn&hl=zh-CN&topic=b&output=rss"

urlfinance = "http://news.google.cn/news?pz=1&ned=ccn&hl=zh-CN&topic=ecn&output=rss"

urlfund = "http://news.google.cn/news?pz=1&ned=ccn&hl=zh-CN&topic=stc&output=rss"

urlfinancing = "http://news.google.cn/news?pz=1&ned=ccn&hl=zh-CN&topic=pf&output=rss"

case param

when 'hot'

RSS::Parser.parse(open(urlhot).read, false)

when 'finance'

RSS::Parser.parse(open(urlfinance).read, false)

when 'fund'

RSS::Parser.parse(open(urlfund).read, false)

when 'financing'

RSS::Parser.parse(open(urlfinancing).read, false)

end   

end



def feed_link(param)

require 'cgi'

CGI.unescape(param.slice(/(http%).*(&)/)).gsub(/&/,'')  if param # 把十六进制路径 例如http%3A2F之类的转化为 字符

end



def feed_title(param)

param.slice(/.*(-)/).gsub(/-/,"") if param #截取需要的title

end



def feed_from(param)

param.slice(/( - ).*/).from(2) if param # 截取需要的部分

end





partial: _feednews.erb.html

<div class="slides">        

<div><%= render :partial => 'shared/feednews_item',:collection => feed_collection("hot").items %></div>

<div><%= render :partial => 'shared/feednews_item',:collection => feed_collection('finance').items %></div>

<div><%= render :partial => 'shared/feednews_item',:collection => feed_collection('fund').items %></div>

<div><%= render :partial => 'shared/feednews_item',:collection => feed_collection('financing').items %></div>

</div>



主义这里参考了 jquery的loopslider 插件(幻灯片) 加载显示的只是第一个div部分,可以参考:

http://github.com/nathansearles/loopedSlider/tree/master



partial: _feednews_item.html.erb

<ul>

<% unless feednews_item.nil? %>

<li  class="news"><a  href="<%= feed_link(feednews_item.link) %>" target="_blank"><%=  feed_title(feednews_item.title) %></a>



<span class="grey small"><span> <%= feed_from(feednews_item.title) %></span> — <span><%= feednews_item.pubDate.to_date %></span></span></li>

<% end %>

</ul>



okay....已经成功了,我实现的截图:



ref:
http://www.rubycentral.com/book/ref_c_string.html
http://www.javaeye.com/topic/60620
http://www.troubleshooters.com/codecorn/ruby/basictutorial.htm#_Regular_Expressions
http://paranimage.com/15-jquery-slideshow-plugins/#respond
http://hi.baidu.com/todayz/blog/item/83c1b219d966fd4142a9ad5f.html
http://dennis-zane.javaeye.com/blog/57538
http://sporkmonger.com/projects/feedtools/
http://rubyrss.com/
http://rubyrss.com/
http://www.superwick.com/archives/2007/06/09/rss-feed-parsing-in-ruby-on-rails/
http://www.ruby-forum.com/topic/144447



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:189407次
    • 积分:2439
    • 等级:
    • 排名:第15702名
    • 原创:40篇
    • 转载:75篇
    • 译文:0篇
    • 评论:36条
    最新评论