一尘不染

clone element with beautifulsoup

python

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

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

当我使用真实文件时,修改后我不能保存document1,但是
有什么方法可以做到不破坏文档?


阅读 279

收藏
2021-01-20

共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项目被接受,并
调整了使用copy.copy()功能; 现在,
BeautifulSoup 4.4已发布,您可以使用该版本(或更高版本)并执行以下操作:

import copy

document2.body.append(copy.copy(document1.find('div', id_='someid')))
2021-01-20