BS4 "元素"到底是什么,元素是如何计算的,哪个解析器可以决定?显然很困惑



我现在对我认为理解的东西感到困惑,但是事实证明我一直认为我是理所当然的。

一种经常遇到这种类型的for循环:

from bs4 import BeautifulSoup as bs
mystring = 'some string'
soup = bs(mystring,'html.parser')
for elem in soup.find_all():
    [do something with elem]

我还没有得到太多关注的是elem实际上是什么,直到我遇到了这个简化字符串的版本:

mystring = 'opening text<p>text one<BR> text two.<br></p>
<p align="right">text three<br/> text four.</p><p class="myclass">text five. </p>
<p>text six <span style="some style">text seven</span></p>
<p>text 8. <span style="some other style">text nine</span></p>closing text'

我不确定我期望的输出是什么,但是当我运行此代码时:

counter = 1 #using 'normal' counting for simplification
for elem in soup.find_all():
    print('elem ',counter,elem)
    counter +=1

输出为:

elem  1 <p>text one<br/> text two.<br/></p>
elem  2 <br/>
elem  3 <br/>
elem  4 <p align="right">text three<br> text four.</br></p>
elem  5 <br> text four.</br>
elem  6 <p class="myclass">text five. </p>
elem  7 <p>text six <span style="some style">text seven</span></p>
elem  8 <span style="some style">text seven</span>
elem  9 <p>text 8. <span style="some other style">text nine</span></p>
elem  10 <span style="some other style">text nine</span>

so bs4 html.parser在字符串中发现了10个元素。他们的选择和演示对我来说似乎并不纯粹(例如,跳过opening textclosing text(。不仅如此,print(len(soup))的输出原来是7

因此,为了确保,我将html.parser换成了lxmlhtml5lib。在这两种情况下,print(len(soup))不仅是1,而且elem S的数量跳至13!而且,自然,额外的元素是不同的。从第4个elem到末端,两个库都与html.parser相同。但是,对于前三个...

使用html5lib您得到:

elem  1 <html><head></head><body>opening text<p>text one<br/> text two.<br/></p><p align="right">text three<br/> text four.</p><p class="myclass">text five. </p><p>text six <span style="some style">text seven</span></p><p>text 8. <span style="some other style">text nine</span></p>closing text</body></html>
elem  2 <head></head>
elem  3 <body>opening text<p>text one<br/> text two.<br/></p><p align="right">text three<br/> text four.</p><p class="myclass">text five. </p><p>text six <span style="some style">text seven</span></p><p>text 8. <span style="some other style">text nine</span></p>closing text</body>

使用lxml,您可以得到:

elem  1 <html><body><p>opening text</p><p>text one<br/> text two.<br/></p><p align="right">text three<br/> text four.</p><p class="myclass">text five. </p><p>text six <span style="some style">text seven</span></p><p>text 8. <span style="some other style">text nine</span></p>closing text</body></html>
elem  2 <body><p>opening text</p><p>text one<br/> text two.<br/></p><p align="right">text three<br/> text four.</p><p class="myclass">text five. </p><p>text six <span style="some style">text seven</span></p><p>text 8. <span style="some other style">text nine</span></p>closing text</body>
elem  3 <p>opening text</p>

那么所有这些背后的哲学是什么?是谁的"错"?是否有"正确"或"错误"的答案?而且,实际上,我应该虔诚地跟随一个解析器,还是每个人都有时间和地点?

对问题的长度表示歉意。

首先,在您的情况下,root对象是soup变量,是BeautifulSoup对象。您可以像浏览器中的document对象一样想到它。在Beautifutsoup中,BeautifulSoup对象是从Element对象派生的,但实际上不是"元素"本身,更像是文档。

当您在元素上调用len(或Beautifuresoup对象(时,您会在对象的contents成员中获取节点数量。这可以包含注释,文档处理语句,文本节点,元素节点等。

一个良好的文档应该具有一个根元素,但是评论和文档处理语句在根级别也可以。在您的情况下,没有评论,也没有处理语句,我通常会期望长度为1。

lxmlhtml5lib尝试确保您拥有一个完善的文档,如果看到您有多个根元素,它们将将其包装在htmlbody标签中,并给您一个根元素。但是,如前所述,如果您的文档已经具有正确的根html元素,并且在根级别上具有注释或处理语句,则可能具有长度> 1。根据解析器,他们可以操纵其他内容以遵守他们在提供怪异畸形的HTML时也执行的任何规则。

另一方面。html.parser非常宽大。它不会试图纠正您在做什么,而只是在解析事情。在您的情况下,它返回一个怪异的文档,其中具有根级别的多个文本节点,以及根级别的多个<p>元素。因此,当您在soup上调用长度时,获得的值大于1。

通常。Beautifulsoup返回的初始元素是BeautifulSoup对象。它可能包含Element节点或NaviagableString节点(文本(,如果它们是评论,文档decleration,cdata或其他处理语句,则可能是各种子类型。NaviagableStrings(和相关的子类型(不是Element节点,而是包含在ElementBeautifulSoup对象的内容中。

取决于您是否赞成宽大处理,速度,HTML5正确性,XML支持等,它可能会影响您希望使用哪种解析器。另外,您有时可能希望在非常特定的用例中使用其他解析器。

相关内容

  • 没有找到相关文章

最新更新