不知何故,如果我获取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>]
作为其值
答案 0 :(得分:1)
如果您访问a.body.contents
,您获得的内容不是字符串列表,而是BeautifulSoup的Tag
个对象列表。
对于这些Tag
对象,BeautifulSoup使用与HTML / XML DOM元素类似的语义。
例如,Tag
对象具有parent
属性,该属性包含当前HTML(BeautifulSoup)文档树中该标记的父级。
如果将标记插入到另一个BeautifulSoup文档中,则插入标记的标记将成为其新父标记,并且由于无法保留其旧父标记,因此它将从旧文档中删除。这是因为每个Tag都有一个且只有一个父级。
就像树的任何元素只能是一棵树的一部分,而不是两棵树的一部分。否则,最终会遇到一个Tag有一个子列表的情况,其中一些子节点具有与此标记不同的父节点,因为它们已被移动到其他标记。这至少可能令人困惑。因此,当您将标签插入其他位置时,它会从旧位置分离。
例如,在您的情况下,a
和b
最初具有以下树结构:
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如何操作。它并不那么简单,因为您需要对所有相关数据进行深度复制。