将大xml分割成小块

问题描述 投票:0回答:1

我们要处理6 GB左右的大型xml文件。

这里我们将大型xml读入数据框,然后将其导出到csv文件我们正在使用带有iterparse的lxml逐行读取xml并将其加载到dataframe中。但是此过程几乎需要6分钟。

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

您能建议分割此大型xml文件的最快方法吗?

我已经尝试过链接https://gist.github.com/benallard/8042835,几乎只需要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>

python split xml-parsing lxml large-files
1个回答
0
投票

当我们想象一个minimal解析器时,在读取XML文件时,我们只需要响应几个事件:

  • 遇到开头<STUDENT>:准备一个新列表,以便我们可以存储将看到的各种值。
  • 遇到开<NAME><REGNUM>等:找出该属性值应该进入哪个插槽(“当前”插槽)。
  • 遇到一段文本:如果此文本属于我们关心的属性,请将其写入当前插槽。
  • 遇到结束</NAME></REGNUM>等:请注意,接下来出现的任何文本都没有意思,应该忽略。
  • 遇到结束</STUDENT>:我们已经完成了当前的学生,并且可以输出我们收集的所有数据。

牢记这一点,我们可以创建一个实现此思想的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

无需保留其他任何内容,无需构建任何树,我们可以while读取输入的XML。这样会很快并且使用very很少的内存。

现在我们可以创建一个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文件更有趣。

这就是callbackStudentReader参数的用途-我们可以将这些列表直接传递给CSV编写器,以便它可以写输出文件while正在处理输入文件。

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)

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

© www.soinside.com 2019 - 2024. All rights reserved.