用BeautifulSoup克隆元素
我需要把一个文档的一部分复制到另一个文档里,但我不想改动我复制的那个文档。
如果我用 .extract()
,它会把那个元素从原来的文档中删除。如果我只是像这样添加选中的元素 document2.append(document1.tag)
,它还是会把元素从document1中移走。
因为我使用的是实际的文件,所以我可以在修改后不保存document1,但有没有什么方法可以做到这一点,而不破坏原来的文档呢?
3 个回答
对于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对象
祝好。
这个方法可能不是最快的解决方案,但它很简短,而且看起来有效……
clonedtag = BeautifulSoup(str(sourcetag)).body.contents[0]
BeautifulSoup会在克隆的标签外面多加一个<html><body>...</body></html>
,这样做是为了让“汤”变成一个合理的HTML文档。使用.body.contents[0]
可以去掉这些多余的标签。
这个想法来源于Peter Woods上面的评论和Clemens Klein-Robbenhaar下面的评论。
在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')))