На этом шаге мы рассмотрим, как можно решить эту задачу.
Вам нужно распарсить XML-документ, но он использует пространства имен XML.
Предположим, что у нас есть документ, использующий пространства имен:
<?xml version="1.0" encoding="utf-8"?>
<top>
<author>David Beazley</author>
<content>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
</content>
</top>
Если вы парсите этот документ и пытаетесь выполнить обычные запросы, вы обнаружите, что это не работает так просто, поскольку все становится невероятно многословным:
>>> from xml.etree.ElementTree import parse, Element >>> doc = parse('autor.xml') >>> # Некоторые запросы, которые работают >>> doc.findtext('author') 'David Beazley' >>> doc.find('content') <Element 'content' at 0x00000264BFA25CC0> >>> # Запрос с использованием пространства имен (не работает) >>> doc.find('content/html') >>> # Работает при полном определении >>> doc.find('content/{http://www.w3.org/1999/xhtml}html') <Element '{http://www.w3.org/1999/xhtml}html' at 0x00000264BFA25BD0> >>> # Не работает >>> doc.findtext('content/{http://www.w3.org/1999/xhtml}html/head/title') >>> # Полностью определен >>> doc.findtext('content/{http://www.w3.org/1999/xhtml}html/' '{http://www.w3.org/1999/xhtml}head/{http://www.w3.org/1999/xhtml}title') 'Hello World' >>>
Часто вы можете упростить дело путем заворачивания работы с пространством имен во вспомогательный класс:
>>> class XMLNamespaces: def __init__(self, **kwargs): self.namespaces = {} for name, uri in kwargs.items(): self.register(name, uri) def register(self, name, uri): self.namespaces[name] = '{' + uri + '}' def __call__(self, path): return path.format_map(self.namespaces)
Чтобы использовать этот класс, вы можете поступить так:
>>> ns = XMLNamespaces(html='http://www.w3.org/1999/xhtml') >>> doc.find(ns('content/{html}html')) <Element '{http://www.w3.org/1999/xhtml}html' at 0x00000264BFA25BD0> >>> doc.findtext(ns('content/{html}html/{html}head/{html}title')) 'Hello World' >>>
Парсинг XML-документов, содержащих пространства имен, может быть запутанным. Класс XMLNamespaces на самом деле предназначен для облегчения этой задачи: он позволяет использовать сокращенные имена для пространств имен в последующих операциях, а не полные URI.
К несчастью, в базовом парсере ElementTree нет механизма для получения дополнительной информации о пространствах имен. Однако вы можете получить немного больше информации об области видимости обработки пространств имен, если будете использовать функцию iterparse(). Например:
>>> from xml.etree.ElementTree import iterparse >>> for evt, elem in iterparse('autor.xml', ('end', 'start-ns', 'end-ns')): print(evt, elem) end <Element 'author' at 0x00000264BFA76540> start-ns ('', 'http://www.w3.org/1999/xhtml') end <Element '{http://www.w3.org/1999/xhtml}title' at 0x00000264BFA767C0> end <Element '{http://www.w3.org/1999/xhtml}head' at 0x00000264BFA76720> end <Element '{http://www.w3.org/1999/xhtml}h1' at 0x00000264BFA768B0> end <Element '{http://www.w3.org/1999/xhtml}body' at 0x00000264BFA76860> end <Element '{http://www.w3.org/1999/xhtml}html' at 0x00000264BFA76680> end-ns None end <Element 'content' at 0x00000264BFA76590> end <Element 'top' at 0x00000264BFA764F0> >>>
Последнее замечание: если текст, который вы парсите, использует пространства имен в дополнение к другим продвинутым возможностям XML, вам лучше перейти с ElementTree на библиотеку lxml. Например, она предоставляет улучшенную поддержку валидации документов по DTD, более полную поддержку XPath и другие продвинутые возможности XML. А этот рецепт - просто небольшой фикс для облегчения парсинга.
На следующем шаге мы рассмотрим взаимодействие с реляционной базой данных.