Python中处理大型XML文件的解析技巧

5
我有一个大小为4 GB的XML文件。我想将其解析并转换为数据框以进行处理。但由于文件太大,以下代码无法将文件转换为Pandas数据框。代码只是一直在加载,没有提供任何输出。但是,当我将其用于较小大小的类似文件时,我获得了正确的输出。
有人能否提出任何解决方案。也许是从XML到数据框的转换速度更快的代码,或者将XML文件拆分成更小的子集。
是否应该在我的个人系统上处理此类大型XML文件(2 GB RAM),还是应该使用Google Colab。如果使用Google Colab,则是否有一种更快地将这样大的文件上传到Drive,然后到Colab的方法?
以下是我使用的代码:
import xml.etree.ElementTree as ET
tree = ET.parse("Badges.xml")
root = tree.getroot()

#Column names for DataFrame
columns = ['row Id',"UserId",'Name','Date','Class','TagBased']

#Creating DataFrame
df = pd.DataFrame(columns = columns)

#Converting XML Tree to a Pandas DataFrame

for node in root: 
    
    row_Id = node.attrib.get("Id")
    UserId = node.attrib.get("UserId")
    Name = node.attrib.get("Name")
    Date = node.attrib.get("Date")
    Class = node.attrib.get("Class")
    TagBased = node.attrib.get("TagBased")
    
    df = df.append(pd.Series([row_Id,UserId,Name,Date,Class,TagBased], index = columns), ignore_index = True)

以下是我的 XML 文件:
<badges>
  <row Id="82946" UserId="3718" Name="Teacher" Date="2008-09-15T08:55:03.923" Class="3" TagBased="False" />
  <row Id="82947" UserId="994" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82949" UserId="3893" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82950" UserId="4591" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82951" UserId="5196" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82952" UserId="2635" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82953" UserId="1113" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />

另一种方法是不将整个XML作为一个整体解析,而是先创建250MB大小的块,并并行解析它们。当然,这仅适用于XML是长列表结构(例如交易、人员或物品),您知道要期望什么的情况。您可以拥有一个读取器,跳转到文件中的大约250MB处,找到正确的切割点,然后重新跳转等等... - Willem Hendriks
你可能会遇到的一个严重性能问题是:永远不要在for循环内调用DataFrame.appendpd.concat。这会导致二次复制。 - Parfait
@user3184950,您能否编写将XML文件转换为小块的代码?我尝试过了,但是遇到了问题。 - Ishan Dutta
@Parfait,您能否建议一种与您提到的代码不同的替代方案。 - Ishan Dutta
将大文件拆分的结果在很大程度上取决于您的XML结构。这是一个非常易于搜索和查找示例代码的话题,比如可以在Stack上找到。 - Willem Hendriks
2个回答

4

建议使用 cElementTree 代替 ElementTree

https://effbot.org/zone/celementtree.htm

cElementTree模块是基于ElementTree API的C语言实现版本,针对快速解析和低内存使用进行了优化。在典型文档上,cElementTree比Python版本的ElementTree快15-20倍,并且使用的内存量只有后者的2-5倍。

cElementTree 模块旨在取代标准elementtree包中的 ElementTree 模块。理论上,您应该可以简单地更改:

from elementtree import ElementTree

to

import cElementTree as ElementTree

我尝试使用cElementTree,但是遇到了同样的问题。代码卡住了,输出没有产生。它一直在加载。你能否建议其他版本的代码或将我的xml文件转换为更小的文件的代码? - Ishan Dutta

4
考虑使用 iterparse 进行快速流处理,逐步构建树形结构。在每次迭代中,构建一个字典列表,然后在循环外部仅一次将其传递给 pandas.DataFrame 构造函数。根据根节点的子节点重复名称进行调整:
from xml.etree.ElementTree import iterparse
#from cElementTree import iterparse
import pandas as pd

file_path = r"/path/to/Input.xml"
dict_list = []

for _, elem in iterparse(file_path, events=("end",)):
    if elem.tag == "row":
        dict_list.append({'rowId': elem.attrib['Id'],
                          'UserId': elem.attrib['UserId'],
                          'Name': elem.attrib['Name'],
                          'Date': elem.attrib['Date'],
                          'Class': elem.attrib['Class'],
                          'TagBased': elem.attrib['TagBased']})

        # dict_list.append(elem.attrib)      # ALTERNATIVELY, PARSE ALL ATTRIBUTES

        elem.clear()

df = pd.DataFrame(dict_list)

我使用了你提供的代码,加载花费了40分钟,但是我遇到了许多错误,下面列出了这些错误。 - Ishan Dutta
我已经添加了XML文件,请查看。 - Ishan Dutta
2
我所编写的代码没有任何输出,因为每次添加到DataFrame中所需的时间太长了。但是使用您的方法将其添加到字典中不仅能够让我得到输出,而且所花费的时间相对较少。 - Ishan Dutta
我使用了相同的代码来处理几乎相同的XML文件,但是出现了不应该发生的键错误。如果可能,请查看问题链接:https://stackoverflow.com/questions/62660270/how-to-solve-key-error-while-xml-file-parsing-in-python - Ishan Dutta
如果你只对“row”标签感兴趣,那么你可以在调用interparse时使用for _, elem in iterparse(file_path, events=('end',), tag='row'):指定。然后你可以摆脱你的if elem.tag测试。 - Tom Johnson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接