简单介绍Python中的RSS处理
RSS是一个可用多种扩展来表示的缩写:“RDF站点摘要(RDFSiteSummary)”、“真正简单的辛迪加(ReallySimpleSyndication)”、“丰富站点摘要(RichSiteSummary)”,也许还能用其他扩展来表示。在如此混乱的名称背后,您会发现和这样一个平凡的技术领域相关的故事多得令人吃惊。RSS是用于分发Web站点上的内容的摘要的一种简单的XML格式。它能够用于共享各种各样的信息,包括(但不是仅限于)简讯、Web站点更新、事件日历、软件更新、特色内容集合和基于Web进行拍卖的商品。
RSS是Netscape在1999年创建的,它允许将许多信息源中的内容聚集到Netcenter门户网站中(这个门户网站现在已经不存在了)。UserLand社区中的Web狂热爱好者们成为了RSS早期的支持者,RSS很快就成为了一种非常流行的格式。这种流行使得人们很难对RSS进行改进从而使它能用于更多的地方。这种限制导致了RSS的发展出现了分歧。其中一个组选择了一种基于RDF的方式,目的在于利用大量的RDF工具和模块,而另一个组则选择了一种更加紧缩的方式。前者被称之为RSS1.0,而后者则被称之为RSS0.91。就在上个月由于RSS非RDF变体的一个新版本的出现使得两者之间的竞争进一步加剧,这一新版本被它的创造者称之为“RSS2.0”。
RSS0.91和1.0非常流行,并且许许多多的门户网站和Web日志都使用它们。事实上,blogging社区是RSS的主要用户,而RSS就是某些现有的用于XML交换的网络令人印象深刻的理由。这些网络已在有机地增长,并且真正地成为现有的最成功的XML服务的网络。RSS成为一种XML服务是因为它被通过网际协议交换XML信息(绝大多数的RSS交换是RSS文档的简单HTTPGET)。在本文中,我们介绍的不过是许多可以与RSS一起工作的Python工具中的少数几个。我们不提供针对RSS的技术上的介绍,因为您可以在其他许多的文章中获得这些内容。(请参阅参考资料)。我们推荐您首先简单地熟悉一下RSS知识,并且能了解XML。您不需要去了解RDF。
[由于RSS使用了XML描述而没有使用WSDL,所以我们将RSS作为一个“XML服务”而不是一个“Web服务”对待。-编者按]
RSS.py
MarkNottingham编写的RSS.py是用于RSS处理的一个Python库。它非常完善并且编写的很好。它需要Python2.2和PyXML0.7.1。它的安装是非常简单;您只需从Mark的主页中下载Python文件并将它复制到您的PYTHONPATH中的某处。
大多数RSS.py的用户本身只需要关心它所提供的两个类:CollectionChannel和TrackingChannel。后者似乎是这两个类中更有用的一个。TrackingChannel是一个包含以每一项的关键字为索引的所有的RSS数据的数据结构。CollectionChannel是一个类似的数据结构,但它的结构更像RSS文档本身,它的顶层通道信息使用URL表示的散列值指向项细节。您很可能会使用RSS.ns结构中的实用程序名称空间声明。清单1是一个简单的脚本,它将下载并解析用于Python新闻的RSS供给,并以一个简单的清单形式打印来自各项的所有信息。
清单1:使用RSS.py的一个简单练习
fromRSSimportns,CollectionChannel,TrackingChannel #Createatrackingchannel,whichisadatastructurethat #IndexesRSSdatabyitemURL tc=TrackingChannel() #ReturnstheRSSParserinstanceused,whichcanusuallybeignored tc.parse("http://www.python.org/channews.rdf") RSS10_TITLE=(ns.rss10,'title') RSS10_DESC=(ns.rss10,'description') #Youcanalsousetc.keys() items=tc.listItems() foriteminitems: #Eachitemisa(url,order_index)tuple url=item[0] print"RSSItem:",url #GetallthedatafortheitemasaPythondictionary item_data=tc.getItem(item) print"Title:",item_data.get(RSS10_TITLE,"(none)") print"Description:",item_data.get(RSS10_DESC,"(none)")
我们从创建一个TrackingChannel实例开始,并且将从http://www.python.org/channews.rdf上的RSS供给解析得到的数据填入其中。RSS.py使用元组作为RSS数据的属性名称。对于那些不习惯XML处理技术的人来说,这种方式看上去也许不太寻常,但它对于精确了解原始的RSS文件中的内容的确是一种很有效的方式。因此,一个RSS0.91title元素被认为不同于一个RSS1.0中的同名元素。应用程序有足够的数据来忽略这个差异,如果您愿意,可以通过忽略每个元组的名称空间的部分来忽略这个差异;但基本的API是与初始RSS文件的语法相结合的,所以这个信息没有丢失。在代码中,我们使用这个属性数据来聚集用于显示的新闻供给中的所有项。请注意,我们很仔细地不去假定任何特殊的项可能会有什么属性。我们使用如下代码所示的安全的形式来检索属性。
print"Title:",item_data.get(RSS10_TITLE,"(none)")
如果没有找到该属性则它会提供一个缺省值,而不是这个示例。
print"Title:",item_data[RSS10_TITLE]
由于您不可能会知道RSS供给中所使用的是什么元素,因此这样的谨慎是有必要的。清单2显示了清单1的输出。
清单2:清单1的输出
$pythonlisting1.py RSSItem:http://www.python.org/2.2.2/ Title:Python2.2.2b1 Description:(none) RSSItem:http://sf.net/projects/spambayes/ Title:spambayesproject Description:(none) RSSItem:http://www.mems-exchange.org/software/scgi/ Title:scgi0.5 Description:(none) RSSItem:http://roundup.sourceforge.net/ Title:Roundup0.4.4 Description:(none) RSSItem:http://www.pygame.org/ Title:Pygame1.5.3 Description:(none) RSSItem:http://www.cosc.canterbury.ac.nz/~greg/python/Pyrex/ Title:Pyrex0.4.4.1 Description:(none) RSSItem:http://www.tundraware.com/Software/hb/ Title:hb1.88 Description:(none) RSSItem:http://www.tundraware.com/Software/abck/ Title:abck2.2 Description:(none) RSSItem:http://www.terra.es/personal7/inigoserna/lfm/ Title:lfm0.9 Description:(none) RSSItem:http://www.tundraware.com/Software/waccess/ Title:waccess2.0 Description:(none) RSSItem:http://www.krause-software.de/jinsitu/ Title:JinSitu0.3 Description:(none) RSSItem:http://www.alobbs.com/pykyra/ Title:PyKyra0.1.0 Description:(none) RSSItem:http://www.havenrock.com/developer/treewidgets/index.html Title:TreeWidgets1.0a1 Description:(none) RSSItem:http://civil.sf.net/ Title:Civil0.80 Description:(none) RSSItem:http://www.stackless.com/ Title:StacklessPythonBeta Description:(none)
当然,你可能会遇到稍微有些不同的输出,这是因为在您对它进行试验时新闻项可能已经更改了。RSS.py通道对象也提供方法来添加并修改RSS信息。您可以使用output()方法将结果写回RSS1.0格式。通过将在清单1中解析的信息写回去来对它进行试验。在交互式模式下通过运行python-ilisting1.py来启动脚本。在产生的Python提示符下,运行以下示例。
>>>result=tc.output(items) >>>printresult
结果是一个打印输出的RSS1.0文档。为了它能工作您必须有RSS.py,版本0.42或者更高的版本。较早版本中的output()方法中有一个错误。
rssparser.py
MarkPilgrim为RSS文件解析提供了另一个模块。它并不提供RSS.py所提供的所有的功能部件和选项,但它却提供了一个非常自由的解析器,它能很好的处理RSS世界中所有令人混乱的差异。以下摘自rssparser.py页面:
如您所见,大多数RSS供给都很糟糕。无效的字符、未转义的&符号(Blogger供给)、无效的实体(Radio供给)和未转义以及无效的HTML(通常为注册中心所提供的)。或者只是RSS0.9x元素和RSS1.0元素的一个笼统的混合(可移动类型供给(MovableTypefeeds))。
还有许多太前沿的供给,就象Aaron的feed。他将一个摘录放入描述元素中而将完整的文本放入content:encoded元素中(象CDATA)。这是一个有效的RSS1.0,但没有人回真正使用它(除了Aaron),几乎没有新闻聚集器支持它,并且许多解析器还排斥它。其他解析器被RSS0.94中的新元素(guid)所困惑(请参阅DaveWiner的供给作为一个示例)。还有JonUdell的供给,其中还有他才从创作中挑选出来的fullitem元素。
XML和Web服务会增加互操作性几乎已成定局,所以这样考虑其实很可笑。无论如何,设计rssparser.py目的就是要处理所有这些荒唐的情况。
安装rssparser.py也十分简单。请您下载Python文件(参阅参考资料),将“rssparser.py.txt”重命名为“rssparser.py”,并将它复制到您的PYTHONPATH中。我同样建议您取得可选的timeoutsocket模块,它可以改进Python中的套接字操作的超时行为,这样有助于取得RSSfeeds而不必为了防止错误就停止应用程序线程。
清单3是一个等同于清单1的脚本,但它使用了rssparser.py,而不是RSS.py。
清单3:使用一个简单的rssparser.py练习
importrssparser #Parsethedata,returnsatuple:(dataforchannels,dataforitems) channel,items=rssparser.parse("http://www.python.org/channews.rdf") foriteminitems: #Eachitemisadictionarymappingpropertiestovalues print"RSSItem:",item.get('link',"(none)") print"Title:",item.get('title',"(none)") print"Description:",item.get('description',"(none)")
如您所见,这段代码非常简单。RSS.py和rssparser.py不能互相取代在很大程度上是因为前者有更多的功能部件,并且维护着RSS供给中更多的语法信息。后者更简单,并且是一个容错能力更强的解析器(RSS.py解析器只能接受格式良好的XML)。
它的输出应该与清单2中的输出相同。