用 beautifulsoup 克隆元素

问题描述 投票:0回答:3

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

如果我使用

.extract()
,它会从树中删除该元素。如果我只是附加选定的元素,如
document2.append(document1.tag)
,它仍然会从 document1 中删除该元素。

当我使用真实文件时,我无法在修改后保存 document1,但是有什么方法可以在不损坏文档的情况下做到这一点?

python beautifulsoup
3个回答
32
投票

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

要克隆一个元素及其所有元素,您必须复制所有属性并重置它们的父子关系;这必须递归地发生。最好的方法是不复制关系属性并重新定位每个递归克隆的元素:

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 项目的功能请求已被接受并调整以使用copy.copy()

功能
;现在 BeautifulSoup 4.4 已发布,您可以使用该版本(或更新版本)并执行以下操作:

import copy document2.body.append(copy.copy(document1.find('div', id_='someid')))
    

8
投票
它可能不是最快的解决方案,但它很短并且似乎有效......

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



BeautifulSoup 在克隆标签周围创建一个额外的

<html><body>...</body></html>

(为了使“汤”成为一个正常的 html 文档)。 
.body.contents[0]
 删除那些包装标签。

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


5
投票
对于 Python,您可以像这样复制父元素:

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

复制 Beautiful Soup 对象

© www.soinside.com 2019 - 2024. All rights reserved.