使用Elementtree编写Python大型XML代码
我刚开始着眼于使用python从csv生成XML。我正在尝试使用ElementTree来做到这一点。但是,我无法得到我想要的格式。使用Elementtree编写Python大型XML代码
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的内存,程序仍会出现内存错误问题。我试图遵循关于使用ElementTree(top).write(sys.stdout)
序列化XML流的建议here,但我无法操作它。我知道(某种)SAX解析器更适合大型XML创建。但是,我现在有点害怕。任何建议或建议将对我有用。
你有正确的想法,但我认为你需要唠叨你的工具。像ElementTree这样的XML DOM文档不打算以迭代方式编写。当你做了print(prettify(root))
时,你写下了当时存在的整棵树,这只是<plans/>
。相反,您可以手动编写xml声明和一个开放标记,然后您可以使用DOM生成并将每个<plan>
作为单独的文档编写。
minidom
包含为您写入文档的每个树的xml声明,因此您需要切换到其他工具。 lxml
有一个很好的打印效果。您想将xml写入二进制文件,因为DOM将处理任何编码。我发现sys.stdout
不必要的问题,直接写文件。
你没有csv文件模式非常适合python3,所以我改变了这一点,而我在这里。
我也摆弄你如何创建每个我认为是更清晰。
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来更深入地了解python。 – Gandalf
.toprettyxml
打印您将它作为XML提供的内容文档,因此它包含XML序言。您正在为每个“计划”打印单独的文档。第一次,你打印空的plans
节点,所以你得到<plans/>
。在循环的每次迭代中,清除根目录,向其中添加一个元素,然后输出整个根目录。所以是的,你要重复输出整个元素plans
。每次它只有一个在里面。当您使用诸如tostring
和toprettyxml
之类的东西时,您会输出整个元素,而不仅仅是开始标记。
正如你猜测的那样,你应该看看使用SAX方法。正如minidom
库的名称所示,它是一个DOM库。正如ElementTree的the docs所说的那样,它被设计用来在内存中存储分层数据结构。如果您不想一次性将数据结构存储在内存中,它可能不是最佳选择。 (它有一些增量设施阅读,但不写。)你可以看看xml.sax库。但是,你应该探索这样的解决方案,并问一个关于如何使用SAX的更具体的问题,如果有的话。
我没有看到您的两个XML示例之间的任何区别。你只是在谈论空白区别? – BrenBarn
在第一个(也是正确的)版本中,所有'person'元素都在'plans'元素中。在第二个版本中,'plans'元素在第二行开始和结束,'person'元素独立于'plans'。此外,在第二版xml序言中,'重复每个'person'元素。 – Gandalf
你为什么用奇怪的方式做事,用'tostring'输出XML,然后用minidom再读一遍? – BrenBarn