Beautiful Soup tag.contents 项从不同的汤实例中移除
Beautiful Soup tag.contents item removed from different soup instance
不知何故,如果我将 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' 保持不变。我只是没有正确阅读文档吗?
如果我在循环之前制作 'additions' 的副本(类似于 not_a_problem = additions[:]
),副本将保持原状——这意味着它的值将是 [<p>0</p>, <p>1</p>]
如果您访问 a.body.contents
,您得到的不是字符串列表,而是 BeautifulSoup 的 Tag
对象列表。
对于这些 Tag
对象,BeautifulSoup 使用与 HTML/XML DOM 元素相似的语义。
例如,Tag
对象有一个 parent
属性,它包含当前 HTML (BeautifulSoup) 文档树中该标签的父级。
如果您将标签插入到不同的 BeautifulSoup 文档中,插入它的标签将成为其新的父标签,并且由于它无法保留其旧的父标签,因此将从旧文档中删除。那是因为每个标签都有一个且只有一个父标签。
就像一棵树的任何元素只能是一棵树的一部分,而不是两棵树。否则你最终会遇到这样的情况,其中一个标签有一个子列表,其中一些子标签的父标签与这个标签不同,因为它们已被移动到其他标签。至少这可能令人困惑。因此,当你在不同的地方插入一个标签时,它就会从原来的地方分离。
例如,在您的情况下,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
的 shorthand,它大致对应于 DOM 中的 TextNode)
现在,当您通过 b.body.insert(0, a.body.contents[-1])
将第二个 p
标签从 a
移动到 b
正文时,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
中,如果它仍然在 a.body
的 contents
中,那么您会遇到 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')
据我所知,文档中没有提及,可能是因为文档的作者假设这是 "everybody knows".
如果要在文档树之间复制 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 如何操作。这并不那么简单,因为您需要对所有相关数据进行深拷贝。
不知何故,如果我将 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' 保持不变。我只是没有正确阅读文档吗?
如果我在循环之前制作 'additions' 的副本(类似于 not_a_problem = additions[:]
),副本将保持原状——这意味着它的值将是 [<p>0</p>, <p>1</p>]
如果您访问 a.body.contents
,您得到的不是字符串列表,而是 BeautifulSoup 的 Tag
对象列表。
对于这些 Tag
对象,BeautifulSoup 使用与 HTML/XML DOM 元素相似的语义。
例如,Tag
对象有一个 parent
属性,它包含当前 HTML (BeautifulSoup) 文档树中该标签的父级。
如果您将标签插入到不同的 BeautifulSoup 文档中,插入它的标签将成为其新的父标签,并且由于它无法保留其旧的父标签,因此将从旧文档中删除。那是因为每个标签都有一个且只有一个父标签。
就像一棵树的任何元素只能是一棵树的一部分,而不是两棵树。否则你最终会遇到这样的情况,其中一个标签有一个子列表,其中一些子标签的父标签与这个标签不同,因为它们已被移动到其他标签。至少这可能令人困惑。因此,当你在不同的地方插入一个标签时,它就会从原来的地方分离。
例如,在您的情况下,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
的 shorthand,它大致对应于 DOM 中的 TextNode)
现在,当您通过 b.body.insert(0, a.body.contents[-1])
将第二个 p
标签从 a
移动到 b
正文时,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
中,如果它仍然在 a.body
的 contents
中,那么您会遇到 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')
据我所知,文档中没有提及,可能是因为文档的作者假设这是 "everybody knows".
如果要在文档树之间复制 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 如何操作。这并不那么简单,因为您需要对所有相关数据进行深拷贝。