我执行以下操作:
from BeautifulSoup import *
html = u'<body><b>In Body<b>Second level</b></b></body>'
soup = BeautifulSoup(html)
soup.contents
结果我得到了:
[<body><b>In Body</b><b>Second level</b></body>]
我看起来很奇怪,因为我看不到原始的XML。最初我有一个标记<b>
,其中包含一些文字(In Body
),然后它包含另一个标记<b>
。但是,BeautifulSoup
“认为”我有标记<b>
,之后(关闭后)我有另一个标记<b>
。因此,标签不会被视为彼此嵌套。那是为什么?
ADDED
对于在我的示例中抱怨HTML有效性的人,我做了以下示例:
xml = u'<aaa><bbb>In Body<bbb>Second level</bbb></bbb></aaa>'
soup = BeautifulSoup(xml)
soup.contents
返回:
[<aaa><bbb>In Body</bbb><bbb>Second level</bbb></aaa>]
已添加2
如果我使用:
xml = u'<body><b>In Body<b>Second level</b></b></body>'
soup = BeautifulSoup(xml, ['lxml', 'xml'])
我明白了:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/BeautifulSoup.py", line 1522, in __init__
BeautifulStoneSoup.__init__(self, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/BeautifulSoup.py", line 1147, in __init__
self._feed(isHTML=isHTML)
File "/usr/local/lib/python2.7/dist-packages/BeautifulSoup.py", line 1189, in _feed
SGMLParser.feed(self, markup)
File "/usr/lib/python2.7/sgmllib.py", line 104, in feed
self.goahead(0)
File "/usr/lib/python2.7/sgmllib.py", line 138, in goahead
k = self.parse_starttag(i)
File "/usr/lib/python2.7/sgmllib.py", line 296, in parse_starttag
self.finish_starttag(tag, attrs)
File "/usr/lib/python2.7/sgmllib.py", line 338, in finish_starttag
self.unknown_starttag(tag, attrs)
File "/usr/local/lib/python2.7/dist-packages/BeautifulSoup.py", line 1344, in unknown_starttag
and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)):
AttributeError: 'list' object has no attribute 'text'
答案 0 :(得分:4)
请注意,您正在使用过时的软件包BeautifulSoup
:
这个包是OBSOLETE。它已被beautifulsoup4取代 包。你应该为所有新项目使用Beautiful Soup 4
BeautifulSoup 3包含一些XML解析功能(BeautifulStoneSoup
),它们确实无法理解再次嵌套相同的标记(如his answer中的7stud所述;因此对于所有XML解析需求,它应该是完全被彻底考虑被BeautifulSoup 4取代。请注意,即使在应用程序中,这些包也可以共存 - 对于BS3为BeautifulSoup.BeautifulSoup
,对于BS4为bs4.BeautifulSoup
。
BeautifulSoup 4默认使用HTML规则进行解析;你需要明确告诉它使用XML(需要安装lxml
)。这是BeautifulSoup 4(PyPI beautifulsoup4
)的一个例子:
>>> from bs4 import BeautifulSoup
>>> xml = u'<body><b>In Body<b>Second level</b></b></body>'
>>> soup = BeautifulSoup(xml, 'xml')
>>> soup.contents
[<body><b>In Body<b>Second level</b></b></body>]
>>> bs4.__version__
'4.1.3'
请注意,文档必须是格式良好的XML;没有宽大处理。
如果您不使用'xml'
参数,则会得到错误解析的文档:
>>> bs4.BeautifulSoup('<p><p></p></p>')
<html><body><p></p><p></p></body></html>
和
>>> bs4.BeautifulSoup('<p><p></p></p>', 'xml')
<?xml version="1.0" encoding="utf-8"?>
<p><p/></p>
答案 1 :(得分:0)
因此,标签不会被视为彼此嵌套。那是为什么?
根据BeautifulSoup源代码中的注释:
标记嵌套规则:
大多数标签根本无法嵌套。例如,a的出现
<p>
标记应隐式关闭之前的<p>
代码。<p>Para1<p>Para2 should be transformed into: <p>Para1</p><p>Para2
然后,源代码指定了几个包含标签名称的列表,这些列表根据HTML标准允许嵌套在其自身内 - 而<b>
不是其中之一。
如果我使用:
xml = u'<body><b>In Body<b>Second level</b></b></body>' soup = BeautifulSoup(xml, ['lxml', 'xml'])
我明白了:
AttributeError: 'list' object has no attribute 'text'
您收到该错误是因为您无法将列表作为参数传递给BeautifulSoup()。
为了警告BeautifulSoup你没有解析html,你需要使用BeautifulStoneSoup()
。不幸的是,我的测试显示BeautifulStoneSoup()
生成相同的xml,因此BeautifulStoneSoup()
似乎将类似的嵌套规则应用于<b>
标记。
如果您未使用BeautifulSoup 3
,则应使用lxml
或BeautifulSoup 4
。许多人认为lxml
是优质包(例如它更快,你可以使用xpath),但安装起来很困难。所以我建议你尝试安装lxml,如果可以,那就太好了。否则,请安装BeautifulSoup 4.
我这么多年来一直在使用BeautifulSoup,我更喜欢它;但是当我想使用xpaths搜索文档时,我也使用lxml。
lxml示例:
from lxml import etree
xml = '<body><b>In Body<b>Second level</b></b></body>'
tree = etree.fromstring(xml)
print etree.tostring(tree)
matching_tags = tree.xpath('/body/b/b')
inner_b_tag = matching_tags[0]
print inner_b_tag.text
--output:--
<body><b>In Body<b>Second level</b></b></body>
Second level
bs4例子:
from bs4 import BeautifulSoup
xml = '<body><b>In Body<b>Second level</b></b></body>'
soup = BeautifulSoup(xml, 'xml') #In BeautifulSoup 4, you pass a second argument to BeautifulSoup() to indicate that you are parsing xml.
print(soup)
body = soup.find('body')
inner_b_tag = body.b.b
print(inner_b_tag.string)
--output:--
<?xml version="1.0" encoding="utf-8"?>
<body><b>In Body<b>Second level</b></b></body>
Second level