使用ElementTree解析RSS文件

RSS订阅现在可以说是非常普遍了,可是你知道RSS文件怎么解析吗,今天从3个不同的版本用python给大家了解下如何解析。

1.先从RSS 0.9X和2.0开始:

下面是一个元素包装类,它允许您使用Python的标准属性访问语法从元素中提取字符数据:

class Wrapper:
def __init__(self, ele):
self._element = ele
def __getattr__(self, tag):
if tag.startswith("__"):
raise AttributeError(tag)
return self._element.findtext(tag)

注意,包装器对于缺少的属性/子元素返回None,除非属性名以两个下划线开头
例如,如果是一个包含RSS 2.0树的元素,那么下面的代码将打印所有条目的标题和链接值:

for item in feed.findall("channel/item"):
item = Wrapper(item)
print(repr(item.title), item.link)

这是一个包含整个RSS树的子类。这个类允许您迭代项目,并使用属性访问来获取通道级别的元素:

class RSSWrapper(ElementWrapper):
def __init__(self, feed):
channel = feed.find("channel")
ElementWrapper.__init__(self, channel)
self._items = channel.findall("item")

def __getitem__(self, index):
return ElementWrapper(self._items[index])

演示下如何使用使用包装器和urllib模块来获取和解析RSS提要的简短脚本:

from urllib import urlopen
from elementtree import ETree

URL = "http://xxx.com/rss.xml"
tree = ETree.parse(urlopen(URL))
feed = RSSWrapper(tree.getroot())
print("FEED", repr(feed.title))
for item in feed:
print("ITEM", repr(item.title), item.link)

然后是RSS 1.0

第1部分中的RSSWrapper类支持RSS 0.9x和2.0。还有第三种RSS格式RSS 1.0,它基于RDF,只是存储频道和条目信息不同。
还有与其他RSS变体不同,item元素是channel元素的兄弟元素,而不是子元素。
为了处理名称空间,我向ElementWrapper类添加了一个xs参数。如果提供该参数,则在访问属性时使用该参数作为名称空间前缀:

class ElementWrapper:
def __init__(self, element, xs=None):
self._xs = xs or ""
self._element = element
def __getattr__(self, tag):
if tag.startswith("__"):
raise AttributeError(tag)
return self._element.findtext(self._xs + tag)

此外,RSSWrapper类需要检查顶层元素,并根据实际RSS版本设置命名空间和条目列表:

XS_RDF = "{****}"
XS_RSS = "{****}"

class RSSWrapper(Wrapper):
def __init__(self, feed):
xs = None
if feed.tag == XS_RDF + "RDF":
xs = XS_RSS
items = feed.findall(XS_RSS + "item")
channel = feed.find(XS_RSS + "channel")
else:
items = channel.findall("item")
channel = feed.find("channel")
Wrapper.__init__(self, channel, xs)
self._items = items
def __len__(self):
return len(self._items)
def __iter__(self):
return iter([self[i] for i in range(len(self))])
def __getitem__(self, index):
return Wrapper(self._items[index], self._xs)

我们只需要支持两种类似的格式,检查构造函数中的不同类型就可以很好地工作,但这不是一个非常可扩展的设计。

RSS 0.9

与1.0一样,0.90格式基于RDF,但它为RSS元素使用了不同的名称空间。要区分格式,可以检查channel元素的名称空间:

XS_RSS_0_9 = "{****}"
XS_RSS_1_0 = "{****}"

def getfeed(path):
tree = ETree.parse(urlopen(path))
feed = tree.getroot()
if feed.tag == XS_RDF + "RDF":
for elem in feed:
if elem.tag.endswith("channel"):
if elem.tag.startswith(XS_RSS_0_9):
return RSS0Wrapper(feed)
if elem.tag.startswith(XS_RSS_1_0):
return RSS1Wrapper(feed)
elif feed.tag == "rss":
return RSS2Wrapper(feed)
raise IOError("unknown feed format")

这种方法的缺点是不能检查实例类型来查看你有的提要类型,也有人认为这是一种优势,并且如果需要扩展包装器接口时,没有地方可以放置特定于版本的代码。

版权所属,如需转载,请注明出处:搜闲鱼

3,149 次浏览

发表评论

邮箱地址不会被公开。 必填项已用*标注