Обработка XML на языке Python: методы и инструменты

Раздел: Обработка данных -> Работа с XML

Основные подходы к обработке 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']

Работа с XML в Python - comments

En
Python xml (python)