我有一個資料夾,目錄結構如下:
sample
--object
--people
--animal
xml範例如下:
DataOut_1.xml
<PDataset>
<PAMRandom B="1">
<Metadata>
<MDI key="SMA">1502</MDI>
<MDI key="SME">3.14</MDI>
</Metadata>
</PAMRandom>
<PAMRandom B="2">
<Metadata>
<MDI key="SMA">3394</MDI>
<MDI key="SME">24.2628</MDI>
</Metadata>
</PAMRandom>
</PDataset>
三個資料夾內各有一大堆的xml檔案,我想將特定的內容擷取出並另存成如下方範例的CSV或ODS檔。
Name,B1_SMA,B1_SME,B2_SMA,B2_SME
DataOut_1,1502,3.14,3394,24.2628
參考一些教學後,目前只能做到擷取字串,卻一直無法擷取指定元素的內容,請問這個問題該如何解呢? 解法不限Python,Cmd寫成的批次檔也可以。
謝謝各位 :D
最後附上跑不動的Python
from lxml import etree
import pandas as pd
tree = etree.parse("DataOut_1.xml")
df = pd.DataFrame({
"B1_SMA" : tree.xpath('/PDataset/PAMRandom/Metadata/MDI*[@key='SMA']'),
})
df.to_csv("output.csv", sep=",", index = None)
正統的作法可能是要用類似xpath去取得xml節點內容,
不過我個人比較偏愛用正則(RegularExpression)來解,
這邊提供一個土法煉鋼的方法,用 Re.findall 將符合的結果存為串列(list)
然後就是把結果重排成你要的格式並存為與xml檔同名的csv檔,Done!
以下範例在macos運作正常,如果你是在windows作業系統執行可能需要把第11行的 .replace('\n','')
改成 .replace('\r\n','')
DataOut_1.xml
<PDataset>
<PAMRandom B="1">
<Metadata>
<MDI key="SMA">1502</MDI>
<MDI key="SME">3.14</MDI>
</Metadata>
</PAMRandom>
<PAMRandom B="2">
<Metadata>
<MDI key="SMA">3394</MDI>
<MDI key="SME">24.2628</MDI>
</Metadata>
</PAMRandom>
</PDataset>
extract_xml.py
import re
# 取得完整檔名(不含路徑)
def get_file_fullname(input_str):
return input_str.split("/")[-1] # for linux or macos
# return input_str.split("\\")[-1] 如果是在windows要改成這樣寫
def extract_from_xml(xml_file,save_file):
with open(xml_file,'r',encoding='utf-8') as f:
xml_content=f.read().replace(' ','').replace('\n','')
#print(xml_content)
RE_RULE=r'<PAMRandom B="([^"]+)">.*?<Metadata>.*?<MDI key="([^"]+)">([^<]+)</MDI>.*?<MDI key="([^"]+)">([^<]+)</MDI></Metadata>.*?</PAMRandom>'
tmp_list=re.findall(RE_RULE, xml_content)
print('XML EXTRACT TO LIST:\n',tmp_list)
#將match結果排成需要的格式
LINE1="Name,B{}_{},B{}_{},B{}_{},B{}_{}\n".format(tmp_list[0][0],tmp_list[0][1],tmp_list[0][0],tmp_list[0][3],tmp_list[1][0],tmp_list[1][1],tmp_list[1][0],tmp_list[1][3])
LINE2="{},{},{},{},{}\n".format(get_file_fullname(xml_file).split(".")[0],tmp_list[0][2],tmp_list[0][4],tmp_list[1][2],tmp_list[1][4])
print('='*30)
print(LINE1+LINE2)
#存為同檔名的csv
with open(save_file,'w',encoding='utf-8') as f:
f.writelines(LINE1)
f.writelines(LINE2)
extract_from_xml('DataOut_1.xml','DataOut_1.csv')
執行結果:
$ python3 ./read_xml.py
EXTRACT TO LIST:
[('1', 'SMA', '1502', 'SME', '3.14'), ('2', 'SMA', '3394', 'SME', '24.2628')]
==============================
Name,B1_SMA,B1_SME,B2_SMA,B2_SME
DataOut_1,1502,3.14,3394,24.2628
DataOut_1.csv
Name,B1_SMA,B1_SME,B2_SMA,B2_SME
DataOut_1,1502,3.14,3394,24.2628
增加能讀取子資料夾下的xml並輸出成CSV的功能
可以參考一下這篇文章裡面我的回覆(把 xlsx 改成 xml 原理是一樣的)需要另外寫的是你的存檔路徑怎麼設 還有記得要測試前記得先把所有檔案備份喔
https://ithelp.ithome.com.tw/questions/10201953
結合這篇的範例可能會像這樣
# 取得特定資料夾底下的檔案列表
# allow_file_type可指定檔案類型(未指定就是列出所有類型)
def dir_list(dir_loc,allow_file_type=[]):
...略...
# 要執行的程式 這邊稍作修改 加上了 save_file 參數
def extract_from_xml(xml_file,save_file):
...略...
# 取得副檔名格式
def get_file_type(file_path_and_name):
...略...
# 取得完整檔名(不含路徑)
def get_file_fullname(input_str):
...略...
if __name__ == '__main__':
xml_dir_path=r"C:\xmlHere\"
csv_dir_path=r"C:\csvHere\"
xml_list=dir_list(xml_dir_path,["xml"])
for i in xml_list:
save_file_full_path=csv_dir_path+(get_file_fullname(i).replace(".xml",".csv"))
extract_from_xml(i,save_file_full_path)
P.S:
r
是raw data的意思,如果不加r
且在windows系統執行的話,字串裡面的\
要寫成\\
,例如r"C:\test\"
等同"C:\\test\\"
好的,非常感謝您!
我回去之後再繼續研究。
不客氣~有疑問再提出來討論。
用python 的beautifulsoap來處理xml比較簡單,底層也是使用正則來解析HTML和XML
https://linuxhint.com/parse_xml_python_beautifulsoup/