在python中将深度嵌套的XML转换为csv

Convert Deeply Nested XML to CSV in Python

本问题已经有最佳答案,请猛点这里访问。

我不熟悉Python,听说它是解析相当大的XML文件(150MB)的最佳方法之一。我无法理解如何迭代这些标签,只提取标签,因为它的嵌套相当深。

我有一些XML格式如下,我需要使用python从中提取"hw"和"defunit"标记,并将它们转换为.csv格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<dps-data xmlns="urn:DPS2-metadata" project="SCRABBLELARGE" guid="7d6b7164fde1e064:34368a61:14306b637ab:-8000--4a25ae5c-c104-4c7a-bba5-b434dd4d9314">
    <superentry xmlns="urn:COLL" xmlns:d="urn:COLL" xmlns:e="urn:IDMEE" e:id="u583c10bfdbd326ba.31865a51.12110e76de1.-336">
        <entry publevel="1" id="a000001" e:id="u583c10bfdbd326ba.31865a51.12110e76de1.-335">
            <hwblk>
                <hwgrp>
                    <hwunit>
                        <hw>aa</hw>
                        <ulsrc>edsh</ulsrc>
                    </hwunit>
                </hwgrp>
            </hwblk>
            <datablk>
                <gramcat publevel="1" id="a000001.001">
                    <pospgrp>
                        <pospunit>
                            <posp value="noun" />
                        </pospunit>
                    </pospgrp>
                    <sensecat id="a000001.001.01" publevel="1">
                        <defgrp>
                            <defunit>
                                <def>volcanic rock</def>
                            </defunit>
                        </defgrp>
                    </sensecat>
                </gramcat>
            </datablk>
        </entry>
    </superentry>
  </dps-data>

我想看到的.csv格式只是:

1
2
hw, defunit
aa, volcanic rock


以XSLT为例,它是一种XML转换语言,可以将源.xml文件操作到各种最终使用结构,包括.csv等文本文件,并在中指定method="text"

python的lxml模块可以运行xslt 1.0脚本。下面假设标记及其子标记使用不同的数据重复。并且必须在XSL中处理两个未声明的名称空间。此外,对于较小的XML,XSLT往往非常有效,但根据计算机环境的不同而有所不同。

XSLT脚本(另存为.xsl,将在下面引用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
               xmlns:ns1="urn:DPS2-metadata" xmlns="urn:COLL">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" method="text"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/ns1:dps-data/ns1:superentry">
   <xsl:text>hw,defunit</xsl:text><xsl:text>&#xa;</xsl:text>
   <xsl:apply-templates select="ns1:entry"/>
</xsl:template>

<xsl:template match="ns1:entry" namespace="urn:COLL">    
   <xsl:value-of select="descendant::ns1:hw" namespace="urn:COLL"/><xsl:text>,</xsl:text>
   <xsl:value-of select="descendant::ns1:defunit" namespace="urn:COLL"/>
   <xsl:text>&#xa;</xsl:text>
</xsl:template>

皮顿脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import lxml.etree as ET

// LOAD XML AND XSL SOURCES
xml = ET.parse('Input.xml')
xsl = ET.parse('XSLTScript.xsl')

// TRANSFORM SOURCE
transform = ET.XSLT(xsl)
newdom = transform(xml)

// SAVE AS .CSV
with open('Output.csv'), 'wb') as f:
    f.write(newdom)

# hw,defunit
# aa,volcanic rock


这个怎么样?

1
2
3
4
5
6
7
8
9
10
from xml.dom import minidom

xmldoc = minidom.parse('your.xml')
hw_lst = xmldoc.getElementsByTagName('hw')
defu_lst = xmldoc.getElementsByTagName('def')

with open('your.csv', 'a') as out_file:
    for i in range(len(hw_lst)):
        out_file.write('{0}, {1}
'
.format(hw_lst[i].firstChild.data, defu_lst[i].firstChild.data))


lxml库能够非常强大的XML解析,并且可以用于在XML树上迭代以搜索特定的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from lxml import etree

with open(r'path/to/xml', 'r') as xml:
    text = xml.read()
tree = lxml.etree.fromstring(text)
row = ['', '']
for item in tree.iter('hw', 'def'):
    if item.tag == 'hw':
       row[0] = item.text
    elif item.tag == 'def':
       row[1] = item.text

line = ','.join(row)

with open(r'path/to/csv', 'a') as csv:
     csv.write(line + '
'
)

如何构建csv文件在很大程度上是基于首选项的,但是我在上面提供了一个简单的例子。如果有多个标记,您可以先提取这些元素(可以使用上面显示的同一tree.iter方法完成),然后将上述逻辑应用于每个元素。

编辑:我应该指出,这个特定的实现将整个XML文件读取到内存中。如果您一次只处理一个150MB的文件,这应该不是问题,但这只是需要注意的一点。