Python 将大型xml拆分为小块
我们希望处理大约6GB的大型xml文件 在这里,我们将大型xml读入dataframe,然后将其导出到csv文件 我们使用lxml和iterparse逐行读取xml并将其加载到dataframe中,但这个过程几乎需要6分钟 因此,我们决定将这个xml分割成小的块,比如每个1GB,然后并发处理它 您能推荐拆分这个大xml文件的最快方法吗 我已经尝试链接,这几乎是采取8-10只是为了分裂文件 我的xml的结构如下所示。在实际应用中,我们有将近200个标签,并且有将近100000条带有标签的记录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
<?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文件时,我们只需要对几个事件做出反应:
- 遇到一个开头:准备一个新的列表,这样我们就可以存储我们将看到的各种值
- 遇到开口
,
等:找出此属性值应放入哪个插槽(“当前”插槽) - 遇到一段文本:如果此文本属于我们关心的属性,请将其写入当前插槽
- 遇到结束语
,
等:请注意,接下来出现的任何文本都不有趣,应该忽略 - 遇到了一个结束符:我们与当前学生的关系结束,可以输出我们收集的所有数据
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。