iT邦幫忙

0

用Python或Cmd擷取xml檔內指定資料

我有一個資料夾,目錄結構如下:
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)
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

1
ccutmis
iT邦高手 2 級 ‧ 2021-01-30 00:19:23

正統的作法可能是要用類似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
看更多先前的回應...收起先前的回應...
wowcat iT邦新手 5 級 ‧ 2021-01-30 10:34:58 檢舉

感謝ccutmis,你的程式碼在Colaboratory上是可行的!

只是不知道如果增加能讀取子資料夾下的xml並輸出成CSV的功能,會不會需要大改 XD

ccutmis iT邦高手 2 級 ‧ 2021-01-30 10:55:17 檢舉

增加能讀取子資料夾下的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:

  1. 在字串前面加r是raw data的意思,如果不加r且在windows系統執行的話,字串裡面的\要寫成\\,例如r"C:\test\"等同"C:\\test\\"
  2. 把一些常用的功能函式化 或寫成模組是個好習慣,讓你後續有用到同樣功能時不用重新造輪子。
wowcat iT邦新手 5 級 ‧ 2021-01-30 14:32:59 檢舉

好的,非常感謝您!
我回去之後再繼續研究。

ccutmis iT邦高手 2 級 ‧ 2021-01-30 14:56:31 檢舉

不客氣~有疑問再提出來討論。

0
japhenchen
iT邦超人 1 級 ‧ 2021-01-30 11:43:44

用python 的beautifulsoap來處理xml比較簡單,底層也是使用正則來解析HTML和XML
https://linuxhint.com/parse_xml_python_beautifulsoup/

至於輸出成CSV,對PYTHON來說那根本小兒科,用join(",")就對了

我要發表回答

立即登入回答