Python 将大型xml拆分为小块

Python 将大型xml拆分为小块,python,split,xml-parsing,lxml,large-files,Python,Split,Xml Parsing,Lxml,Large Files,我们希望处理大约6GB的大型xml文件 在这里,我们将大型xml读入dataframe,然后将其导出到csv文件 我们使用lxml和iterparse逐行读取xml并将其加载到dataframe中,但这个过程几乎需要6分钟 因此,我们决定将这个xml分割成小的块,比如每个1GB,然后并发处理它 您能推荐拆分这个大xml文件的最快方法吗 我已经尝试链接,这几乎是采取8-10只是为了分裂文件 我的xml的结构如下所示。在实际应用中,我们有将近200个标签,并且有将近100000条带有标签的记录 &l

我们希望处理大约6GB的大型xml文件

在这里,我们将大型xml读入dataframe,然后将其导出到csv文件 我们使用lxml和iterparse逐行读取xml并将其加载到dataframe中,但这个过程几乎需要6分钟

因此,我们决定将这个xml分割成小的块,比如每个1GB,然后并发处理它

您能推荐拆分这个大xml文件的最快方法吗

我已经尝试链接,这几乎是采取8-10只是为了分裂文件

我的xml的结构如下所示。在实际应用中,我们有将近200个标签,并且有将近100000条带有标签的记录

<?xml version="1.0" encoding="UTF-8"?>
<ACADEMICS>  
  <STUDENTS ASOF_DATE="11/21/2019" CREATE_DATE="11/22/2019" RECORDS="108881">      
    <STUDENT>      
      <NAME>JOHN</NAME>      
      <REGNUM>1000</REGNUM>      
      <COUNTRY>USA</COUNTRY>      
      <ID>JH1</ID>
      <SHORT_STD_DESC>JOHN IS A GOOD STUDENT</SHORT_STD_DESC>
    </STUDENT>
   <STUDENT>      
    <NAME>ADAM</NAME>      
    <REGNUM>1001</REGNUM>      
    <COUNTRY>FRANCE</COUNTRY>      
    <ID>AD2</ID>
    <SHORT_STD_DESC>ADAM IS A GOOD STUDENT</SHORT_STD_DESC>
  </STUDENT>
  <STUDENT>      
    <NAME>PETER</NAME>      
    <REGNUM>1003</REGNUM>      
    <COUNTRY>BELGIUM</COUNTRY>      
    <ID>PE5</ID>
    <SHORT_STD_DESC>PETER IS A GOOD STUDENT</SHORT_STD_DESC>
</STUDENT>
<STUDENT>      
    <NAME>ERIC</NAME>      
    <REGNUM>1006</REGNUM>      
    <COUNTRY>AUSTRALIA</COUNTRY>      
    <ID>ER7</ID>
    <SHORT_STD_DESC>ERIC IS A GOOD STUDENT</SHORT_STD_DESC>
</STUDENT>
<STUDENT>      
    <NAME>NICHOLAS</NAME>      
    <REGNUM>1009</REGNUM>      
    <COUNTRY>GREECE</COUNTRY>      
    <ID>NI8</ID>
    <SHORT_STD_DESC>NICHOLAS IS A GOOD STUDENT</SHORT_STD_DESC>
</STUDENT>

厕所
1000
美国
JH1
约翰是个好学生
亚当
1001
法国
AD2
亚当是个好学生
彼得
1003
比利时
PE5
彼得是个好学生
埃里克
1006
澳大利亚
ER7
埃里克是个好学生
尼古拉斯
1009
希腊
NI8
尼古拉斯是个好学生

当我们设想一个最小的解析器时,在读取XML文件时,我们只需要对几个事件做出反应:

  • 遇到一个开头:准备一个新的列表,这样我们就可以存储我们将看到的各种值
  • 遇到开口
    等:找出此属性值应放入哪个插槽(“当前”插槽)
  • 遇到一段文本:如果此文本属于我们关心的属性,请将其写入当前插槽
  • 遇到结束语
    等:请注意,接下来出现的任何文本都不有趣,应该忽略
  • 遇到了一个结束符:我们与当前学生的关系结束,可以输出我们收集的所有数据
考虑到这一点,我们可以创建一个SAX事件处理程序来实现这一想法:

from xml.sax.handler import ContentHandler

class StudentReader(ContentHandler):
    def __init__(self, callback=None):
        self.column_order = 'ID,NAME,REGNUM,COUNTRY,SHORT_STD_DESC'
        self.current_student = None
        self.current_idx = None
        self.mapping = {key: idx for idx, key in enumerate(self.column_order.split(','))}
        self.num_cols = len(self.mapping)
        self.callback = callback

    def startElement(self, tag, attrs):
        if tag == 'STUDENT':
            # new student with correct number of columns
            self.current_student = [''] * self.num_cols
        elif tag in self.mapping:
            # which column are we writing to?
            self.current_idx = self.mapping[tag]
        else:
            self.current_idx = None

    def endElement(self, tag):
        if tag == 'STUDENT':
            if self.callback is not None:
                # when we have a callback, call it
                self.callback(self.current_student)
            else:
                # without a callback, just print to console (for debugging)
                print(self.current_student)
        elif tag in self.mapping:
            self.current_idx = None

    def characters(self, data):
        if self.current_idx is not None:
            self.current_student[self.current_idx] += data
无需保留任何其他内容,无需构建树,我们可以在读取输入XML时执行此操作。这将是快速的,并使用很少的内存

现在我们可以创建一个SAX解析器,并为其提供一个
StudentReader
实例:

import xml.sax

handler = StudentReader()
xml.sax.parse('data.xml', handler)
…将列表打印到控制台:

['JH1', 'JOHN', '1000', 'USA', 'JOHN IS A GOOD STUDENT']
['AD2', 'ADAM', '1001', 'FRANCE', 'ADAM IS A GOOD STUDENT']
['PE5', 'PETER', '1003', 'BELGIUM', 'PETER IS A GOOD STUDENT']
['ER7', 'ERIC', '1006', 'AUSTRALIA', 'ERIC IS A GOOD STUDENT']
['NI8', 'NICHOLAS', '1009', 'GREECE', 'NICHOLAS IS A GOOD STUDENT']
例如,将数据写入CSV文件可能更有趣

这就是我们的
StudentReader
中的
callback
参数的作用-我们可以将这些列表直接传递给CSV编写器,这样它就可以在处理输入文件时编写输出文件

import csv
import xml.sax

with open('output.csv', 'w', encoding='utf8', newline='') as fp:
    writer = csv.writer(fp, delimiter=';')

    handler = StudentReader(writer.writerow)

    xml.sax.parse('data.xml', handler)

当然,您也可以在回调中使用数据库命令,或者您需要的任何命令。

不要使用
.iterparse()
,因为它在解析输入时仍在内存中构建整个文档树,但您甚至不需要任何树。拆分文档和读取较小的部分也不一定会加快操作。使用定制的基于事件的解析器来流式处理文件可能是您的最佳选择,但为此,您应该显示XML中有意义的部分,并指出需要提取哪些数据。嗨,Tomalak,我在我的原始帖子中更新了XML结构。为了简单起见,这里我只提到了5条记录,其中只有5个属性,比如,等等。在实际应用中,我们有将近100000条记录,其中有标记和200个属性。“[…]并指出需要提取哪些数据。”我们希望提取所有200个字段属性。一般方法如下。在开始时只使用几个属性来测试它,以获得对它的感觉。如果您需要的所有属性都是
的直接子级,那么您所需要做的就是扩展列列表。嗨,Tomalak,这真的很有帮助。现在我可以在8分钟内处理该文件,并且保持低内存。不确定是否还有改进的余地,以使其少于8分钟。请给我一些建议。此外,在将相同的文件数据转储到MongoDB时,我们也遇到了性能问题。我已经提出了一张如下的罚单。请稍等,也许你也可以在这方面帮助我们。你好,托马拉克,你也可以看到,我们有ASOF_DATE=“11/21/2019”学生标签属性。您能告诉我如何使用您建议的上述代码获取ASOF_日期值吗?@user1957116重新阅读我写的文本,我回答了这个问题(在对您问题的评论中)。阅读代码。了解它的作用。如果你盲目地从互联网上复制和运行其他人编写的代码,而你并不完全理解这些代码,那么你就有问题了。此外,将内容写入CSV文件,然后将该CSV导入MongoDB,所需的工作也比需要的要多。通过编写不同的回调直接将数据导入MongoDB。