用BeautifulSoup克隆元素

22 投票
3 回答
13435 浏览
提问于 2025-04-18 02:40

我需要把一个文档的一部分复制到另一个文档里,但我不想改动我复制的那个文档。

如果我用 .extract(),它会把那个元素从原来的文档中删除。如果我只是像这样添加选中的元素 document2.append(document1.tag),它还是会把元素从document1中移走。

因为我使用的是实际的文件,所以我可以在修改后不保存document1,但有没有什么方法可以做到这一点,而不破坏原来的文档呢?

3 个回答

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>

参考链接:https://www.crummy.com/software/BeautifulSoup/bs4/doc/,章节:复制Beautiful Soup对象

祝好。

8

这个方法可能不是最快的解决方案,但它很简短,而且看起来有效……

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

BeautifulSoup会在克隆的标签外面多加一个<html><body>...</body></html>,这样做是为了让“汤”变成一个合理的HTML文档。使用.body.contents[0]可以去掉这些多余的标签。

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

32

在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项目的一个功能请求得到了接受,并且被调整为使用copy.copy()函数;现在BeautifulSoup 4.4版本已经发布,你可以使用这个版本(或更新的版本)来进行:

import copy

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

撰写回答