【问题标题】:BS4 replace_with result is no longer in treeBS4 replace_with 结果不再在树中
【发布时间】:2020-08-15 18:29:48
【问题描述】:

我需要替换 html 文档中的多个单词。 Atm 我通过为每次替换调用一次 replace_with 来做到这一点。在 NavigableString 上调用 replace_with 两次会导致 ValueError(参见下面的示例),导致被替换的元素不再在树中。

小例子

#!/usr/bin/env python3
from bs4 import BeautifulSoup
import re
def test1():
  html = \
  '''
    Identify
  '''
  soup = BeautifulSoup(html,features="html.parser")
  for txt in soup.findAll(text=True):
    if re.search('identify',txt,re.I) and txt.parent.name != 'a':
      newtext = re.sub('identify', '<a href="test.html"> test </a>', txt.lower())
      txt.replace_with(BeautifulSoup(newtext, features="html.parser"))
      txt.replace_with(BeautifulSoup(newtext, features="html.parser"))
      # I called it twice here to make the code as small as possible.
      # Usually it would be a different newtext ..
      # which was created using the replaced txt looking for a different word to replace.        

  return soup
print(test1())

预期结果:

The txt is == newstring

结果:

ValueError: Cannot replace one element with another when the element to be replaced is not
part of the tree.

一个简单的解决方案就是修补新字符串,最后只一次全部替换,但我想了解当前的现象。

【问题讨论】:

    标签: python beautifulsoup replacewith


    【解决方案1】:

    第一个 txt.replace_with(...) 从文档树 (doc) 中删除 NavigableString(这里存储在变量 txt 中)。这有效地将txt.parent 设置为None

    第二个txt.replace_with(...) 查看parent 属性,找到None(因为txt 已从树中删除)并引发ValueError。

    正如您在问题末尾所说,一种解决方案是只使用一次.replace_with()

    import re
    from bs4 import BeautifulSoup
    
    def test1():
        html = \
        '''
        word1 word2 word3 word4
        '''
        soup = BeautifulSoup(html,features="html.parser")
    
        to_delete = []
        for txt in soup.findAll(text=True):
            if re.search('word1', txt, flags=re.I) and txt.parent.name != 'a':
                newtext = re.sub('word1', '<a href="test.html"> test1 </a>', txt.lower())
                
                # ...some computations
    
                newtext = re.sub('word3', '<a href="test.html"> test2 </a>', newtext)
    
                # ...some more computations
    
                # and at the end, replce txt only once:
                txt.replace_with(BeautifulSoup(newtext, features="html.parser"))
    
        return soup
    print(test1())
    

    打印:

    <a href="test.html"> test1 </a> word2 <a href="test.html"> test2 </a> word4
    

    【讨论】:

    • 非常感谢!你能解释一下之后的替代品在哪里吗?我一直认为 txt.replace_with(new) 会用 new 替换树中 txt 所在的区域。是不是因为 txt 不是指树中的位置,而是指现在被删除的内容,它[变量 txt] 不是指新的替换?
    • @Natan 是的,txt 内容被替换为新内容。之后txt 指的是树外的内容,而不是指新的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-05
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    相关资源
    最近更新 更多