【问题标题】:Python large XML generation using Elementtree使用 Elementtree 生成 Python 大型 XML
【发布时间】:2017-03-15 02:21:05
【问题描述】:

我刚开始研究使用 python 从 csv 生成 XML。我正在尝试使用 ElementTree 来做到这一点。但是,我无法获得我想要的格式。

Here 是我正在使用的示例 csv 数据。原始数据大约有 1200 万行,由此产生的完整 xml 将有大约 3800 万行。下面是我的代码。

import csv
import sys
from xml.etree.ElementTree import Element, SubElement, Comment, ElementTree, tostring
from xml.etree import ElementTree
from xml.dom import minidom

def prettify(elem):
    rough_string = tostring(elem, 'utf-8', method="xml")
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")

root = Element('plans')
sys.stdout = open('C:/Users/s/Desktop/xml6.xml', 'w')
print(prettify(root))

with open('C:/Users/s/Desktop/trip2.csv', 'rt') as f:
    current_group = None
    reader = csv.reader(f)
    for row in reader:
            personid, hno, pno, OXutmmtr, OYutmmtr, DXutmmtr, DYutmmtr, opcl, dpcl, depday, deptm, arrtm, newendacttma, dept, arr, newendacttmh, mode2, opurp2, dpurp2, dorp2 = row
            if current_group is None or personid != old1 :
            # Start a new group
             current_group = SubElement(root, 'person', {'id':personid})
             old1 = personid
             pln = SubElement(current_group, 'plan')
             activ = SubElement(pln, 'act', {'type':opurp2, 'x':OXutmmtr, 'y':OYutmmtr,})
             trvl = SubElement(pln, 'leg', {'mode': mode2,})
             activ = SubElement(pln, 'act',{'type': dpurp2, 'x': DXutmmtr, 'y': DYutmmtr,})

            elif personid == old1:
             trvl = SubElement(pln, 'leg', {'mode': mode2,})
             activ = SubElement(pln, 'act', {'type': dpurp2, 'x': DXutmmtr,'y': DYutmmtr,})
                if newendacttmh == "02:59:00":
                sys.stdout = open('C:/Users/s/Desktop/xml6.xml', 'a')
                print(prettify(current_group))
                root.clear()

我需要像

这样的格式
<?xml version="1.0" ?>
<plans>


<person id="101">
  <plan>
    <act type="home" x="338471.624256" y="3114225.84531"/>
    <leg mode="sov"/>
    <act type="work" x="353108.46905" y="3086263.42028"/>
    <leg mode="sov"/>
    <act type="home" x="338471.624256" y="3114225.84531"/>
  </plan>
</person>


<person id="201">
  <plan>
    <act type="home" x="338535.623855" y="3114558.14898"/>
    <leg mode="hov3+"/>
    <act type="meal" x="338520.432083" y="3105225.60283"/>
    <leg mode="hov3+"/>
    <act type="shop" x="333193.19769" y="3103842.61842"/>
    <leg mode="hov3+"/>
    <act type="pers.bus" x="338148.26292" y="3083556.85073"/>
    <leg mode="hov3+"/>
    <act type="home" x="338535.623855" y="3114558.14898"/>
  </plan>
</person>

</plans>

但我得到的格式类似于

<?xml version="1.0" ?>
<plans/>

<?xml version="1.0" ?>
<person id="101">
  <plan>
    <act type="home" x="338471.624256" y="3114225.84531"/>
    <leg mode="sov"/>
    <act type="work" x="353108.46905" y="3086263.42028"/>
    <leg mode="sov"/>
    <act type="home" x="338471.624256" y="3114225.84531"/>
  </plan>
</person>

<?xml version="1.0" ?>
<person id="201">
  <plan>
    <act type="home" x="338535.623855" y="3114558.14898"/>
    <leg mode="hov3+"/>
    <act type="meal" x="338520.432083" y="3105225.60283"/>
    <leg mode="hov3+"/>
    <act type="shop" x="333193.19769" y="3103842.61842"/>
    <leg mode="hov3+"/>
    <act type="pers.bus" x="338148.26292" y="3083556.85073"/>
    <leg mode="hov3+"/>
    <act type="home" x="338535.623855" y="3114558.14898"/>
  </plan>
</person>

本质上,当我到达每个人的记录末尾时(由时间字符串 02:59:00 指示),我试图追加到文件,因为如果我等到整个根树构建完成,那么我就会遇到内存错误问题。有趣的是,内存使用量永远不会超过 2 gb,即使还剩下 12 gb 的内存,程序也会因内存错误问题而失败。我已尝试遵循 here 关于使用 ElementTree(top).write(sys.stdout) 序列化 xml-stream 的建议,但无法操作它。我知道(有点)SAX 解析器更适合创建大型 xml。但是,此刻我有点被它吓倒了。任何建议或意见都会对我有用。

【问题讨论】:

  • 我看不出你的两个 XML 示例有什么区别。您只是在谈论空格的区别吗?
  • 在第一个(也是正确的)版本中,所有person 元素都在plans 元素内。在第二个版本中,plans 元素在第二行开始和结束,person 元素独立于plans。此外,在第二个版本中,xml 前导码 &lt;?xml version="1.0" ?&gt; 对每个 person 元素重复。
  • 你为什么要以这种奇怪的方式做事,使用tostring输出XML,然后用minidom再次读取它?
  • 据说,元素树不会缩进 xml 输出。所以,美化功能就是这样做的。但更重要的是,我完全是 python 新手,我正在重新利用我从 here 获得的代码,所以对这段代码没有完整的了解。

标签: python xml csv elementtree


【解决方案1】:

你的想法是对的,但我认为你需要调整你的工具。像 ElementTree 这样的 XML DOM 文档并不打算以迭代方式编写。当您执行print(prettify(root)) 时,您编写了当时存在的整个树,即&lt;plans/&gt;。相反,您可以手动编写 xml 声明和一个 open 标记,然后您可以使用 DOM 生成每个 &lt;plan&gt; 并将其编写为单独的文档。

minidom 包含您写入文档的每棵树的 xml 声明,因此您需要切换到不同的工具。 lxml 有漂亮的印刷品,效果很好。您想将 xml 写入二进制文件,因为 DOM 将处理任何编码。我发现sys.stdout 有不必要的问题,直接写文件。

你没有完全适合 python3 的 csv 文件模式,所以我在这里的时候改变了它。

我还摆弄了您如何创建每个我认为更清楚的方法。

import csv
import sys
from lxml.etree import Element, SubElement, Comment, ElementTree

# note: I changed file paths for test

with open('xml6.xml', 'wb') as outxml:
    # write xml declaration and containing <plans> tag
    outxml.write(b"""<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<plans>
""")
    # process csv
    with open('trip.csv', 'r', newline='') as f:
        old1 = ''
        current_group = None
        reader = csv.reader(f)
        for row in reader:
            personid, hno, pno, OXutmmtr, OYutmmtr, DXutmmtr, DYutmmtr, opcl, dpcl, depday, deptm, arrtm, newendacttma, dept, arr, newendacttmh, mode2, opurp2, dpurp2, dorp2 = row
            if personid != old1 :
                # skip on first loop then write current group to file
                if old1:
                    outxml.write(b"\n")
                    ElementTree(current_group).write(outxml, encoding='utf-8',  method='xml',  pretty_print=True)
                # Start a new group
                current_group = Element('person', {'id':personid})
                old1 = personid
                pln = SubElement(current_group, 'plan')
                activ = SubElement(pln, 'act', {'type':opurp2, 'x':OXutmmtr, 'y':OYutmmtr,})
            # write data
            trvl = SubElement(pln, 'leg', {'mode': mode2,})
            activ = SubElement(pln, 'act',{'type': dpurp2, 'x': DXutmmtr, 'y': DYutmmtr,})

    # terminate outer tag and done
    outxml.write(b"""
</plans>
""")

【讨论】:

  • 感谢您花时间解释这一点并帮助我解决问题。我需要通过更结构化的方式学习 python 来更深入地了解它。
【解决方案2】:

.toprettyxml 将您提供的内容打印为 XML 文档,因此它包含 XML 序言。您正在为每个“计划”打印单独的文档。第一次,打印空的plans 节点,得到&lt;plans/&gt;。在循环的每次迭代中,清除根,向其添加一个元素,然后输出整个根。所以,是的,您正在重复输出整个 plans 元素。而且每次它只有一个plan。当你使用 tostringtoprettyxml 之类的东西时,你输出的是整个元素,而不仅仅是开始标签。

正如您所推测的,您应该考虑使用 SAX 方法。正如minidom 库的名称所暗示的,它是一个DOM 库。正如 ElementTree 的 the docs 所说,它“旨在将分层数据结构存储在内存中”。如果您不想一次将数据结构全部存储在内存中,它可能不是最佳选择。 (它有一些增量读取的功能,但不支持写入。)您可以查看xml.sax 库。但是您应该探索这些解决方案,并提出一个更具体的问题,即如果您有 SAX 以及何时使用 SAX。

【讨论】:

    猜你喜欢
    • 2021-11-15
    • 2021-02-06
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 2017-03-16
    相关资源
    最近更新 更多