#python #excel #xml
Вопрос:
У меня есть каталог, который содержит несколько XML-файлов, допустим, он содержит следующие 2:
<Record>
<RecordID>Madird01</RecordID>
<Location>Madird</Location>
<Date>07-09-2020</Date>
<Time>07u43m55s</Time>
<Version>2.0.1</Version>
<Version_2>v1.9</Version_2>
<Max_30e>
<I_25Hz_1s>56.40</I_25Hz_1s>
<I_25Hz_2s>7.44</I_25Hz_2s>
</Max_30e>
<Max_30e>
<I_75Hz_1s>1.56</I_75Hz_1s>
<I_75Hz_2s>0.36</I_75Hz_2s>
</Max_30e>
</Record>
И:
<Record>
<RecordID>London01</RecordID>
<Location>London</Location>
<Date>07-09-2020</Date>
<Time>08u53m45s</Time>
<Version>2.0.1</Version>
<Version_2>v1.9</Version_2>
<Max_30e>
<I_25Hz_1s>56.40</I_25Hz_1s>
<I_25Hz_2s>7.44</I_25Hz_2s>
</Max_30e>
<Max_30e>
<I_75Hz_1s>1.56</I_75Hz_1s>
<I_75Hz_2s>0.36</I_75Hz_2s>
</Max_30e>
</Record>
Теперь я хочу преобразовать это в файл Excel, который показывает каждый XML-файл в горизонтальном порядке следующим образом:
Я попытался сначала преобразовать XML в строку csv, а затем в Excel, но я застрял, должны быть более простые способы.
Это мой текущий код:
import xml.etree.ElementTree as ET
import os
xml_root = r'c:dataDesktopBlueXML-files'
for file in os.listdir(xml_root):
xml_file_path = os.path.join(xml_root, file)
tree = ET.parse(xml_file_path)
root = tree.getroot()
tree = ET.ElementTree(root)
for child in root:
mainlevel = child.tag
xmltocsv = ''
for elem in root.iter():
if elem.tag == root.tag:
continue
if elem.tag == mainlevel:
xmltocsv = xmltocsv 'n'
xmltocsv = xmltocsv str(elem.tag).rstrip() str(elem.attrib).strip() ';' str(elem.text).rstrip() ';'
Комментарии:
1. Эта библиотека может быть вашим другом: openpyxl.readthedocs.io/en/stable
2. @floatingpurr это только для Excel 2010, у меня последняя версия я нахожу эту библиотеку довольно сложной
3. Он создает файлы xlsx. У вас не должно возникнуть проблем с последней версией Excel. Здесь вы можете найти еще больше ресурсов: python-excel.org
Ответ №1:
создайте CSV-файл в формате, удобном для Excel.
import xml.etree.ElementTree as ET
from os import listdir
xml_lst = [f for f in listdir() if f.startswith('xml')]
fields = ['RecordID','I_25Hz_1s','I_75Hz_2s'] # TODO - add rest of the fields
with open('out.csv','w') as f:
f.write(','.join(fields) 'n')
for xml in xml_lst:
root = ET.parse(xml)
values = [root.find(f'.//{f}').text for f in fields]
f.write(','.join(values) 'n')
вывод
RecordID,I_25Hz_1s,I_75Hz_2s
Madird01,56.40,0.36
London01,56.40,0.36
Комментарии:
1. Я использую XML-файлы вместо XML-строк. У меня есть
ET.parse(xml_file_path)
, а затем я используюroot=tree.getroot()
so после того, как я это сделаюroot.text
, я получаюAttributeError: 'NoneType' object has no attribute 'text'
2. Вам просто нужно заменить fromstring на parse
3. Это то, что у меня уже есть, но все та же ошибка:
root = ET.parse(xml_file_path) values = [root.find(f'.//{f}').text for f in fields]
я получаю следующую ошибку:AttributeError: 'NoneType' object has no attribute 'text'
4. Каков путь к файлу XML? Возможно, ваша проблема в этом
5. Это абсолютный путь к файлу XML-файла.
ET.parse
создаетelementtree
элемент, в то времяET.fromstring
как создает элемент.
Ответ №2:
Когда вам нужно перебирать файлы в папке с похожими именами , одним из способов может быть создание шаблона и использование glob
. Чтобы убедиться , что возвращаемый путь — это файл, который вы можете использовать isfile()
.
Что касается XML, я вижу, что в основном вам нужно записать значения каждого терминального тега в столбец с именем этого тега. Поскольку у вас есть различные файлы, вы можете создавать словари значений тегов из каждого файла и сохранять их в ChainMap
нем. После обработки всех файлов вы можете использовать DictWriter
их для записи всех данных в конечный CSV-файл.
Этот метод гораздо более безопасен и гибок, чем использование статических имен столбцов. Сначала программа соберет все возможные имена тегов (столбцов) из всех файлов, поэтому в случае, если в XML нет такого тега или есть несколько дополнительных тегов, он не выдаст исключение, и все данные будут сохранены.
Код:
import xml.etree.ElementTree as ET
from glob import iglob
from os.path import isfile, join
from csv import DictWriter
from collections import ChainMap
xml_root = r"C:dataDesktopBlueXML-files"
pattern = "xmlfile_*"
data = ChainMap()
for filename in iglob(join(xml_root, pattern)):
if isfile(filename):
tree = ET.parse(filename)
root = tree.getroot()
temp = {node.tag: node.text for node in root.iter() if not node}
data = data.new_child(temp)
with open(join(xml_root, "data.csv"), "w", newline="") as f:
writer = DictWriter(f, data)
writer.writeheader()
writer.writerows(data.maps[:-1]) # last is empty dict
Upd. Если вы хотите использовать xlsx
format вместо csv
того, чтобы использовать стороннюю библиотеку (например openpyxl
). Пример использования:
from openpyxl import Workbook
...
wb = Workbook(write_only=True)
ws = wb.create_sheet()
ws.append(list(data)) # write header
for row in data.maps[:-1]:
ws.append([row.get(key, "") for key in data])
wb.save(join(xml_root, "data.xlsx"))
Комментарии:
1. как мне записать точно такой же файл data.csv в Excel?
2. @Аль-Андалус, что ты имеешь в виду? Вам нужен
xlsx
формат? Excel должен поддерживать CSV3. Какую строку мне добавить после
writer.writerows(data.maps[:-1])
, чтобы превратить этот csv-файл в файл Excel?4. @Al-Andalus, вы можете открыть
.csv
файл с помощью Excel, это поддерживаемый формат.5. Я пробовал это, но я получаю все до первой запятой внутри 1 столбца. Я предпочитаю, чтобы это был непосредственно файл Excel, а затем файл csv