BeautifulSoup4 find_all()在extract()或decompose()之后表现得很奇怪

时间:2015-09-16 10:11:55

标签: python beautifulsoup

我观察到一种在使用BeautifulSoup4时发现奇怪的行为。 我有以下XML(文件名:fake_product.xml):

<product acronym="ACRO1">
<formats>
    <format id="format1">
    </format>
    <format id="format2">
    </format>
    <format id="format3">
    </format>
    <format id="format4">
    </format>
    <format id="format5">
    </format>
    <format id="format6">
    </format>
</formats>
</product>

TestCase失败:

import unittest
from bs4 import BeautifulSoup


class Test(unittest.TestCase):

    def setUp(self):
        with open('fake_product.xml') as f:
            self.soup = BeautifulSoup(f, 'xml')

    def test_product_removal(self):
        output = len(self.soup.find_all('format'))
        expected = 6
        self.assertEqual(output, expected)

        format_to_delete = self.soup.find(id='format2')
        format_to_delete.extract()
        #self.soup = BeautifulSoup(self.soup.prettify(), 'xml')
        output = len(self.soup.find_all('format'))
        expected -= 1
        self.assertEqual(output, expected)

原因是find_all()无法找到所有格式。如果我这样做print self.soup.prettify()一切看起来都很好。
如果我取消注释TestCase中的注释行并在extract()之后创建一个新的BeautifulSoup对象,find_all()似乎再次正常工作并且TestCase成功。

有人可以向我解释这种行为吗?

1 个答案:

答案 0 :(得分:3)

这是4.4.0中引入的错误,请参阅BeautifulSoup 4 project bug tracker

  

在某些情况下,似乎调用extract()无法正确调整前一个元素的next_sibling属性。这将提取的元素留在后代生成器中。稍后调用find(...)find_all(...)时,搜索会终止于提取的元素,从而导致错过结果。

This bug也是相关的,包含一个潜在的修复方法:

  

第265,267,274,277行需要!=更改为is not

     

第290行需要==更改为is

我可以确认它修复了您的具体测试。

如果您对编辑BeautifulSoup源代码感到不舒服,那么解决方法就是像您一样重建树,或者降级到4.3.2直到修复程序出来为止。