我们要处理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>
当我们想象一个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文件更有趣。
这就是callback
中StudentReader
参数的用途-我们可以将这些列表直接传递给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)
当然,您也可以在回调中使用数据库命令,或任何您需要的东西。