В стандартной библиотеке Python имеется две реализации этого модуля – xml.etree.ElementTree
и xml.etree.cElementTree
.
xml.etree.ElementTree
– реализация API для работы с XML файлами на чистом Python, а xml.etree.cElementTree
– то же, но на C, и даёт существенный прирост производительности при обработке больших файлов.
Можно импортировать их так:
#!/usr/bin/env python try: import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET print ET
Результат:
$ ./xml_par.py <module 'xml.etree.cElementTree' from '/usr/lib64/python2.6/xml/etree/cElementTree.pyc'>
В Python версии 3.3 и выше необходимости в такой try/except
нет, т.к. интерпретатор самостоятельно будет выполнять поиск cElementTree
при импорте ElementTree
(да и в Python 2.6 и 2.7 cElementTree
импортируется без проблем).
Содержание
Парсинг XML-файла
Для примера возьмём простой XML-файл с таким содержимым:
<TreeRoot> <Element1 value1="Value1"> <SubElement1>SubElement1</SubElement1> <SubElement2>SubElement2</SubElement2> </Element1> <Element2 value2="Value2"> <SubElement3>SubElement3</SubElement3> <SubElement4>SubElement4</SubElement4> </Element2> </TreeRoot>
Изменим скрипт:
import os import xml.etree.cElementTree as ET XML_FILE = os.path.join(os.environ['HOME'], 'xmlfile.xml') try: tree = ET.ElementTree(file=XML_FILE) print help(tree) except IOError as e: print 'nERROR - cant find file: %sn' % e
В результате – мы должны получить список доступных методов для объекта tree
:
Help on instance of ElementTree in module __builtin__: class ElementTree(xml.etree.ElementTree.ElementTree) | Methods defined here: | | parse(self, source, parser=None) | | ---------------------------------------------------------------------- | Methods inherited from xml.etree.ElementTree.ElementTree: | | __init__(self, element=None, file=None) | | find(self, path) | | findall(self, path) | | findtext(self, path, default=None) | | getiterator(self, tag=None) | | getroot(self) | | write(self, file, encoding='us-ascii')
В случае ошибки синтаксиса XML – будет вызвана ошибка с указанием точного места:
cElementTree.ParseError: mismatched tag: line 11, column 2
Тогда файл стоит проверить в XML-валидаторе, например – тут>>>.
Для получения корневого элемента – используется метод getroot()
:
... try: tree = ET.ElementTree(file=XML_FILE) print tree.getroot() print type(tree.getroot()) ...
$ ./xml_par.py <Element 'TreeRoot' at 0x6ffffd96120> <type 'Element'>
Каждый элемент содержит несколько параметров:
tag
– строка, отображающая тип данных, которые представляет элемент;attrib
– атрибуты элемента, которые сохраняются в словарь Python;text
– текстовое значение элемента;- дочерние элементы.
Например – получить тег корневого элемента можно так:
... try: tree = ET.ElementTree(file=XML_FILE) root = tree.getroot() print root.tag ...
$ ./xml_par.py TreeRoot
Что бы получить список прямых потомков корневого элемента – можно просто вызвать цикл:
... try: tree = ET.ElementTree(file=XML_FILE) root = tree.getroot() for child_of_root in root: print child_of_root.tag, child_of_root.attrib ...
Результат:
$ ./xml_par.py TreeRoot Element1 {'value1': 'Value1'} Element2 {'value2': 'Value2'}
Можно так же вывести только ключи, или ключи:значения:
... for child_of_root in root: print child_of_root.tag, child_of_root.keys(), child_of_root.items() ...
Результат:
$ ./xml_par.py Element1 ['value1'] [('value1', 'Value1')] Element2 ['value2'] [('value2', 'Value2')]
Поиск элементов в файле
Что бы перебрать все элементы в файле – можно воспользоваться методом iter()
:
... try: tree = ET.ElementTree(file=XML_FILE) root = tree.getroot() for child_of_root in root.iter(): print 'Tag: %snKeys: %snItems: %snText: %sn' % (child_of_root.tag, child_of_root.keys(), child_of_root.items(), child_of_root.text) ...
Результат:
$ ./xml_par.py Tag: TreeRoot Keys: [] Items: [] Text: Tag: Element1 Keys: ['value1'] Items: [('value1', 'Value1')] Text: Tag: SubElement1 Keys: [] Items: [] Text: SubElement1 Tag: SubElement2 Keys: [] Items: [] Text: SubElement2 Tag: Element2 Keys: ['value2'] Items: [('value2', 'Value2')] Text: Tag: SubElement3 Keys: [] Items: [] Text: SubElement3 Tag: SubElement4 Keys: [] Items: [] Text: SubElement4
А что бы найти только один элемент – его тег можно передать аргументом этому методу:
... for child_of_root in root.iter('SubElement1'): print 'Tag: %snKeys: %snItems: %snText: %sn' % (child_of_root.tag, child_of_root.keys(), child_of_root.items(), child_of_root.text) ...
Результат:
$ ./xml_par.py Tag: SubElement1 Keys: [] Items: [] Text: SubElement1
Можно выполнить поиск с помощью XPath.
Например, что бы отобразить корневой элемент:
... try: tree = ET.ElementTree(file=XML_FILE) root = tree.getroot() for item in root.iterfind('.'): print 'Find: %sn' % item.tag ...
Результат:
$ ./xml_par.py Find: TreeRoot
С помощью //
можно найти все вложенные элементы:
... for item in root.iterfind('.//'): print 'Find: %sn' % item.tag ...
$ ./xml_par.py Find: Element1 Find: SubElement1 Find: SubElement2 Find: Element2 Find: SubElement3 Find: SubElement4
Или вложенные элементы вложенного элемента:
... for item in root.iterfind('./Element1//'): print 'Find: %sn' % item.tag ...
Результат:
$ ./xml_par.py Find: SubElement1 Find: SubElement2
Добавление записей в файл
Для начала – добавим функцию prettify:
from xml.dom import minidom def prettify(elem): """Return a pretty-printed XML string for the Element. """ rough_string = ET.tostring(elem, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent='t')
Теперь – отредактируем код:
#!/usr/bin/env python import os import xml.etree.cElementTree as ET from xml.dom import minidom def prettify(elem): """Return a pretty-printed XML string for the Element. """ rough_string = ET.tostring(elem, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent='t') XML_FILE = os.path.join(os.environ['HOME'], 'xmlfile.xml') try: tree = ET.parse(XML_FILE) root = tree.getroot() new_element = ET.Element('NewElement') new_subelement = ET.SubElement(new_element, 'NewSubelement') new_subelement.text = 'NewSubelement' root.append(new_element) print prettify(root) # пока оставим так # tree.write(XML_FILE) except IOError as e: print 'nERROR - cant find file: %sn' % e
- с помощью
ET.Element
мы создаём новый элемент с именемNewElement
; - затем – с помощью
SubElement
– мы добавляем новый вложенный элемент с имменемNewSubelement
внутрь элемента в объектеnew_element
; - далее – мы определяем параметр
text
объектаnew_subelement
, и задаём занчение'NewSubelement'
; - после этого – мы добавляем новый элемент
new_element
со всем содержимым к корню нашего файла; - последним – вызываем
print()
и функциюprettify()
.
Результат:
$ ./xml_par.py <?xml version="1.0" ?> <TreeRoot> <Element1 value1="Value1"> <SubElement1>SubElement1</SubElement1> <SubElement2>SubElement2</SubElement2> </Element1> <Element2 value2="Value2"> <SubElement3>SubElement3</SubElement3> <SubElement4>SubElement4</SubElement4> </Element2> <NewElement> <NewSubelement>NewSubelement</NewSubelement> </NewElement> </TreeRoot>
Для того, что бы в таком виде записать в файл – можно использовать функцию indent():
def indent(elem, level=0): i = "n" + level*" " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: indent(elem, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i
И вызвать её, передав корневой элемент, перед запись в файл:
... indent(root) tree.write(XML_FILE) ...
Результат:
$ cat xmlfile.xml <TreeRoot> <Element1 value1="Value1"> <SubElement1>SubElement1</SubElement1> <SubElement2>SubElement2</SubElement2> </Element1> <Element2 value2="Value2"> <SubElement3>SubElement3</SubElement3> <SubElement4>SubElement4</SubElement4> </Element2> <NewElement> <NewSubelement>NewSubelement</NewSubelement> </NewElement> </TreeRoot>
Файл с функциями можно скачать тут>>>.
Что бы добавить новый элемент внутрь уже имеющегося – можно указать его индекс в корне, например:
... new_element = ET.Element('NewElementInElement2') new_subelement = ET.SubElement(new_element, 'NewSubelementInElement2') new_subelement.text = 'NewSubelementInElement2' root[1].append(new_element) ...
Результат:
$ ./xml_par.py <?xml version="1.0" ?> <TreeRoot> <Element1 value1="Value1"> <SubElement1>SubElement1</SubElement1> <SubElement2>SubElement2</SubElement2> </Element1> <Element2 value2="Value2"> <SubElement3>SubElement3</SubElement3> <SubElement4>SubElement4</SubElement4> <NewElementInElement2> <NewSubelementInElement2>NewSubelementInElement2</NewSubelementInElement2> </NewElementInElement2> </Element2> <NewElement> <NewSubelement>NewSubelement</NewSubelement> </NewElement> </TreeRoot>
Ссылки по теме