美丽的汤tag.contents项目从不同的汤实例中删除

时间:2015-03-06 18:13:06

标签: python beautifulsoup

不知何故,如果我获取BeautifulSoup实例'a'的内容并将其插入实例'b',它会将其从实例'a'中删除。这有意义吗?

a = BeautifulSoup('<p>0</p><p>1</p>')
b = BeautifulSoup('<p>2</p>')
additions = a.body.contents
while additions:
    b.body.insert(0, additions[-1])

'a'最终会像

一样
<html><body></body></html>

'b'最终会像

一样
<html><body><p>0</p><p>1</p><p>2</p></body></html>

无限循环,我希望'a'不变。我只是没有正确阅读文档吗?

如果我在循环之前复制'添加'(类似not_a_problem = additions[:]),则副本不会受到影响 - 这意味着它将[<p>0</p>, <p>1</p>]作为其值

1 个答案:

答案 0 :(得分:1)

如果您访问a.body.contents,您获得的内容不是字符串列表,而是BeautifulSoup的Tag个对象列表。 对于这些Tag对象,BeautifulSoup使用与HTML / XML DOM元素类似的语义。

例如,Tag对象具有parent属性,该属性包含当前HTML(BeautifulSoup)文档树中该标记的父级。

如果将标记插入到另一个BeautifulSoup文档中,则插入标记的标记将成为其新父标记,并且由于无法保留其旧父标记,因此它将从旧文档中删除。这是因为每个Tag都有一个且只有一个父级。

就像树的任何元素只能是一棵树的一部分,而不是两棵树的一部分。否则,最终会遇到一个Tag有一个子列表的情况,其中一些子节点具有与此标记不同的父节点,因为它们已被移动到其他标记。这至少可能令人困惑。因此,当您将标签插入其他位置时,它会从旧位置分离。

例如,在您的情况下,ab最初具有以下树结构:

 a = Tag(html)         b = Tag(html)
       |                       |
    Tag(body)              Tag(body)
     /    \                    |
 Tag(p)   Tag(p)             Tag(p)
   |        |                  |
Str('0') Str('1')           Str('2')

Str这里是BeautifulSoups NavigableString的简写,它与DOM中的TextNode松散对应)

现在,您将第二个p代码从a移至b&#39;身体通过b.body.insert(0, a.body.contents[-1])b的结构现在看起来像:

  b = Tag(html)
       |
    Tag(body)
     /    \
 Tag(p)   Tag(p)
   |        |
Str('1') Str('2')

但是,此标记的parent现在是b的正文,而不再是a的正文。如果a.body的内容中仍有标记,则您将拥有无效数据结构,例如

   a = Tag(html) Tag(html) = b
       |             |
    Tag(body)    Tag(body)
     /    \      /      \
 Tag(p)    Tag(p)      Tag(p)
   |         |           |
Str('0')  Str('1')    Str('2')

那不行; <p>1</p>中包含b,如果它仍然位于contents的{​​{1}},那么您将遇到a.body中的一个元素的情况a.body.contents parent本身不是a.body.contents

相反,您最终(正如您已经正确观察到的那样)使用如下数据结构:

 a = Tag(html)           b = Tag(html)
       |                       |
    Tag(body)              Tag(body)
       |                    /     \
     Tag(p)              Tag(p)  Tag(p)
       |                   |       |
    Str('0')            Str('1') Str('2')

据我所知,文档中没有提到,也许是因为文档的作者认为这是某种事情&#34;每个人都知道&#34;。

如果要在文档树之间复制Tag对象,则需要克隆;然后你最终会得到类似的东西:

 a = Tag(html)         b = Tag(html)
       |                       |
    Tag(body)              Tag(body)
     /    \                 /     \
 Tag(p)   Tag(p)         Tag(p)  Tag(p)
   |        |              |       |
Str('0') Str('1')       Str('1') Str('2')

在这种情况下,请查看clone element with beautifulsoup如何操作。它并不那么简单,因为您需要对所有相关数据进行深度复制。