Обработка XML на языке Python: методы и инструменты
Основные подходы к обработке XML в Python
Для работы с XML в Python существует несколько библиотек. Наиболее распространенные: xml.etree.ElementTree (встроенная), lxml и xml.dom.minidom. Выбор зависит от требований к производительности, функциональности и удобству. Рассмотрим каждый вариант с примерами.
Как быстро и эффективно разобрать XML с минимальными зависимостями?
Для стандартных задач лучше всего подходит ElementTree из стандартной библиотеки. Он легковесен, прост в использовании и достаточен для большинства сценариев.
import xml.etree.ElementTree as ET
# Парсинг из файла
tree = ET.parse('data.xml')
root = tree.getroot()
# Поиск всех элементов -
for item in root.findall('item'):
name = item.find('name').text
price = item.find('price').text
print(f'{name}: {price}')
Python xml (работа с xml в python)
Пояснение: Метод parse() загружает файл, getroot() возвращает корневой элемент. findall() ищет дочерние элементы по тегу. find() возвращает первый подходящий элемент. Текст получается через свойство .text.
Товар1: 100 Товар2: 200
Типичные ошибки
- FileNotFoundError: файл не существует. Используйте try-except.
- XMLSyntaxError: некорректный XML. Проверьте валидность файла.
- Атрибут .text может быть None, если элемент пустой. Проверяйте на None перед использованием.
Как создать новый XML-документ?
ElementTree позволяет легко создавать XML с нуля.
import xml.etree.ElementTree as ET
root = ET.Element('catalog')
item = ET.SubElement(root, 'item', attrib={'id': '1'})
name = ET.SubElement(item, 'name')
name.text = 'Ноутбук'
price = ET.SubElement(item, 'price')
price.text = '50000'
tree = ET.ElementTree(root)
tree.write('new_catalog.xml', encoding='utf-8', xml_declaration=True)
Создается корневой элемент catalog, в него вкладывается item с атрибутом id, далее дочерние элементы с текстом. Метод write() сохраняет в файл.
Как использовать все возможности XML, включая XPath и пространства имен, с высокой производительностью?
Если требуется мощная поддержка XPath, XSLT, DTD, валидация XSD, то библиотека lxml (не входит в стандартную поставку) - лучший выбор. Она основана на libxml2 и libxslt, очень быстрая.
from lxml import etree
# Парсинг
tree = etree.parse('data.xml')
root = tree.getroot()
# XPath запрос
for item in root.xpath('//item[@category="book"]/title'):
print(item.text)
# Создание
new_root = etree.Element('data')
child = etree.SubElement(new_root, 'child', attrib={'key': 'value'})
child.text = 'text'
etree.tostring(new_root, pretty_print=True, encoding='unicode')
Пояснение: xpath() возвращает список элементов по запросу. Поддерживаются полные выражения XPath 1.0. tostring() сериализует в строку.
Мастер и Маргарита Преступление и наказание
Проблемы
- Необходимо установить библиотеку: pip install lxml.
- Ошибка XMLSyntaxError при невалидном XML – аналогично ElementTree.
- XPath может генерировать исключение, если синтаксис неверен.
Как работать с XML, используя DOM-модель для древовидного доступа?
xml.dom.minidom - минимальная реализация DOM, встроенная в Python. Она предоставляет полный обход дерева, но громоздка и медленнее, чем ElementTree.
from xml.dom import minidom
# Парсинг
dom = minidom.parse('data.xml')
roots = dom.getElementsByTagName('catalog')
if roots:
items = roots[0].getElementsByTagName('item')
for item in items:
name = item.getElementsByTagName('name')[0].firstChild.data
price = item.getElementsByTagName('price')[0].firstChild.data
print(name, price)
# Создание
doc = minidom.Document()
root = doc.createElement('catalog')
doc.appendChild(root)
item = doc.createElement('item')
root.appendChild(item)
name = doc.createElement('name')
name.appendChild(doc.createTextNode('Телефон'))
item.appendChild(name)
with open('out.xml', 'w', encoding='utf-8') as f:
doc.writexml(f, addindent=' ', newl='\n')
DOM хранит весь документ в памяти. Доступ через методы getElementsByTagName и firstChild.data. Код получается многословным.
Ограничения
- Потребление памяти высоко, не подходит для больших файлов.
- Отсутствует поддержка XPath.
- Код менее читаем, чем в ElementTree.
Как обрабатывать ошибки при парсинге XML и избежать краха программы?
Независимо от библиотеки, парсинг XML может вызвать исключения. Всегда следует использовать блоки try-except.
import xml.etree.ElementTree as ET
try:
tree = ET.parse('malformed.xml')
except ET.ParseError as e:
print(f'Ошибка разбора: {e}')
except FileNotFoundError:
print('Файл не найден')
else:
root = tree.getroot()
# дальнейшая обработка
ParseError – основное исключение для XML-ошибок. Также возможны UnicodeDecodeError при неправильной кодировке. Рекомендуется указывать кодировку при открытии файла.
Расширенные примеры работы с XML
Пример 1: Итеративный парсинг большого XML файла
Для обработки гигабайтных XML без загрузки всего документа в память используется iterparse() из ElementTree или lxml. Пример с lxml:
from lxml import etree
# Предположим, что файл large.xml содержит много записей
context = etree.iterparse('large.xml', events=('end',), tag='record')
for event, elem in context:
# обрабатываем один элемент record
id_ = elem.find('id').text
data = elem.find('data').text
print(f'Record {id_}: {data}')
# очищаем освобожденные элементы
elem.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
# закрываем контекст (необязательно)
del context
Пояснение: iterparse с событиями 'end' и тегом 'record' позволяет обрабатывать каждый элемент по мере завершения парсинга. elem.clear() освобождает память. Альтернативно можно использовать clear() для родительского элемента.
Результат для 5 записей:
Record 1: А Record 2: Б Record 3: В Record 4: Г Record 5: Д
Пример 2: Применение XSLT трансформации с lxml
Библиотека lxml поддерживает XSLT. Создадим трансформацию, которая меняет порядок элементов.
from lxml import etree
# Исходный XML
xml_data = '''<root><item>B</item><item>A</item><item>C</item></root>'''
xml_tree = etree.fromstring(xml_data)
# XSLT стиль
xslt_str = '''<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<catalog>
<xsl:for-each select="root/item">
<xsl:sort select="." data-type="text" order="ascending"/>
<item><xsl:value-of select="."/></item>
</xsl:for-each>
</catalog>
</xsl:template>
</xsl:stylesheet>'''
xslt_root = etree.fromstring(xslt_str)
transform = etree.XSLT(xslt_root)
result_tree = transform(xml_tree)
print(str(result_tree))
Пояснение: Создаем объект XSLT из строки стиля. Применяем к xml-дереву. Результат - новое дерево. Метод str() дает строковое представление.
Вывод:
<catalog> <item>A</item> <item>B</item> <item>C</item> </catalog>
Пример 3: Валидация XML по XML Schema (XSD)
lxml предоставляет класс XMLSchema для проверки соответствия документа схеме.
from lxml import etree
# Схема XSD
xsd_content = '''<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>'''
xs_schema = etree.XMLSchema(etree.fromstring(xsd_content))
# Валидный XML
valid_xml = '''<root>
<item><name>Table</name><price>1500</price></item>
</root>'''
doc = etree.fromstring(valid_xml)
if xs_schema.validate(doc):
print('XML валиден')
else:
print('Ошибки:', xs_schema.error_log)
# Невалидный XML (price не число)
invalid_xml = '''<root>
<item><name>Chair</name><price>abc</price></item>
</root>'''
doc2 = etree.fromstring(invalid_xml)
if not xs_schema.validate(doc2):
for error in xs_schema.error_log:
print(f'Ошибка: {error.message}')
Пояснение: Сначала создается объект XMLSchema из схемы. Метод validate() возвращает True или False. Свойство error_log содержит подробности.
Вывод:
XML валиден Ошибка: Element 'price': 'abc' is not a valid value of type 'decimal'.
Пример 4: Работа с пространствами имен в lxml с XPath
XML часто использует namespace. Для XPath необходимо передавать словарь пространств имен.
from lxml import etree
xml_ns = '''<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:response xmlns:ns2="http://example.com/ns2">
<ns2:result>OK</ns2:result>
</ns2:response>
</soap:Body>
</soap:Envelope>'''
tree = etree.fromstring(xml_ns)
# XPath с пространством имен
ns = {'soap': 'http://schemas.xmlsoap.org/soap/envelope/', 'ns2': 'http://example.com/ns2'}
result = tree.xpath('//ns2:result/text()', namespaces=ns)
print(result)
Пояснение: В XPath используем префиксы, определенные в словаре namespaces. text() возвращает содержимое элемента.
Результат:
['OK']