【问题标题】:Reading from XML files in a loop eventually causes memory error循环读取 XML 文件最终会导致内存错误
【发布时间】:2020-11-28 12:56:35
【问题描述】:

案例:

我正在尝试读取一个 XML 文件,使用 BeautifulSoup 从其中提取少量数据,将数据添加到字典中,关闭文件,然后进入下一个文件。提取所需数据后,应关闭文件并从内存中释放。

问题:

程序最终会因内存错误而停止,并且任务管理器清楚地显示每个文件之后的内存消耗量都在增加,这让我相信我的文件没有正确关闭或从内存中释放。在我的环境中,这将在读取大约 200 个文件后发生。

我尝试过但没有成功的事情:

  • 用 gc.collect() 收集垃圾(似乎没什么区别)

  • 用soup.decompose()分解文件(好像没什么区别)

  • 各种不同大小的文件

  • SoupStrainer(有/没有它几乎没有区别)

我找到了 2 个“解决方案”:

  • 强制脚本在一段时间后自行重启(不是最佳的)

  • 64 位版本和更多物理内存(不是最佳)

文件信息:

  • 大小从 100kb 到 5mb 不等
  • 每个文件 10.000 到 70.000 行。
  • 标准 .xml 格式

EXML 结构示例/来自文件的 sn-p。 (最多可以有 70.000 行):

<!-- language: xml -->
<Partner>
  <Language>en-US</Language>
  <PartnerRole>stackoverflow1</PartnerRole>
  <IsSalesAreaDependent>True</IsSalesAreaDependent>
  <ContactPerson>
    <ContactPerson>
      <Language>en-US</Language>
    </ContactPerson>
  </ContactPerson>
  <InheritFromSoldTo>True</InheritFromSoldTo>
  <SalesAreaData>
    <SalesAreaData>
      <Language>en-US</Language>
      <Valid>False</Valid>
      <SalesOrganization>stackoverflow2</SalesOrganization>
      <DistributionChannel>stackoverflow3</DistributionChannel>
      <SalesDivision>stackoverflow4</SalesDivision>
      <CustomerGroup />
      <Currency>stackoverflow5</Currency>
      <PriceGroup />
      <PriceList>stackoverflow6</PriceList>
      <ShippingConditions />
      <Plant />
      <PaymentTerms />
    </SalesAreaData>
  </SalesAreaData>
  <CustomerHierarchy />
</Partner>

代码:

for fname in glob.glob(path+"/Quotes/**/*.quote"): #Further define path

    with open(fname, encoding="utf8") as open_file:

        gc.collect()
        counter += 1
        contents = open_file.read()
        soup = BeautifulSoup(contents, 'lxml')

        try:
            results = ("("+str(counter)+") " + " Ref: " + soup.quickref.string + " Last modified: " + soup.modifieddate.string)
            bsize = os.path.getsize(fname)
            totalsize += bsize

            tempdata = (soup.modifieddate.string, soup.quickref.string, soup.ownerusername.string, soup.companyname.string, soup.totalnetvalue.string, fname)
            dictHolder[counter] = tempdata

        except AttributeError:

            results = "("+ str(counter) + ")" + "Invalid data / corrupted file, please check: " + fname
            corruptCounter += 1

        soup.decompose()
        gc.collect()
        print (results)

10/08/2020:问题已通过切换到 xml.etree.elementtree 模块“解决”,并不能真正算作答案或解决方案,但如果将来有人遇到同样的问题并阅读这个,试试上面的模块。

【问题讨论】:

  • with open 块不应该在 for 循环中吗?
  • 很好看。它在实际代码中,但我似乎无法弄清楚如何在 stackoverflow 上缩进它。如果有人熟悉 StackOverflow 格式,请缩进该部分,谢谢。
  • 不知道为什么需要BeautifulSoup,但可以直接使用底层引擎lxml,它可以在没有open 的情况下解析文件。此外,.read is memory-intensive 并且可能不会在 for 循环内释放回来。请发布 XML 示例,以便我们可以看到树。
  • 我用 XML 结构的示例更新了 OP。可能值得注意的是,每个循环不仅消耗更多内存,而且每个循环的速度也越来越慢(尽管实际文件的大小或复杂性没有增加)我不一定“需要” beatifulsoup,但我发现它很容易与我在 Python 中相对初学者的技能相关的工作。
  • 10/08 更新:通过将模块切换到“xml.etree.ElementTree”,问题已“解决”,否则代码相同或相似。切换到不同的模块并不能真正算作一个答案,所以我会留下这个,以防有人发现它有用或想通了。总处理时间从 ~2 小时缩短到 ~5 分钟。

标签: python xml memory beautifulsoup


【解决方案1】:

我对beautifulsoup 了解不多......但是用pandas 读取数千个csv 文件并将其存储在字典中对我来说很有效,只需阅读并将其添加到字典中即可。 您可以尝试使用 pandas 读取文件并检查问题是否出现在读取第 200 个文件时。如果是这种情况,我假设它是 RAM 问题。

【讨论】:

  • 我很欣赏这个建议,但是,如果这是我可能不得不做的唯一解决方案,切换到不同的模块并不是可取的。
【解决方案2】:

试试..

del contents

可能是垃圾无法删除它,因为仍然有一个针对该对象的引用计数器。

也尝试运行此代码....

sorted([(x, round(sys.getsizeof(globals().get(x)) / 1000000, 2)) for x in dir() if not x.startswith('_') and x not in sys.modules and x not in ['In', 'Out', 'exit', 'quit', 'get_ipython', 'ipython_vars']], key=lambda x: x[1], reverse=True)

它显示内存中对象的大小(以 mb 为单位) - 可能表明什么占用了资源

【讨论】:

  • 感谢您的建议,不幸的是它没有改变任何东西。
  • 奇怪,我个人会存储在 pandas 中——更容易调试。
  • 有趣的是,您共享的代码清楚地显示了每次迭代都会清除“内容”,但根据任务管理器的实际内存使用量仍然在每次迭代中不断增加。
  • del contents 可能是不必要的,因为即使 gc 没有释放分配,它也会在每次迭代时被覆盖(之后旧的分配被“释放”)。
猜你喜欢
  • 1970-01-01
  • 2014-01-24
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
  • 2020-07-20
相关资源
最近更新 更多