BeautifulSoup 嵌套标签
我正在尝试用BeautifulSoup解析一个XML文件,但在使用“recursive”这个属性和findall()时遇到了麻烦。
我有一个比较奇怪的XML格式,如下所示:
<?xml version="1.0"?>
<catalog>
<book>
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
<book>true</book>
</book>
<book>
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
<book>false</book>
</book>
</catalog>
你可以看到,书籍标签(book)在书籍标签内部重复,这导致我在尝试做一些事情时出现错误,比如:
from BeautifulSoup import BeautifulStoneSoup as BSS
catalog = "catalog.xml"
def open_rss():
f = open(catalog, 'r')
return f.read()
def rss_parser():
rss_contents = open_rss()
soup = BSS(rss_contents)
items = soup.findAll('book', recursive=False)
for item in items:
print item.title.string
rss_parser()
在我的soup.findAll中,我添加了recursive=false,理论上这应该让它不去递归查找找到的项目,而是跳到下一个。
但这似乎不起作用,因为我总是会遇到以下错误:
File "catalog.py", line 17, in rss_parser
print item.title.string
AttributeError: 'NoneType' object has no attribute 'string'
我知道我这里可能做错了什么,如果有人能帮我解决这个问题,我会非常感激。
改变HTML结构不是一个选项,因为这段代码需要高效运行,因为它可能会解析一个很大的XML文件。
3 个回答
Beautifulsoup这个库速度慢,而且已经不再更新了,建议你用lxml这个库来代替它。:)
>>> from lxml import etree
>>> rss = open('/tmp/catalog.xml')
>>> items = etree.parse(rss).xpath('//book/title/text()')
>>> items
["XML Developer's Guide", 'Midnight Rain']
>>>
问题出在嵌套的 book
标签上。BeautifulSoup 有一套预定义的可以嵌套的标签(BeautifulSoup.NESTABLE_TAGS
),但是它不知道 book
这个标签可以嵌套,所以就出现了问题。
自定义解析器 解释了发生了什么,以及你如何可以通过子类化 BeautifulStoneSoup
来自定义可嵌套的标签。下面是我们如何用它来解决你的问题:
from BeautifulSoup import BeautifulStoneSoup
class BookSoup(BeautifulStoneSoup):
NESTABLE_TAGS = {
'book': ['book']
}
soup = BookSoup(xml) # xml string omitted to keep this short
for book in soup.find('catalog').findAll('book', recursive=False):
print book.title.string
如果我们运行这个代码,得到的输出是:
XML Developer's Guide
Midnight Rain
soup.findAll('catalog', recursive=False)
这个命令会返回一个只包含你最顶层的 "catalog" 标签的列表。因为这个标签下面没有 "title" 这个子标签,所以 item.title
的值是 None
,也就是没有值。
你可以试试 soup.findAll("book")
或者 soup.find("catalog").findChildren()
。
编辑:好的,问题不是我想的那样。试试这个:
BSS.NESTABLE_TAGS["book"] = []
soup = BSS(open("catalog.xml"))
soup.catalog.findChildren(recursive=False)