2017-03-15 31 views
0

我剛開始着眼於使用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創建。但是,我現在有點害怕。任何建議或建議將對我有用。

+0

我沒有看到您的兩個XML示例之間的任何區別。你只是在談論空白區別? – BrenBarn

+0

在第一個(也是正確的)版本中,所有'person'元素都在'plans'元素中。在第二個版本中,'plans'元素在第二行開始和結束,'person'元素獨立於'plans'。此外,在第二版xml序言中,<?xml version =「1.0」?>'重複每個'person'元素。 – Gandalf

+0

你爲什麼用奇怪的方式做事,用'tostring'輸出XML,然後用minidom再讀一遍? – BrenBarn

回答

1

你有正確的想法,但我認爲你需要嘮叨你的工具。像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> 
""") 
+0

感謝您花時間解釋這一點並幫助我解決問題。我需要通過更有條理的方式學習python來更深入地瞭解python。 – Gandalf

1

.toprettyxml打印您將它作爲XML提供的內容文檔,因此它包含XML序言。您正在爲每個「計劃」打印單獨的文檔。第一次,你打印空的plans節點,所以你得到<plans/>。在循環的每次迭代中,清除根目錄,向其中添加一個元素,然後輸出整個根目錄。所以是的,你要重複輸出整個元素plans。每次它只有一個​​在裏面。當您使用諸如tostringtoprettyxml之類的東西時,您會輸出整個元素,而不僅僅是開始標記。

正如你猜測的那樣,你應該看看使用SAX方法。正如minidom庫的名稱所示,它是一個DOM庫。正如ElementTree的the docs所說的那樣,它被設計用來在內存中存儲分層數據結構。如果您不想一次性將數據結構存儲在內存中,它可能不是最佳選擇。 (它有一些增量設施閱讀,但不寫。)你可以看看xml.sax庫。但是,你應該探索這樣的解決方案,並問一個關於如何使用SAX的更具體的問題,如果有的話。