从大型xml fi中删除一些元素

2024-09-30 05:32:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个超过1GB的xml文件,我想通过创建一个新的xml文件或重写现有的xml文件来删除父标记中不需要的子项来减小文件的大小。当文件很大时,如何通过python实现这一点,简单的解析tree = ElementTree.parse(xmlfile)将不起作用。在

XML file

在每个父标记TasksReportNode的文件中,我只希望具有值为0的TableRow属性的子标记{},并拒绝该父标记的所有其他子标记(表行)。在

XML代码示例:

<TasksReportNode Name="Task15">
    <TableData NumRows="97" NumColumns="15">
        <TableRow RowCount="0">
            <TableColumn Name="Task"><![CDATA[   Task15 [GET - /PULSEV31/appView/projectFeedHidden.jsp - 200]]]></TableColumn>
            <TableColumn Name="Status"><![CDATA[Success]]></TableColumn>
            <TableColumn Name="Successful"><![CDATA[96]]></TableColumn>
            <TableColumn Name="Failed"><![CDATA[0]]></TableColumn>
            <TableColumn Name="Timedout"><![CDATA[0]]></TableColumn>
            <TableColumn Name="Total"><![CDATA[96]]></TableColumn>
            <TableColumn Name="Min(ms)"><![CDATA[15]]></TableColumn>
            <TableColumn Name="Avg(ms)"><![CDATA[24.20]]></TableColumn>
            <TableColumn Name="Avg-90%(ms)"><![CDATA[54.55]]></TableColumn>
            <TableColumn Name="90%ile(ms)"><![CDATA[89.98]]></TableColumn>
            <TableColumn Name="95%ile(ms)"><![CDATA[95.24]]></TableColumn>
            <TableColumn Name="99%ile(ms)"><![CDATA[99.45]]></TableColumn>
            <TableColumn Name="Max(ms)"><![CDATA[94]]></TableColumn>
            <TableColumn Name="Std. Dev."><![CDATA[15.74]]></TableColumn>
            <TableColumn Name="Bytes Recd(KB)"><![CDATA[192]]></TableColumn>
        </TableRow>
        <TableRow RowCount="1">
            <TableColumn Name="Task"><![CDATA[      VirtualUser1]]></TableColumn>
            <TableColumn Name="Status"><![CDATA[Success]]></TableColumn>
            <TableColumn Name="Successful"><![CDATA[1]]></TableColumn>
            <TableColumn Name="Failed"><![CDATA[0]]></TableColumn>
            <TableColumn Name="Timedout"><![CDATA[0]]></TableColumn>
            <TableColumn Name="Total"><![CDATA[1]]></TableColumn>
            <TableColumn Name="Min(ms)"><![CDATA[934]]></TableColumn>
            <TableColumn Name="Avg(ms)"><![CDATA[934.00]]></TableColumn>
            <TableColumn Name="Avg-90%(ms)"><![CDATA[950.00]]></TableColumn>
            <TableColumn Name="90%ile(ms)"><![CDATA[1,000.50]]></TableColumn>
            <TableColumn Name="95%ile(ms)"><![CDATA[1,000.50]]></TableColumn>
            <TableColumn Name="99%ile(ms)"><![CDATA[1,000.50]]></TableColumn>
            <TableColumn Name="Max(ms)"><![CDATA[934]]></TableColumn>
            <TableColumn Name="Std. Dev."><![CDATA[0.00]]></TableColumn>
            <TableColumn Name="Bytes Recd(KB)"><![CDATA[0]]></TableColumn>
    </TableData>
    <TableData NumRows="1" NumColumns="2">
        <TableRow RowCount="0">
            <TableColumn Name="Response Time Interval (ms)"><![CDATA[0 - 99]]></TableColumn>
            <TableColumn Name="Frequency"><![CDATA[96]]></TableColumn>
        </TableRow>
    </TableData>
</TasksReportNode>
<TasksReportNode Name="Task16">
    <TableData NumRows="97" NumColumns="15">
        <TableRow RowCount="0">
            <TableColumn Name="Task"><![CDATA[   Task16 [GET - /PULSEV31/appView/projectCommentHidden.jsp - 200]]]></TableColumn>
            <TableColumn Name="Status"><![CDATA[Success]]></TableColumn>
            <TableColumn Name="Successful"><![CDATA[96]]></TableColumn>
            <TableColumn Name="Failed"><![CDATA[0]]></TableColumn>
            <TableColumn Name="Timedout"><![CDATA[0]]></TableColumn>
            <TableColumn Name="Total"><![CDATA[96]]></TableColumn>
            <TableColumn Name="Min(ms)"><![CDATA[15]]></TableColumn>
            <TableColumn Name="Avg(ms)"><![CDATA[22.73]]></TableColumn>
            <TableColumn Name="Avg-90%(ms)"><![CDATA[54.55]]></TableColumn>
            <TableColumn Name="90%ile(ms)"><![CDATA[90.93]]></TableColumn>
            <TableColumn Name="95%ile(ms)"><![CDATA[96.25]]></TableColumn>
            <TableColumn Name="99%ile(ms)"><![CDATA[100.50]]></TableColumn>
            <TableColumn Name="Max(ms)"><![CDATA[109]]></TableColumn>
            <TableColumn Name="Std. Dev."><![CDATA[14.76]]></TableColumn>
            <TableColumn Name="Bytes Recd(KB)"><![CDATA[192]]></TableColumn>
        </TableRow>
    </TableData>
</TasksReportNode>

以下是我尝试过的:

^{pr2}$

现在我们有了所有值为“0”的RowCount,这些值可以添加到parent中,剩下所有其他同级。在


Tags: 文件name标记xmlmsavgcdataile
2条回答

我建议使用lxml,因为它在大多数方面比stdlibxml.ElementTree更有效。在

不要试图将整个文档作为一个整体来解析,因为它太大了,而应该迭代地处理源文档。在

lxml页是Event driven parsing

有两种选择:

  • etree.iterparse
  • 使用自定义解析器,激发类似SAX的事件

我个人更喜欢etree.iterparse,因为它以更方便的方式为您解析了元素。但是您不能忘记对处理过的部分执行清理工作,否则与一次解析整个文档相比,您不会节省任何内存。在

编辑:添加真实示例

例子比大量的理论更能说明问题。以下是我的尝试:

from lxml import etree

# fname = "large.xml"  # 78 MB
fname = "verylarge.xml"  # 773 MB

toremove = []

for event, element in etree.iterparse(fname):
    if element.tag == "TableRow":
        if element.attrib["RowCount"] != "0":
            element.clear()
            # removing current element causes segmentation fault
            # element.getparent().remove(element)
            toremove.append(element)
    if element.tag == "TableData":
        for rowelm in toremove:
            element.remove(rowelm)
        toremove = []

# last processed element is the root one
with open("out.xml", "w") as f:
    f.write(etree.tostring(element))

为了测试性能,我取了你的大样本文件(73MB),重复内部部分10次,得到 773 MB的大型XML文件,并已处理该文件。在

处理耗时24秒(ZenbookCoreI7,内存为4GB),结果文件的大小为4.7MB。在

举例说明

默认情况下,iterparse只提供“end”事件,当某个元素被完全解析时激发。在

这个解决方案使用了这样一个事实,即即使使用iterparse,元素也会保存在内存中。这是用过的 在以下位置:

  • 在iterparse期间,不需要的元素被清除(element.clear())并被删除 (element.remove(rowelm))。clear()清除元素的内部内容,但是元素 仍然存在。remove()对父元素起作用,并从中删除内部部分。在
  • 要使用的元素没有被移除和清除,所以我们在最后找到它们 根元素。在
  • 最后,当处理完所有内容时,last processed element是根目录。它还在记忆中, 所以我可以把它作为字符串写入文件。在

remove()元素时必须小心。正在尝试从位于的父级中删除元素 当前迭代元素的瞬间导致分割错误。因此,代码 使用"TableRow"元素remove()等待,直到完成对父元素TableData的解析。在

变量toremove用于收集所有"TableRow"元素,并在父元素后立即使用 "TableData"元素被完全解析。注意,remove()只对实际元素有效 父母,所以我们一定要在适当的时候做。在

更大文件的想法

对于更大的文件,此解决方案将受到结果XML文档大小的限制 保存在内存中,直到源XML的修剪完成。在

对于这样的场景,我们必须在解析过程中写出输出,并将 内存中已处理的所有元素。实际上,你必须写下 “开始”事件时的“打开XML元素”部分(如"<TaskReportSummary att="a" otheratt="bb") 将出现,并在“end”事件中编写关闭XML元素部分"/>"。在

以下是我尝试过的:

xmL = 'F:\\Reports\\Logs\\Result_TG1_V16.xml'


context = etree.iterparse(xmL,  events=("start", "end"),)
for event, element in context:
if element.tag == 'TasksReportNode':
    for child1 in element:
        for child2 in child1:
        if child2.get("RowCount") == "0":
            for child3 in child2:
            print(child3.tag, child3.text)
element.clear() # discard the element
del context

现在我们有了值为“0”的所有行数,可以将其添加到parent中,剩下所有其他同级。在

相关问题 更多 >

    热门问题