Python 使用beautifulsoup克隆元素

Python 使用beautifulsoup克隆元素,python,beautifulsoup,Python,Beautifulsoup,我必须将一个文档的一部分复制到另一个文档,但我不想修改从中复制的文档 如果我使用.extract(),它会从树中删除元素。如果我只是像document2.append(document1.tag)那样附加所选元素,它仍然会从document1中删除该元素 由于我使用的是真实文件,修改后我无法保存文档1,但有没有办法在不损坏文档的情况下保存文档?在4.4之前的版本(2015年7月发布)中,BeautifulSoup中没有本机克隆功能;您必须自己创建一个深度副本,这很棘手,因为每个元素都维护到树的其

我必须将一个文档的一部分复制到另一个文档,但我不想修改从中复制的文档

如果我使用
.extract()
,它会从树中删除元素。如果我只是像
document2.append(document1.tag)
那样附加所选元素,它仍然会从document1中删除该元素


由于我使用的是真实文件,修改后我无法保存文档1,但有没有办法在不损坏文档的情况下保存文档?

在4.4之前的版本(2015年7月发布)中,BeautifulSoup中没有本机克隆功能;您必须自己创建一个深度副本,这很棘手,因为每个元素都维护到树的其余部分的链接

要克隆元素及其所有元素,必须复制所有属性并重置其父子关系;这必须递归地发生。最好不要复制关系属性并重新放置每个递归克隆的元素:

from bs4 import Tag, NavigableString

def clone(el):
    if isinstance(el, NavigableString):
        return type(el)(el)

    copy = Tag(None, el.builder, el.name, el.namespace, el.nsprefix)
    # work around bug where there is no builder set
    # https://bugs.launchpad.net/beautifulsoup/+bug/1307471
    copy.attrs = dict(el.attrs)
    for attr in ('can_be_empty_element', 'hidden'):
        setattr(copy, attr, getattr(el, attr))
    for child in el.contents:
        copy.append(clone(child))
    return copy
此方法对当前的BeautifulSoup版本比较敏感;我用4.3测试了这一点,未来的版本可能会添加需要复制的属性

您还可以将此功能添加到BeautifulSoup中:

from bs4 import Tag, NavigableString


def tag_clone(self):
    copy = type(self)(None, self.builder, self.name, self.namespace, 
                      self.nsprefix)
    # work around bug where there is no builder set
    # https://bugs.launchpad.net/beautifulsoup/+bug/1307471
    copy.attrs = dict(self.attrs)
    for attr in ('can_be_empty_element', 'hidden'):
        setattr(copy, attr, getattr(self, attr))
    for child in self.contents:
        copy.append(child.clone())
    return copy


Tag.clone = tag_clone
NavigableString.clone = lambda self: type(self)(self)
允许您直接对元素调用
.clone()

document2.body.append(document1.find('div', id_='someid').clone())
我对美联项目的使用;现在,BeautifulSoup 4.4已发布,您可以使用该版本(或更新版本)并执行以下操作:


这可能不是最快的解决方案,但它很短而且似乎有效

clonedtag=BeautifulSoup(str(sourcetag)).body.contents[0]

BeautifulSoup在克隆的标记周围创建一个额外的
(以便使“soup”成为一个健全的html文档)
.body.contents[0]
删除那些包装标签

这个想法来源于上面的Peter Woods评论和下面的Clemens Klein Robbenhaar评论。

对于Python:

可以复制父元素,如下所示:

import copy
p_copy = copy.copy(soup.p)
print p_copy
# <p>I want <b>pizza</b> and more <b>pizza</b>!</p>

导入副本
p_copy=copy.copy(soup.p)
打印复印件
#我想要比萨饼和更多的比萨饼

参考: 部分:复制漂亮的汤对象


问候语

也许考虑使用<代码>摘录<代码>方法,但在此之前复制标签的树位置。然后将提取的标记添加回其原始位置(使用您复制的位置信息)和新位置。@dilbert:这不起作用;您还没有副本,因此将元素插入多个位置只会更新父引用和同级引用。
.extract()的深度副本
/deepcopy/reinsert操作也不起作用,因为
navigablesting
对象假设它们是不可变的,因此实现了一个
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法,该方法返回
self
,但忽略它们有
下一个同引用是可变的。@MartijnPieters:我在黑暗中捅了一刀。我认为BS的模块化程度更高,允许多个删除和重新插入操作。您还可以创建
BeautifulSoup
的第二个实例,并将标记移到第一个实例。您现在可以使用copy.copy(tree)吗?@Overdrivr是的,前提是您有足够的最新版本如果我尝试此操作,我在标签周围得到了一个额外的
(这是应该的-BeautifulSoup试图按照要求将其变成一个正常的html文档)。要从该文档中提取隐藏的标记,还需要进一步的困难:
clonedtag=BeautifulSoup(str(sourcetag)).body.contents[0]
谢谢克莱门斯,你说得对。自从发布后,我发现我的乱码也相当慢,这表明它必须做大量的格式化工作来创建字符串,然后再次将其解析回新的BeautifulSoup实例。
import copy
p_copy = copy.copy(soup.p)
print p_copy
# <p>I want <b>pizza</b> and more <b>pizza</b>!</p>