【问题标题】:using beautifulsoup 4 for xml causes strange behaviour (memory issues?)对 xml 使用 beautifulsoup 4 会导致奇怪的行为(内存问题?)
【发布时间】:2012-04-07 22:12:24
【问题描述】:

我的行为很奇怪

>>> from bs4 import BeautifulSoup

>>> smallfile = 'small.xml'      #approx 600bytes
>>> largerfile = 'larger.xml'    #approx 2300 bytes
>>> len(BeautifulSoup(open(smallfile, 'r'), ['lxml', 'xml']))
1
>>> len(BeautifulSoup(open(largerfile, 'r'), ['lxml', 'xml']))
0

small.xml 的内容:

<?xml version="1.0" encoding="us-ascii"?>
<Catalog>
<CMoverMissile id="HunterSeekerMissile">
<MotionPhases index="1">
<Driver value="Guidance"/>
<Acceleration value="3200"/>
<MaxSpeed value="2.9531"/>
<Clearance value="0.5"/>
<ClearanceLookahead value="3"/>
<Outro value="-4.5,-4.25"/>
<YawPitchRoll value="MAX"/>
</MotionPhases>
<MotionPhases index="2">
<Driver value="Guidance"/>
<Acceleration value="4"/>
<MaxSpeed value="2.9531"/>
<Clearance value="0.5"/>
<ClearanceLookahead value="3"/>
<Outro value="-2.25,-2"/>
<YawPitchRoll value="MAX"/>
</MotionPhases>
</CMoverMissile>
</Catalog>

largerfile 只是较小的文件,但用空格和换行符填充(在最后两个标签之间,以防相关)。 IE xml 的结构和内容对于这两种情况应该是相同的。

在极少数情况下,处理较大的文件实际上会产生部分结果,其中只有一小部分 xml 已被解析。我似乎无法可靠地重现当时的情况。

由于 BeautifulSoup 使用 lxml,我测试了 lxml 是否可以独立处理文件。 lxml 似乎能够解析这两个文件。

>>> from lxml import etree
>>> tree = etree.parse(smallfile)
>>> len(etree.tostring(tree))
547
>>> tree = etree.parse(largerfile)
>>> len(etree.tostring(tree))
2294

我正在使用

  • 1gb 内存上网本
  • Windows 7
  • lxml 2.3(安装时遇到了一些问题,我希望安装不当不会导致问题)
  • 靓汤4.0.1
  • python 3.2(我也安装了python 2.7x,但这段代码一直使用3.2)

是什么阻止了较大的文件被正确处理?我目前的怀疑是一些奇怪的内存问题,因为文件大小似乎有所不同,可能与 BeautifulSoup 4 如何与 lxml 交互的一些错误有关。

编辑: 为了更好地说明......

>>> smallsoup = BeautifulSoup(smallfile), ['lxml', 'xml'])
>>> smallsoup
<?xml version="1.0" encoding="utf-8"?>
<Catalog>
<CMoverMissile id="HunterSeekerMissile">
<MotionPhases index="1">
<Driver value="Guidance"/>
<Acceleration value="3200"/>
<MaxSpeed value="2.9531"/>
<Clearance value="0.5"/>
<ClearanceLookahead value="3"/>
<Outro value="-4.5,-4.25"/>
<YawPitchRoll value="MAX"/>
</MotionPhases>
<MotionPhases index="2">
<Driver value="Guidance"/>
<Acceleration value="4"/>
<MaxSpeed value="2.9531"/>
<Clearance value="0.5"/>
<ClearanceLookahead value="3"/>
<Outro value="-2.25,-2"/>
<YawPitchRoll value="MAX"/>
</MotionPhases>
</CMoverMissile>
</Catalog>
>>> largersoup = BeautifulSoup(largerfile, ['lxml', 'xml'])
>>> largersoup
<?xml version="1.0" encoding="utf-8"?>

>>>

>>> repr(open(largefile, 'r').read())
'\'<?xml version="1.0" encoding="us-ascii"?>\\n<Catalog>\\n<CMoverMissile id="HunterSeekerMissile">\\n<MotionPhases index="1">\\n<Driver value="Guidance"/>\\n<Acceleration value="3200"/>\\n<MaxSpeed value="2.9531"/>\\n<Clearance value="0.5"/>\\n<ClearanceLookahead value="3"/>\\n<Outro value="-4.5,-4.25"/>\\n<YawPitchRoll value="MAX"/>\\n</MotionPhases>\\n<MotionPhases index="2">\\n<Driver value="Guidance"/>\\n<Acceleration value="4"/>\\n<MaxSpeed value="2.9531"/>\\n<Clearance value="0.5"/>\\n<ClearanceLookahead value="3"/>\\n<Outro value="-2.25,-2"/>\\n<YawPitchRoll value="MAX"/>\\n</MotionPhases>\\n</CMoverMissile>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        </Catalog>\''

注意:和\''之间有很多空格(可能不会在浏览器中显示)

【问题讨论】:

  • 1200 字节仍然很小,所以我怀疑这是问题所在。
  • 你能把xml文件的内容贴出来吗?
  • 我的怀疑(由于懒惰我没有检查)是在 beautifulsoup 对象上运行的 len 不返回字节,而是节点数或类似的东西。当然这不是内存问题'
  • 我认为 BS4 所称的 'lxml' 是 lxml HTML 解析器,因为 BeautifulSoup 主要用于 HTML。如果您只想解析 xml,请尝试使用 'xml'crummy.com/software/BeautifulSoup/bs4/doc/#parsing-xml
  • @alonisser 发布了 xml 的内容(在更改它们以使较大的文件与较小的文件相同,但带有空白填充之后)。

标签: python memory beautifulsoup lxml


【解决方案1】:

len(soup) 返回len(soup.contents) 即直接子代的数量(在本例中为单个子代&lt;Catalog&gt;)。

BeautifulSoup 无法解析largerfile 所以len(soup) == 0

【讨论】:

  • 我的问题实际上是“什么可能会阻止正确处理较大的文件?”
  • 我认为它处理得当——你检查错了。但是您可以针对特定问题提出更确切的新问题,因为 len 不是问题
  • @alonisser 正如我刚刚发布的答案中提到的,bs4/BeautifulSoup 的作者认识到 bs4/lxml 中存在错误。这解决了我的问题。重新发布格式更好的问题对您或 Stackoverflow 社区仍然有用吗?
【解决方案2】:

事实证明问题出在 BS4/LXML 的某个地方。 BS4 (BeautifulSoup) 的作者认识到问题所在 (https://groups.google.com/group/beautifulsoup/browse_thread/thread/24a82209aca4c083):

"显然 BS4+lxml 不会解析长度超过 大约 550 字节。我只用小文件对其进行了测试。 BS4 甚至没有调用处理程序代码,这使得调试变得困难, 但不能保证问题出在 lxml 方面。”

对 J.F.Sebastian 有用的代码示例稍作调整即可给出代码失败的大小:

>>> from bs4 import BeautifulSoup
>>> from itertools import count
>>> for n in count():
    s = "<a>" + " " * n + "</a>"
    nchildren = len(BeautifulSoup(s, 'xml'))
    if nchildren != 1: # broken
       print(len(s)) 
       break

1092

代码按预期处理 xml,字符数小于或等于 1091. 长于或等于 1092 的字符串的 XML 通常会失败。

更新: BeautifulSoup 4.0.2 已经发布,有一个解决方法:

“这个新版本解决了 lxml 中的一个错误 XMLParser.feed(),阻止 BS 解析 XML 文档 大于约 512-1024 个字符。 "

【讨论】:

  • 我会在 cmets 中保持更新,直到问题得到解决。更新:BS4 作者:“它看起来像 lxml 中的一个错误。bugs.launchpad.net/lxml/+bug/963936 我已经在 bzr 中提供了一个解决方法。我可能会在周一发布一个 4.0.2 的解决方法,但我想看看 lxml 开发人员是什么说。” groups.google.com/group/beautifulsoup/browse_thread/thread/…
  • 在我的情况下似乎仍然损坏。我有最新的 BS4、最新的 lxml 库,但对于更大的输入仍然失败。我可以看到旧 lxml 版本(2.3.6 之前)之间的区别,旧版本只显示几行,而现代版本(尝试 3.0.1 和 2.3.6+)除了 部分。你们是怎么解决的?我的 XML 有效但未解析:(
【解决方案3】:

在我检查后,似乎在 beautifulsoup 对象上运行 len 不会返回字节长度,而是返回一些其他类型的属性(节点深度或其他......不太确定)

【讨论】:

  • 他的示例不是在对象上运行 len(),而是在该对象的 etree.tostring() 上运行,因此它实际上是序列化中的 unicode 字符数(这不是字节,但足够接近)。
  • 我实际上也在 BeautifulSoup 对象上运行了 len。目的只是为了说明没有为更大的文件解析 xml。
  • @CharlesDuffy 实际上 - 他在对象上运行 len 然后将其与在 etree.tostring() 上运行 len 进行比较,这是两个非常不同的事情..
  • @alonisser 我在 BS4 对象上运行 len() 以测试文件是否已被解析。如果 BeautifulSoup 成功解析文件,我希望 len(BeautifulSoup(open(xmlfile, 'r'), ['lxml', 'xml'])) 返回 1。回想起来,可能有更好的方法来说明我想要实现的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 2012-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多