XML-обработка в Python: библиотеки и методы для эффективной работы
Для кого эта статья:
- Python-разработчики, особенно начинающие и среднего уровня
- Специалисты, работающие с XML и API
Разработчики, заинтересованные в улучшении своих навыков обработки данных
Обработка XML-документов – навык, который рано или поздно понадобится каждому Python-разработчику. Будь то обмен данными с внешними API, работа с конфигурационными файлами или извлечение информации из структурированных данных – XML встречается повсеместно. Многие начинающие разработчики теряются в разнообразии библиотек и подходов: DOM, SAX, потоковая обработка, древовидные структуры... 🤔 В этой статье я проведу вас через джунгли XML-парсинга в Python, покажу оптимальные инструменты для различных задач и поделюсь реальными примерами кода, которые можно сразу внедрить в свои проекты.
Хотите углубить свои навыки и стать востребованным Python-разработчиком? Курс Python-разработки от Skypro даст вам не только фундаментальные знания, но и практический опыт работы с XML и другими форматами данных. Вы научитесь создавать эффективные веб-приложения с нуля, работать с API и базами данных. Наши выпускники трудоустраиваются уже через 8 месяцев обучения – инвестируйте в своё будущее прямо сейчас!
XML в Python: основные библиотеки и их возможности
Python предлагает несколько мощных библиотек для работы с XML, каждая со своими сильными сторонами и областями применения. Понимание их особенностей поможет выбрать оптимальный инструмент для конкретной задачи и сэкономит время на разработку.
| Библиотека | Тип парсера | Производительность | Удобство использования | Особенности |
|---|---|---|---|---|
| ElementTree | DOM | Высокая | Простой API | Встроен в стандартную библиотеку |
| minidom | DOM | Средняя | Соответствие W3C DOM API | Встроен в стандартную библиотеку |
| lxml | DOM/SAX | Очень высокая | ElementTree-совместимый API | Поддержка XPath, XSLT, валидация схем |
| SAX | Событийный | Высокая для больших файлов | Сложный API | Низкое потребление памяти |
| Beautifulsoup4 | DOM | Низкая | Очень простой API | Устойчивость к ошибкам в разметке |
Выбор библиотеки зависит от нескольких факторов:
- Размер данных: для гигантских XML-файлов событийные парсеры (SAX) или потоковые (iterparse в ElementTree) будут предпочтительнее DOM-парсеров, загружающих весь документ в память.
- Сложность обработки: если требуется выполнять XPath-запросы или XSLT-преобразования, lxml будет оптимальным выбором.
- Производительность: lxml обычно в 10-100 раз быстрее встроенных парсеров благодаря использованию библиотек libxml2 и libxslt, написанных на C.
- Совместимость: для кода, который должен работать без внешних зависимостей, стоит выбирать встроенные библиотеки (ElementTree или minidom).
Александр Петров, Senior Python Developer
Однажды я получил задачу обработать XML-файл с историческими данными биржевых котировок. Размер файла превышал 5 ГБ, и первая попытка загрузить его через ElementTree привела к исчерпанию оперативной памяти. Я перепробовал все стандартные методы, но каждый раз сталкивался с OOM-ошибками.
Решение пришло, когда я применил комбинированный подход: использовал итеративный парсинг через ElementTree.iterparse() для последовательного чтения файла и обрабатывал каждый элемент сразу после его чтения, очищая память. Ключевым трюком стало не только извлечение данных, но и явное удаление обработанных элементов из дерева:
PythonСкопировать кодfor _, element in etree.iterparse(huge_xml_file, events=('end',), tag='QUOTE'): process_data(element) element.clear() # Освобождаем ссылки на удаленные элементы while element.getprevious() is not None: del element.getparent()[0]Эта техника позволила обработать файл с постоянным потреблением памяти менее 200 МБ, что на порядки эффективнее прямолинейных подходов.

ElementTree: быстрый и эффективный парсинг XML
ElementTree — это встроенная в стандартную библиотеку Python библиотека, которая представляет XML-документ как древовидную структуру. Она сочетает производительность и удобство использования, что делает её отличным выбором для большинства задач по обработке XML.
Основные преимущества ElementTree:
- Простой и интуитивно понятный API
- Достаточная производительность для большинства задач
- Встроенная поддержка в Python (не требует установки дополнительных пакетов)
- Возможность итеративной обработки для больших XML-файлов
Рассмотрим базовые операции с ElementTree на примерах. Допустим, у нас есть файл users.xml:
<users>
<user id="1">
<name>John Doe</name>
<email>john@example.com</email>
<roles>
<role>admin</role>
<role>developer</role>
</roles>
</user>
<user id="2">
<name>Jane Smith</name>
<email>jane@example.com</email>
<roles>
<role>developer</role>
</roles>
</user>
</users>
Базовое чтение XML-файла с помощью ElementTree:
import xml.etree.ElementTree as ET
# Загрузка из файла
tree = ET.parse('users.xml')
root = tree.getroot()
# Или из строки
xml_string = '''<users>...</users>'''
root = ET.fromstring(xml_string)
# Перебор всех элементов user
for user in root.findall('user'):
user_id = user.get('id')
name = user.find('name').text
email = user.find('email').text
print(f"User {user_id}: {name}, {email}")
# Получение вложенных элементов
roles = [role.text for role in user.findall('./roles/role')]
print(f"Roles: {', '.join(roles)}")
ElementTree предлагает несколько методов для поиска элементов:
element.find(path)– находит первый дочерний элемент, соответствующий путиelement.findall(path)– находит все соответствующие элементыelement.findtext(path)– находит текст первого соответствующего элемента
Путь может содержать ограниченный набор XPath-выражений:
# Получить всех пользователей с ролью admin
admin_users = []
for user in root.findall('.//user'):
roles = [role.text for role in user.findall('./roles/role')]
if 'admin' in roles:
admin_users.append(user.find('name').text)
print(f"Administrators: {', '.join(admin_users)}")
Создание и модификация XML с ElementTree:
# Создание нового документа
root = ET.Element('users')
user = ET.SubElement(root, 'user', {'id': '3'})
ET.SubElement(user, 'name').text = 'Bob Johnson'
ET.SubElement(user, 'email').text = 'bob@example.com'
roles = ET.SubElement(user, 'roles')
ET.SubElement(roles, 'role').text = 'tester'
# Запись в файл
tree = ET.ElementTree(root)
tree.write('new_users.xml', encoding='utf-8', xml_declaration=True)
Для больших XML-файлов рекомендуется использовать итеративный подход:
# Обработка большого файла с минимальным использованием памяти
for event, elem in ET.iterparse('huge_file.xml', events=('end',)):
if elem.tag == 'user':
process_user(elem)
elem.clear() # Освобождаем память
ElementTree — это сбалансированный инструмент, подходящий для большинства задач по работе с XML. Однако, для более сложных операций, таких как выполнение полноценных XPath-запросов или XSLT-преобразований, может потребоваться более мощная библиотека, такая как lxml.
Работа с XML через minidom: особенности и применение
Библиотека minidom представляет собой легковесную реализацию DOM (Document Object Model) API в Python. Она является частью встроенного модуля xml.dom и предоставляет более традиционный подход к работе с XML-документами в соответствии со спецификациями W3C.
Minidom может быть предпочтительным выбором в следующих случаях:
- Когда требуется строгое соответствие DOM API (например, для совместимости с существующим кодом)
- При работе с небольшими XML-документами, где удобство использования важнее производительности
- Когда необходимо сохранить форматирование и комментарии в XML
Дмитрий Соколов, Python Team Lead
В одном из проектов мне пришлось интегрировать Python-код с устаревшей системой, работающей с XML через строгий DOM-интерфейс. Большинство современных библиотек не подходили из-за несоответствия API.
Изначально я пытался использовать ElementTree и адаптировать его результаты к ожидаемому формату, но это приводило к ошибкам и потере данных. После нескольких дней борьбы я обратился к minidom:
PythonСкопировать кодfrom xml.dom import minidom # Загружаем документ doc = minidom.parse('legacy_data.xml') # Работаем напрямую через DOM API elements = doc.getElementsByTagName('record') for element in elements: id_attr = element.getAttribute('id') name_node = element.getElementsByTagName('name')[0] name_text = name_node.firstChild.nodeValue # Модификация данных в DOM-стиле if some_condition: new_node = doc.createElement('status') text = doc.createTextNode('active') new_node.appendChild(text) element.appendChild(new_node)Этот подход позволил сохранить все тонкости исходного XML-формата: комментарии, порядок атрибутов, пространства имён — всё то, что обычно "сглаживается" более высокоуровневыми библиотеками. Несмотря на более многословный код, интеграция прошла безупречно.
Базовые операции с minidom:
from xml.dom import minidom
# Разбор XML из файла
doc = minidom.parse('users.xml')
# Или из строки
xml_string = '''<users>...</users>'''
doc = minidom.parseString(xml_string)
# Получение элементов по тегу
users = doc.getElementsByTagName('user')
for user in users:
# Получение атрибута
user_id = user.getAttribute('id')
# Получение текста элемента
name = user.getElementsByTagName('name')[0].firstChild.nodeValue
email = user.getElementsByTagName('email')[0].firstChild.nodeValue
print(f"User {user_id}: {name}, {email}")
# Получение вложенных элементов
roles_elem = user.getElementsByTagName('roles')[0]
role_elems = roles_elem.getElementsByTagName('role')
roles = [role.firstChild.nodeValue for role in role_elems]
print(f"Roles: {', '.join(roles)}")
Создание и модификация XML с minidom:
# Создание нового документа
doc = minidom.getDOMImplementation().createDocument(None, "users", None)
root = doc.documentElement
# Создание элемента user
user = doc.createElement('user')
user.setAttribute('id', '3')
root.appendChild(user)
# Добавление дочерних элементов
name = doc.createElement('name')
name_text = doc.createTextNode('Bob Johnson')
name.appendChild(name_text)
user.appendChild(name)
email = doc.createElement('email')
email_text = doc.createTextNode('bob@example.com')
email.appendChild(email_text)
user.appendChild(email)
# Создание ещё одного уровня вложенности
roles = doc.createElement('roles')
user.appendChild(roles)
role = doc.createElement('role')
role_text = doc.createTextNode('tester')
role.appendChild(role_text)
roles.appendChild(role)
# Сохранение с отступами
xml_str = doc.toprettyxml(indent=" ")
with open('new_users.xml', 'w') as f:
f.write(xml_str)
| Операция | ElementTree | minidom |
|---|---|---|
| Загрузка из файла | ET.parse('file.xml') | minidom.parse('file.xml') |
| Загрузка из строки | ET.fromstring(xml_string) | minidom.parseString(xml_string) |
| Получение элементов | root.findall('element') | doc.getElementsByTagName('element') |
| Получение атрибута | element.get('attr') | element.getAttribute('attr') |
| Получение текста | element.text | element.firstChild.nodeValue |
| Создание элемента | ET.SubElement(parent, 'tag') | doc.createElement('tag')<br>parent.appendChild(element) |
| Запись в файл | tree.write('file.xml') | f.write(doc.toprettyxml()) |
Основные недостатки minidom:
- Более многословный и менее интуитивный API по сравнению с ElementTree
- Более низкая производительность, особенно для больших документов
- Отсутствие поддержки XPath и других продвинутых функций
- Более высокое потребление памяти для крупных XML-файлов
Несмотря на эти недостатки, minidom остаётся полезным инструментом, особенно в ситуациях, где важна совместимость с DOM API или необходимо точное сохранение структуры XML, включая комментарии и форматирование. 🔄
Мощь lxml: продвинутые методы обработки XML-документов
Библиотека lxml — это мощный инструмент для обработки XML, который объединяет скорость и функциональность библиотек libxml2 и libxslt, написанных на C, с удобством Python API. Это делает lxml одним из самых быстрых и функциональных XML-парсеров для Python. 🚀
Ключевые преимущества lxml:
- Высокая производительность (в 10-100 раз быстрее встроенных парсеров)
- Полная поддержка XPath 1.0
- Встроенная поддержка XSLT-трансформаций
- Валидация XML через DTD, XML Schema, Relax NG
- API, совместимый с ElementTree, что облегчает миграцию
- Устойчивость к ошибкам в XML через lxml.html
- Поддержка инкрементального парсинга больших файлов
Установка lxml:
pip install lxml
Базовые операции с lxml:
from lxml import etree
# Разбор из файла
tree = etree.parse('users.xml')
root = tree.getroot()
# Или из строки
xml_string = '''<users>...</users>'''
root = etree.fromstring(xml_string)
# Работа как с ElementTree
for user in root.findall('user'):
user_id = user.get('id')
name = user.find('name').text
email = user.find('email').text
print(f"User {user_id}: {name}, {email}")
Где lxml действительно выделяется, так это в поддержке полноценного XPath. В отличие от ограниченной реализации XPath в ElementTree, lxml поддерживает полный стандарт XPath 1.0, включая функции, предикаты и сложные выражения:
# Использование полноценного XPath
# Найти всех пользователей с ролью 'admin'
admin_users = root.xpath('//user[roles/role="admin"]')
for user in admin_users:
print(f"Admin user: {user.find('name').text}")
# Выбор атрибутов через XPath
all_user_ids = root.xpath('//user/@id')
print(f"User IDs: {all_user_ids}")
# Комбинирование условий
developers_with_email = root.xpath('//user[roles/role="developer" and contains(email, "@example.com")]')
print(f"Found {len(developers_with_email)} developers with example.com email")
# Использование функций XPath
first_user_name = root.xpath('string(//user[1]/name)')
user_count = root.xpath('count(//user)')
print(f"First user: {first_user_name}, Total users: {int(user_count)}")
lxml также предлагает мощные возможности для модификации XML-документов:
# Добавление нового элемента
new_user = etree.SubElement(root, 'user', id='4')
etree.SubElement(new_user, 'name').text = 'Alice Cooper'
etree.SubElement(new_user, 'email').text = 'alice@example.com'
roles = etree.SubElement(new_user, 'roles')
etree.SubElement(roles, 'role').text = 'manager'
# Модификация существующих элементов
for user in root.xpath('//user[roles/role="developer"]'):
# Добавляем новую роль для разработчиков
roles = user.find('roles')
new_role = etree.SubElement(roles, 'role')
new_role.text = 'coder'
# Удаление элементов
for old_role in root.xpath('//role[text()="developer"]'):
parent = old_role.getparent()
parent.remove(old_role)
# Запись с красивым форматированием
tree = etree.ElementTree(root)
tree.write('modified_users.xml', pretty_print=True, encoding='utf-8', xml_declaration=True)
XSLT-трансформации — ещё одна мощная возможность lxml:
# Определение XSLT-преобразования
xslt_string = '''
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<h1>User List</h1>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Roles</th>
</tr>
<xsl:for-each select="users/user">
<tr>
<td><xsl:value-of select="@id"/></td>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="email"/></td>
<td>
<xsl:for-each select="roles/role">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
'''
# Создание XSLT-преобразователя
xslt_root = etree.XML(xslt_string)
transform = etree.XSLT(xslt_root)
# Применение преобразования
result = transform(tree)
# Сохранение результата
with open('users.html', 'wb') as f:
f.write(etree.tostring(result, pretty_print=True))
Валидация XML с использованием схем — ещё одна важная функция lxml:
# XML Schema валидация
schema_str = '''
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="users">
<xs:complexType>
<xs:sequence>
<xs:element name="user" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
<xs:element name="roles">
<xs:complexType>
<xs:sequence>
<xs:element name="role" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:positiveInteger" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
'''
schema_root = etree.XML(schema_str)
schema = etree.XMLSchema(schema_root)
# Проверка документа на соответствие схеме
try:
schema.assertValid(tree)
print("Document is valid")
except etree.DocumentInvalid as err:
print(f"Document is invalid: {err}")
lxml также эффективно обрабатывает большие XML-файлы через итеративный парсинг:
# Обработка большого XML-файла с минимальным использованием памяти
for event, element in etree.iterparse('huge_file.xml', events=('end',), tag='user'):
# Обработка каждого пользователя
process_user(element)
# Очистка памяти
element.clear()
while element.getprevious() is not None:
del element.getparent()[0]
lxml — это невероятно мощная библиотека, которая сочетает высокую производительность с богатым набором возможностей. Если ваш проект требует серьезной работы с XML, включая XPath-запросы, XSLT-преобразования или валидацию схем, lxml является оптимальным выбором. 💪
Практические задачи: от чтения до модификации XML в Python
Для закрепления материала рассмотрим несколько практических задач, с которыми часто сталкиваются разработчики при работе с XML. Эти примеры демонстрируют применение различных библиотек и подходов в реальных сценариях. 📊
Задача 1: Чтение и анализ конфигурационного XML-файла
Предположим, у нас есть конфигурационный файл приложения в формате XML:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<database>
<host>localhost</host>
<port>3306</port>
<name>myapp_db</name>
<user>app_user</user>
<password>s3cr3t</password>
</database>
<api>
<endpoint>https://api.example.com/v1</endpoint>
<timeout>30</timeout>
<retry>3</retry>
</api>
<logging>
<level>INFO</level>
<path>/var/log/myapp.log</path>
<max_size>10485760</max_size>
<backups>5</backups>
</logging>
</config>
Решение с использованием ElementTree:
import xml.etree.ElementTree as ET
import os
def load_config(config_path):
"""Загружает конфигурацию из XML-файла в словарь Python."""
if not os.path.exists(config_path):
raise FileNotFoundError(f"Config file not found: {config_path}")
tree = ET.parse(config_path)
root = tree.getroot()
config = {}
# Обработка секции database
db = root.find('database')
if db is not None:
config['database'] = {
'host': db.findtext('host', 'localhost'),
'port': int(db.findtext('port', '3306')),
'name': db.findtext('name', 'default_db'),
'user': db.findtext('user', ''),
'password': db.findtext('password', '')
}
# Обработка секции api
api = root.find('api')
if api is not None:
config['api'] = {
'endpoint': api.findtext('endpoint', ''),
'timeout': int(api.findtext('timeout', '60')),
'retry': int(api.findtext('retry', '3'))
}
# Обработка секции logging
logging = root.find('logging')
if logging is not None:
config['logging'] = {
'level': logging.findtext('level', 'INFO'),
'path': logging.findtext('path', './app.log'),
'max_size': int(logging.findtext('max_size', '1048576')),
'backups': int(logging.findtext('backups', '3'))
}
return config
# Использование
try:
app_config = load_config('config.xml')
print(f"Database connection: {app_config['database']['user']}@{app_config['database']['host']}")
print(f"API endpoint: {app_config['api']['endpoint']}")
print(f"Log level: {app_config['logging']['level']}")
except Exception as e:
print(f"Error loading config: {e}")
Задача 2: Преобразование данных из CSV в XML
Часто требуется конвертировать данные из одного формата в другой. Рассмотрим пример преобразования CSV-файла в XML:
import csv
import xml.etree.ElementTree as ET
from xml.dom import minidom
def csv_to_xml(csv_file, xml_file, root_element='data', row_element='item'):
"""Преобразует CSV-файл в XML."""
# Чтение CSV
with open(csv_file, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
# Создание корневого элемента XML
root = ET.Element(root_element)
# Обработка каждой строки CSV
for row in reader:
# Создание элемента для строки
item = ET.SubElement(root, row_element)
# Добавление всех полей из строки CSV как атрибуты или вложенные элементы
for field, value in row.items():
# Заменяем проблемные символы в именах полей
field_name = ''.join(c if c.isalnum() else '_' for c in field)
# Создаём элемент для каждого поля
field_elem = ET.SubElement(item, field_name)
field_elem.text = value
# Преобразование в строку с отступами
xml_str = minidom.parseString(ET.tostring(root)).toprettyxml(indent=" ")
# Запись в файл
with open(xml_file, 'w', encoding='utf-8') as out:
out.write(xml_str)
return True
# Пример использования
try:
result = csv_to_xml('employees.csv', 'employees.xml', 'employees', 'employee')
print(f"Conversion {'successful' if result else 'failed'}")
except Exception as e:
print(f"Error during conversion: {e}")
Задача 3: Извлечение данных из XML с использованием XPath
Предположим, у нас есть XML с данными о книгах в библиотеке, и нам нужно извлечь определённую информацию. Здесь пригодится lxml с его поддержкой XPath:
from lxml import etree
def extract_books_info(xml_file, author=None, category=None, min_year=None):
"""
Извлекает информацию о книгах с применением фильтров.
Возвращает список словарей с данными о книгах.
"""
tree = etree.parse(xml_file)
root = tree.getroot()
# Построение XPath-запроса на основе фильтров
xpath_query = "//book"
conditions = []
if author:
conditions.append(f"author[contains(text(), '{author}')]")
if category:
conditions.append(f"@category='{category}'")
if min_year:
conditions.append(f"year >= {min_year}")
if conditions:
xpath_query += "[" + " and ".join(conditions) + "]"
# Выполнение запроса
books = root.xpath(xpath_query)
# Сбор результатов
results = []
for book in books:
book_info = {
'id': book.get('id'),
'title': book.findtext('title', ''),
'author': book.findtext('author', ''),
'year': int(book.findtext('year', '0')),
'price': float(book.findtext('price', '0')),
'category': book.get('category', '')
}
results.append(book_info)
return results
# Использование
try:
# Поиск книг по фантастике после 2000 года
sci_fi_books = extract_books_info('library.xml', category='sci-fi', min_year=2000)
print(f"Found {len(sci_fi_books)} sci-fi books published after 2000:")
for book in sci_fi_books:
print(f"- {book['title']} by {book['author']} ({book['year']})")
# Поиск книг определённого автора
author_books = extract_books_info('library.xml', author='Tolkien')
print(f"\nFound {len(author_books)} books by Tolkien:")
for book in author_books:
print(f"- {book['title']} ({book['year']}) – ${book['price']}")
except Exception as e:
print(f"Error processing books: {e}")
Задача 4: Модификация существующего XML-документа
Рассмотрим пример, где нам нужно обновить XML-документ с данными о сотрудниках:
import xml.etree.ElementTree as ET
def update_employee_data(xml_file, employee_id, updates):
"""
Обновляет информацию о сотруднике в XML-файле.
Args:
xml_file: путь к XML-файлу
employee_id: ID сотрудника для обновления
updates: словарь с обновляемыми полями
Returns:
bool: True если обновление успешно, False если сотрудник не найден
"""
tree = ET.parse(xml_file)
root = tree.getroot()
# Поиск сотрудника по ID
employee = root.find(f".//employee[@id='{employee_id}']")
if employee is None:
return False
# Обновление полей
for field, value in updates.items():
# Проверяем, существует ли такой элемент
field_elem = employee.find(field)
if field_elem is not None:
# Обновляем существующий элемент
field_elem.text = str(value)
else:
# Создаём новый элемент
new_field = ET.SubElement(employee, field)
new_field.text = str(value)
# Сохраняем изменения
tree.write(xml_file, encoding='utf-8', xml_declaration=True)
return True
# Пример использования
try:
updates = {
'position': 'Senior Developer',
'salary': '75000',
'department': 'Engineering'
}
success = update_employee_data('employees.xml', '1001', updates)
if success:
print("Employee information updated successfully")
else:
print("Employee not found")
except Exception as e:
print(f"Error updating employee data: {e}")
Задача 5: Генерация отчёта в формате HTML на основе XML-данных
Часто XML используется как промежуточный формат данных, которые затем преобразуются в более удобный для конечного пользователя вид. Рассмотрим пример генерации HTML-отчёта:
from lxml import etree
def generate_sales_report(xml_file, output_html):
"""Генерирует HTML-отчёт о продажах на основе XML-данных."""
# XSLT-шаблон для преобразования
xslt_str = '''
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<xsl:template match="/">
<html>
<head>
<title>Sales Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #2c3e50; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f9f9f9; }
.total { font-weight: bold; background-color: #e9f7ef; }
</style>
</head>
<body>
<h1>Sales Report</h1>
<h2>Summary</h2>
<p>Period: <xsl:value-of select="sales/@period"/></p>
<p>Total Sales: $<xsl:value-of select="format-number(sum(//sale/amount), '#,##0.00')"/></p>
<p>Number of Transactions: <xsl:value-of select="count(//sale)"/></p>
<h2>Sales by Product</h2>
<table>
<tr>
<th>Product</th>
<th>Total Units</th>
<th>Total Amount</th>
</tr>
<xsl:for-each select="//product[not(@name=preceding::product/@name)]/@name">
<xsl:variable name="product_name" select="."/>
<tr>
<td><xsl:value-of select="$product_name"/></td>
<td><xsl:value-of select="sum(//sale[product/@name=$product_name]/quantity)"/></td>
<td>$<xsl:value-of select="format-number(sum(//sale[product/@name=$product_name]/amount), '#,##0.00')"/></td>
</tr>
</xsl:for-each>
<tr class="total">
<td>Total</td>
<td><xsl:value-of select="sum(//quantity)"/></td>
<td>$<xsl:value-of select="format-number(sum(//amount), '#,##0.00')"/></td>
</tr>
</table>
<h2>Detailed Sales</h2>
<table>
<tr>
<th>Date</th>
<th>Customer</th>
<th>Product</th>
<th>Quantity</th>
<th>Amount</th>
</tr>
<xsl:for-each select="//sale">
<xsl:sort select="date" order="descending"/>
<tr>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="customer"/></td>
<td><xsl:value-of select="product/@name"/></td>
<td><xsl:value-of select="quantity"/></td>
<td>$<xsl:value-of select="format-number(amount, '#,##0.00')"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
'''
try:
# Загружаем XML и XSLT
xml_doc = etree.parse(xml_file)
xslt_doc = etree.fromstring(xslt_str)
transform = etree.XSLT(xslt_doc)
# Применяем трансформацию
result = transform(xml_doc)
# Сохраняем результат
with open(output_html, 'wb') as f:
f.write(etree.tostring(result, pretty_print=True, method='html', encoding='utf-8'))
return True
except Exception as e:
print(f"Error generating report: {e}")
return False
# Пример использования
if generate_sales_report('sales_data.xml', 'sales_report.html'):
print("Sales report generated successfully!")
else:
print("Failed to generate sales report")
Эти практические примеры демонстрируют, как Python-библиотеки для работы с XML могут применяться для решения разнообразных задач — от простого чтения конфигураций до сложного анализа данных и генерации отчётов. 🔍
Python предлагает богатый арсенал инструментов для работы с XML, подходящих для любой задачи. ElementTree подойдет для повседневных операций благодаря своей простоте и интеграции со стандартной библиотекой. Minidom незаменим при необходимости строгого соответствия DOM-спецификации. А lxml станет выбором профессионалов для высокопроизводительной обработки больших XML-документов с поддержкой XPath и XSLT. Главное — правильно оценить масштаб задачи и выбрать библиотеку, которая обеспечит баланс между удобством разработки и эффективностью исполнения. Вооружившись этими знаниями, вы превратите работу с XML из рутинной задачи в мощный инструмент решения бизнес-задач.