Apache Commons FeedParser读取RSS及Atom

这段时间闲来无事想回顾下对RSS操作的API,刚好想要抓取OSChina的新闻。结果“可恶”的 @红薯 竟然对RSS读取做了UA限制。过程中出现这么一个小插曲是我没想到的,不过也好,这样才好玩。接下来,我将对Apache Commons FeedParser的使用做一个简单的介绍,并用它来读取OSChina的RSS新闻。

RSS & Atom

关于RSS的wikipedia
http://en.wikipedia.org/wiki/RSS

关于Atom的wikipedia
http://en.wikipedia.org/wiki/Atom_(standard)

关于RSS和Atom的介绍,可以参照上面的wiki页面。总体说来,无论是RSS或Atom都只是某种XML罢了。网站通过一种“标准”的形式将自己网站的内容写入XML文件中并对外公布,开发者则可以通过解析该XML从而得到网站公布的内容。通过“订阅”RSS或Atom栏目,读者朋友可以不登陆网站而看到该网站实时发布的内容。但由于某些网站不希望影响了流量,因此并不支持这种订阅方式。还有一些网站,RSS或Atom内容中并不输出全文。

RSS示例:


<channel>
<title>开源中国社区最新新闻</title> 
<link>http://www.oschina.net/?from=rss</link> 
<description>开源中国社区——找到您想要的开源软件,分享和交流</description>
<language>zh-CN</language>
<pubDate>Thu, 18 Apr 2013 11:09:39 +0800</pubDate>    
<image>
    <link>http://www.oschina.net</link>
    <url>http://www.oschina.net/img/logo.gif</url>
    <title>OsChina.NET</title>
</image>
<item>
    <title>UPUPW Nginx版PHP高配引擎发布</title>
    <link>http://www.oschina.net/news/39720/upupw-nginx-php-engine</link>
    <category>软件更新新闻</category>
    <description>新闻内容</description>
    <pubDate>Thu, 18 Apr 2013 11:09:39 +0800</pubDate>
    <guid>http://www.oschina.net/news/39720/upupw-nginx-php-engine</guid>
</item>
<item>
    .........
</item>

Atom示例:


<feed xmlns="http://www.w3.org/2005/Atom">
<title>Katana Photo Groups: (Latest Updates)</title>
<subtitle>This is the place to go for the latest photo updates on Katana Photo Groups</subtitle>
<link rel="alternate" href="http://www.katanapg.com/latest"/>
<link rel="self" href="http://www.katanapg.com/xml/latest/atom.xml"/>
<updated>2012-10-31T01:17:29+00:00</updated>
<author>
       <name>Nick Pappas</name>
 </author>
 <logo>/images/rss.gif</logo>
 <icon>/favicon.ico</icon>
 <category term="photos"/>
 <id>urn:uuid:www.katanapg.com-latest-xml</id>
 <entry>
       <title>10/31 1AM - Guns FS [By: Parker Benda]</title>
       <link rel="alternate" href="http://www.katanapg.com/group/2747"/>
       <link rel="enclosure" href="http://misuzu.katanapg.com/112/1203191732c60304/thumb.jpg" length="1000"/>
       <id>urn:uuid:www.katanapg.com-group-2747</id>
       <updated>2012-10-31T01:17:29+00:00</updated>
       <content type="xhtml">atom内容</content>
 </entry>
 <entry>
          atom......
  </entry>

读着朋友们可以比较上述两个示例,RSS和Atom的区别我就不再论述了。这些区别或者文档结构其实并不困难,随便找几个XML解析的API(如:DOM、SAX、Stax)都可以。当然,业界也存在许多解析XML的框架,如:Dom4j、JDOM等。更有专门用来解析RSS和Atom的Rome框架,以及今天将要讲到的Apache Commons FeedParser。如果你稍微留意就会发现,问题不在于这些API或者框架多么复杂。关键问题在于:各种网站对于RSS和Atom标准的遵守,实在令人蛋疼不已。

Apache Commons FeedParser

FeedParser作为专门用来解析RSS和Atom的框架而言,还是非常不错的。其相关历史及FeedParser和ROME的比较等我不再提起,感兴趣的朋友可以去FeedParser官网查询。

Apache Commons FeedParser
http://commons.apache.org/dormant/feedparser/

在FeedParser中提供了某种时间机制(类似于SAX解析)来完成对RSS和Atom的解析工作。因此,开发者所需要做的事情即是实现某种Listener即可。针对不同的需求,FeedParser中提供了若干不同的接口或默认实现类,常用的如下:

FeedParser中常用事件接口及实现类

MetaFeedParserListener接口

DefaultFeedParserListener类

DefaultFeedDirectoryParserListener类

上述两个类均实现了MetaFeedParserListener接口,均可作为默认实现类继承。它已经帮我们做好了许多事情,若想偷懒就可以用上述两个类。若需要更加复杂的处理,则不得不实现MetaFeedParserListener接口。

为了能够顺利读取OSChina咨询栏目的RSS内容,我们需要定义一个监听器类,其内容大致如下:


public class OSChinaRssFeedsListener extends DefaultFeedParserListener {

public void onChannel(FeedParserState state, String title, String link,
        String description) throws FeedParserException {
    //获取channel信息
}

public void onChannelEnd() throws FeedParserException {
    //channel信息获取完毕
}

public void onItem(FeedParserState state, String title, String link,
        String description, String permalink) throws FeedParserException {
    //获取item信息
}

public void onItemEnd() throws FeedParserException {
    //item信息获取完毕
}

public void onCreated(FeedParserState state, Date date)
        throws FeedParserException {
    //获取channel或item创建时间
}

public void onCreatedEnd() throws FeedParserException {
    //创建时间获取完毕
}
}

根据上述几个方法可以看出,我们可以很容易的使用上述监听器获取OSChina咨询RSS的channel信息及每个item的信息,并对其进行一定的处理。上述方法中也可以看出,其中之对RSS或Atom中部分信息进行了解析(只有channel、item及对应创建时间)。如果需要对RSS或Atom中其他信息进行操作,比如author、entry、image等,则需要我们自己实现其响应的方法。你可以实现上述MetaFeedParserListener接口,其中包含的方法有:

MetaFeedParserListener接口中包含的方法:

void onAuthor(FeedParserState state, String name, String email, String resource)

void onAuthorEnd()

void onComments(FeedParserState state, String resource)

void onCommentsEnd()

void onCommentsFeed(FeedParserState state, String resource)

void onCommentsFeedEnd()

void onCopyright(FeedParserState state, String content)

void onCopyrightEnd()

void onCreated(FeedParserState state, Date date)

void onCreatedEnd()

void onGenerator(FeedParserState state, String content)

void onGeneratorEnd()

void onGUID(FeedParserState state, String value, boolean isPermalink)

void onGUIDEnd()

void onIssued(FeedParserState state, String content)

void onIssuedEnd()

void onLocale(FeedParserState state, Locale locale)

void onLocaleEnd()

void onSubject(FeedParserState state, String content)

void onSubjectEnd()

上述方法可以看出,无论是RSS还是Atom,大部分的信息都可以通过这些接口获取并进行一定处理。相信它可以基本完成我们所需要的解析工作。

实现了自己的监听器逻辑之后,就可以通过如下方法执行并获取结果了:


String feedUrl =  "http://www.oschina.net/news/rss";
OSChinaRssFeedsListener listener = new OSChinaRssFeedsListener();
FeedParserFactory.newFeedParser().parse(listener, new URL(feedUrl).openStream(), feedUrl);

关于 @红薯 做的User-Agent限制问题

“某些不良网站”经常对RSS或Atom进行UA限制已防止“不良用心”之人做坏事。既然咱不是“不良用心”之人,那又要如何呢?
我猜:红薯应该是对rss读取的网络请求User-Agent进行了某种判断。如果是正常途径通过浏览器访问则放行通过,如果User-Agent为null或一些特殊的字符,就返回一个403错误。为此我还在社区问过红薯,“可恶的”竟然回复“哈哈”。尼玛,红薯竟然回复“哈哈”有木有?有木有?!

对策:java代码里发出的网络请求User-Agent既然被“过滤”,那只好写一段代码将自己的java代码“伪装”成为正常途径查看就好。至于怎么做,请大家自行在网络上搜索。

不过,Apache Commons FeedParser中也对这个问题进行了一定的处理。我们可以通过调用其提供的Network IO的几个方法解决这个问题。使用Apache Commons FeedParser提供的方法会默认生成如下User-Agent信息(“可恶的”红薯不会把这个也禁用吧?!)

Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.2.1; aggregator:FeedParser; http://commons.apache.org/sandbox/feedparser/) Gecko/20021130

因此,我们需要修改上述Java代码,不要直接发送rss请求,而是通过FeedParser提供的ResourceRequestFactory进行封装然后再发送,就成功“骗过”红薯做的UA限制了。


String feedUrl =  "http://www.oschina.net/news/rss";
OSChinaRssFeedsListener listener = new OSChinaRssFeedsListener();
InputStream is = ResourceRequestFactory.getResourceRequest(feedUrl).getInputStream();
FeedParserFactory.newFeedParser().parse(listener, is, feedUrl);

FeedParser中还有什么?

Apache Commons FeedParser中还提供了很多很有意思的工具类可以使用。比如:FeedLocator类可以用来查找某个网址中可能存在的RSS或Atom地址。我试着用它抓去OSChina中各种RSS和Atom信息,大家看看:

通过FeedLocator.locate(“http://www.oschina.net”)抓取出来的结果:

http://www.oschina.net/code/rss

http://www.oschina.net/code/source_rss

http://www.oschina.net/question/rss

http://www.oschina.net/project/rss

have fun :)

你可能感兴趣的:(apache,rss,commons,ATOM,feedparser)