我的目标是解析25 GB的XML数据。下面给出了这种数据的一个例子:
<Document>
<Data Id='12' category='1' Body="abc"/>
<Data Id='13' category='1' Body="zwq"/>
.
.
<Data Id='82018030' category='2' CorrespondingCategory1Id='13' Body="pqr"/>
然而......考虑到我所拥有的“25 GB”数据......我的方法效率很低。请提出一些改进我的代码或替代方法的方法。还要包括一个小例子代码,以使事情更清晰。
答案 0 :(得分:4)
您可能会发现SAX解析器更适合此任务。 SAX解析器不是构建DOM,而是将XML文件转换为元素流,并调用您提供的函数以便处理每个元素。
好处是,与DOM解析器相比,SAX解析器可以非常快速且内存效率高,有些甚至不需要同时给出所有XML,这对于25 GB的解析是理想的
不幸的是,如果您需要任何上下文信息,例如“我想要标记<B>
,但只有它位于标记<A>
内”,您必须自己维护它,因为所有解析器都是“开始”标记<A>
,开始标记<B>
,结束标记<B>
,结束标记<A>
。“它从未明确告诉您标记<B>
位于标记<A>
内,您必须从您所看到的内容中找出答案。一旦你看到一个元素,它就会消失,除非你自己记住它。
这对于复杂的解析作业非常繁琐,但是你的作业可能是可管理的。
Python的标准库在xml.sax
中有一个SAX解析器。您可能想要xml.sax.xmlreader.IncrementalParser
。
答案 1 :(得分:0)
我在查看您的问题时的第一个建议是使用MySQL或sqlite等关系数据库。将XML数据放入此表单并不难,然后查询该数据会更直接,更快。
答案 2 :(得分:0)
您的初始算法在O(n ^ 2)中运行,对于25GB的数据来说这将非常慢。理想情况下,您将其归结为O(n)或O(n log n)。如果没有关于数据的任何其他信息(比如类别1或类别2是否更小等),您可以执行类似的操作(即O(n)):
from lxml import objectify
f=open('myfile25GB', 'r')
text=f.read()
root=objectify.fromstring(text)
cat_one_bodies = {}
for e in root.attrib['Document'].row:
category = int(e.attrib['category'])
body = e.attrib['Body']
if category == 1:
e_id = int(e.attrib['Id'])
cat_one_bodies[e_id] = body
else: #Assuming there are only 2 categories
cat_one_id = int(e.attrib['CorrespondingCategory1Id'])
print "Cat1 Body: '%s' Cat2 Body: '%s'" % (body, cat_one_bodies[cat_one_id])
虽然这不会解析您的文件,但希望它会向您展示这个想法。它可能会占用相当多的内存(因为它维护了字典中的所有category1主体),所以这可能是一个考虑因素。
答案 3 :(得分:0)
在目前在Saxon-EE中实现的XSLT 3.0(草案)中,您可以编写流式转换来解决此问题,如下所示:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map">
<xsl:mode streamable="yes"/>
<xsl:template match="/">
<xsl:iterate select="Document/Data">
<xsl:param name="map" select="map{}"/>
<xsl:choose>
<xsl:when test="@category='1'">
<xsl:next-iteration>
<xsl:with-param name="map" select="map:put($map, string(@Id), string(@Body))"/>
</xsl:next-iteration>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'Cat1 Body: ',
$map(@CorrespondingCategoryId), 'Cat2 Body', @Body"/>
</xsl:otherwise>
</xsl:choose>
</xsl:iterate>
</xsl:template>
我没有测试过这个问题(这是在为期四天的假期前夕深夜......)但是如果你有兴趣采用这种方法,我会很乐意提供帮助。 XSLT 3.0仍然是草案规范,相当流畅。它的重点是解决像这样的问题,使用流媒体方法处理使用有界内存的非常大的文档。 Saxon-EE 9.4实现了规范的快照。
答案 4 :(得分:0)
如果ID按升序排列,那么您可以推出自己的函数,该函数读取文件中任何位置的元素。然后你可以只扫描整个文件,对于每个元素,你可以使用二进制搜索算法找到相应的元素。该东西将在O(n log n)中运行,同时仍然使用可忽略的内存量。
答案 5 :(得分:0)
尝试使用lxml中的iterparse。我认为它适合您希望处理的问题。