Python 大 XML 代使用 Elementtree
Python large XML generation using Elementtree
我刚开始研究使用 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 的 ram。我已尝试遵循有关使用 ElementTree(top).write(sys.stdout)
序列化 xml-stream 的建议 here,但无法对其进行操作。我知道(有点)SAX 解析器更适合大型 xml 创建。但是,此刻我有点被它吓倒了。任何建议或忠告都会对我有用。
.toprettyxml
将您提供的内容打印为 XML 文档 ,因此它包含 XML 序言。您正在为每个 "plan" 打印一个单独的文档。第一次,您打印空 plans
节点,因此您得到 <plans/>
。在循环的每次迭代中,您清除根,向其添加一个元素,然后输出整个根。所以是的,您正在重复输出整个 plans
元素。而且每次里面只有一个plan
。当您使用 tostring
和 toprettyxml
之类的东西时,您输出的是整个元素,而不仅仅是开始标记。
如您推测的那样,您应该考虑使用 SAX 方法。正如 minidom
库的名称所示,它是一个 DOM 库。作为 the docs for ElementTree say, it is "designed to store hierarchical data structures in memory." If you don't want to store your data structure in memory all at once, it may not be the best choice. (It has some facilities for incremental reading, but not writing.) You could take a look at the xml.sax 库。但是您应该探索此类解决方案,并提出一个更具体的问题,了解如何使用 SAX(如果有的话)。
你的想法是对的,但我认为你需要调整你的工具。 XML DOM 像 ElementTree 这样的文档不适合迭代编写。当您执行 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 从 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 的 ram。我已尝试遵循有关使用 ElementTree(top).write(sys.stdout)
序列化 xml-stream 的建议 here,但无法对其进行操作。我知道(有点)SAX 解析器更适合大型 xml 创建。但是,此刻我有点被它吓倒了。任何建议或忠告都会对我有用。
.toprettyxml
将您提供的内容打印为 XML 文档 ,因此它包含 XML 序言。您正在为每个 "plan" 打印一个单独的文档。第一次,您打印空 plans
节点,因此您得到 <plans/>
。在循环的每次迭代中,您清除根,向其添加一个元素,然后输出整个根。所以是的,您正在重复输出整个 plans
元素。而且每次里面只有一个plan
。当您使用 tostring
和 toprettyxml
之类的东西时,您输出的是整个元素,而不仅仅是开始标记。
如您推测的那样,您应该考虑使用 SAX 方法。正如 minidom
库的名称所示,它是一个 DOM 库。作为 the docs for ElementTree say, it is "designed to store hierarchical data structures in memory." If you don't want to store your data structure in memory all at once, it may not be the best choice. (It has some facilities for incremental reading, but not writing.) You could take a look at the xml.sax 库。但是您应该探索此类解决方案,并提出一个更具体的问题,了解如何使用 SAX(如果有的话)。
你的想法是对的,但我认为你需要调整你的工具。 XML DOM 像 ElementTree 这样的文档不适合迭代编写。当您执行 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>
""")