Открыть xml-файлы в python с плохим представлением

Рейтинг: 0Ответов: 2Опубликовано: 04.03.2023

У меня 100 файлов формата xml, которые я не могу открыть

Пример файла:

<?xml version="1.0" <?xml version="1.0" encoding="UTF-8"?>
<api version="3.2">
<peoplecount>
<entry id="1" name="" userid="">
<count datetime="2022.07.11 14:16:20"  realin="0" realout="1" realpass="0" queuetime="0" />
<count datetime="2022.07.11 14:21:57"  realin="0" realout="1" realpass="0" queuetime="0" />
<count datetime="2022.07.11 14:23:11"  realin="0" realout="1" realpass="0" queuetime="0" />
%skipzero[1,2]%</entry>
</peoplecount>
<timezone name="Europe/Moscow" offset="10800"/>
</api>

Я использую xml.etree.ElementTree для парсинга файла

import xml.etree.ElementTree as ET

tree = ET.parse('pathname.xml')
root = tree.getroot()

for child in root.iter('count'):
    print(child.tag, child.attrib)

Ошибка связана с "not well-formed"

Я знаю причину ошибки (In the beginning, you need to delete '<?xml version="1.0" ') , но так как файлов более 100, устану открывать каждый и удалять

Есть идеи, как поправить формат файла перед открытием?

Ответы

▲ 2Принят
# with open('pathname.xml') as f:
#     txt = f.read()
txt = '''<?xml version="1.0" <?xml version="1.0" encoding="UTF-8"?>
<api version="3.2">
<peoplecount>
<entry id="1" name="" userid="">
<count datetime="2022.07.11 14:16:20"  realin="0" realout="1" realpass="0" queuetime="0" />
<count datetime="2022.07.11 14:21:57"  realin="0" realout="1" realpass="0" queuetime="0" />
<count datetime="2022.07.11 14:23:11"  realin="0" realout="1" realpass="0" queuetime="0" />
%skipzero[1,2]%</entry>
</peoplecount>
<timezone name="Europe/Moscow" offset="10800"/>
</api>
'''

# 1
import xml.etree.ElementTree as ET

txt_r = txt.replace('<?xml version="1.0" <?xml version="1.0" encoding="UTF-8"?>',
                  '<?xml version="1.0" encoding="UTF-8"?>')
root = ET.fromstring(txt_r)
for child in root.iter('count'):
    print(child.tag, child.attrib)

# 2
from bs4 import BeautifulSoup

soup = BeautifulSoup(txt, 'xml')
for child in soup.find_all('count'):
    print(child.name, child.attrs)
▲ 1

похожий вариант с recover-режимом обработки ошибочных xml, то есть замену делать не надо:

x = """<?xml version="1.0" <?xml version="1.0" encoding="UTF-8"?>
<api version="3.2">
<peoplecount>
<entry id="1" name="" userid="">
<count datetime="2022.07.11 14:16:20"  realin="0" realout="1" realpass="0" queuetime="0" />
<count datetime="2022.07.11 14:21:57"  realin="0" realout="1" realpass="0" queuetime="0" />
<count datetime="2022.07.11 14:23:11"  realin="0" realout="1" realpass="0" queuetime="0" />
%skipzero[1,2]%</entry>
</peoplecount>
<timezone name="Europe/Moscow" offset="10800"/>
</api>
"""
from lxml import etree

x = x.encode('utf-8')

parser = etree.XMLParser(recover=True)
root = etree.fromstring(x, parser=parser)

for child in root.iter('count'):
    print(child.tag, child.attrib)

вывод:

/bin/python3 /home/ru/py.1/p1/p.py

count {'datetime': '2022.07.11 14:16:20', 'realin': '0', 'realout': '1', 'realpass': '0', 'queuetime': '0'}

count {'datetime': '2022.07.11 14:21:57', 'realin': '0', 'realout': '1', 'realpass': '0', 'queuetime': '0'}

count {'datetime': '2022.07.11 14:23:11', 'realin': '0', 'realout': '1', 'realpass': '0', 'queuetime': '0'}