在Python中将深层嵌套XML转换为CSV

时间:2016-09-19 15:19:02

标签: python xml csv

我是Python的新手,并且听说它是解析相当大的XML文件(150MB)的最佳方法之一。我无法理解如何遍历标记并仅提取<hw><defunit>标记,因为它是相当深层嵌套的。

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

<?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格式很简单:

hw, defunit
aa, volcanic rock

3 个答案:

答案 0 :(得分:1)

lxml库具有非常强大的XML解析功能,可用于迭代XML树以搜索特定元素。

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 + '\n')

如何构建CSV文件主要取决于偏好,但我在上面提供了一个简单的例子。如果有多个<dps-data>标记,您可以先提取这些元素(可以使用上面显示的相同tree.iter方法完成),然后将上述逻辑应用于每个元素。

编辑:我应该指出,这个特定的实现将整个XML文件读入内存。如果您一次使用一个150mb的文件,这应该不是问题,但它只是需要注意的事项。

答案 1 :(得分:1)

这个怎么样:

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}\n'.format(hw_lst[i].firstChild.data, defu_lst[i].firstChild.data)) 

答案 2 :(得分:1)

考虑XSLT,这是一种XML转换语言,可以将源.xml文件操作到各种最终用途结构,包括.csv等文本文件,在method="text"中指定<xsl:output>

Python的lxml模块可以运行XSLT 1.0脚本。下面假定<entry>标记及其子代用不同的数据重复。并且必须在xsl中处理两个未声明的命名空间。此外,XSLT在较小的XML上往往效率很高,但根据计算机环境而有所不同。

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

<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>

Pyton 脚本

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