Чтение и запись файлов XML в Python

XML или расширяемый язык разметки - это язык разметки, который обычно используется для структурирования, хранения и передачи данных между системами. Хотя он и не так распространен, как раньше, он все еще используется в таких службах, как RSS и SOAP, а также для структурирования файлов, таких как документы Microsoft Office. Поскольку Python является популярным языком для Интернета и анализа данных, вполне вероятно, что в какой-то момент вам понадобится читать или записывать XML-данные, и в этом случае вам повезло. В этой статье мы в первую очередь будем использовать все

XML или расширяемый язык разметки - это язык разметки, который обычно используется для структурирования, хранения и передачи данных между системами. Хотя он и не так распространен, как раньше, он все еще используется в таких службах, как RSS и SOAP, а также для структурирования файлов, таких как документы Microsoft Office.

Поскольку Python является популярным языком для Интернета и анализа данных, вполне вероятно, что в какой-то момент вам понадобится читать или записывать XML-данные, и в этом случае вам повезло.

В этой статье мы в первую очередь рассмотрим модуль ElementTree для чтения, записи и изменения данных XML. Мы также сравним его со старым модулем minidom в первых нескольких разделах, чтобы вы могли получить хорошее сравнение этих двух.

Модули XML

minidom или минимальная реализация DOM - это упрощенная реализация объектной модели документа (DOM). DOM - это интерфейс прикладного программирования, который рассматривает XML как древовидную структуру, где каждый узел в дереве является объектом. Таким образом, использование этого модуля требует, чтобы мы были знакомы с его функциями.

Модуль ElementTree предоставляет более "питонический" интерфейс для работы с XMl и является хорошим вариантом для тех, кто не знаком с DOM. Это также, вероятно, лучший кандидат для использования большим количеством начинающих программистов из-за его простого интерфейса, который вы увидите в этой статье.

В этой статье ElementTree будет использоваться во всех примерах, тогда как minidom также будет продемонстрирован, но только для подсчета и чтения XML-документов.

Пример файла XML

В приведенных ниже примерах мы будем использовать следующий XML-файл, который мы сохраним как «items.xml»:

 <data> 
 <items> 
 <item name="item1">item1abc</item> 
 <item name="item2">item2abc</item> 
 </items> 
 </data> 

Как видите, это довольно простой пример XML, содержащий всего несколько вложенных объектов и один атрибут. Однако этого должно быть достаточно, чтобы продемонстрировать все операции XML в этой статье.

Чтение XML-документов

Использование минидома

Чтобы проанализировать XML-документ с помощью minidom , мы должны сначала импортировать его из модуля xml.dom Этот модуль использует parse для создания объекта DOM из нашего XML-файла. Функция parse имеет следующий синтаксис:

 xml.dom.minidom.parse(filename_or_file[, parser[, bufsize]]) 

Здесь имя файла может быть строкой, содержащей путь к файлу или объект типа файла. Функция возвращает документ, который можно обрабатывать как тип XML. Таким образом, мы можем использовать функцию getElementByTagName() чтобы найти конкретный тег.

Поскольку каждый узел можно рассматривать как объект, мы можем получить доступ к атрибутам и тексту элемента, используя свойства объекта. В приведенном ниже примере мы получили доступ к атрибутам и тексту определенного узла и всех узлов вместе.

 from xml.dom import minidom 
 
 # parse an xml file by name 
 mydoc = minidom.parse('items.xml') 
 
 items = mydoc.getElementsByTagName('item') 
 
 # one specific item attribute 
 print('Item #2 attribute:') 
 print(items[1].attributes['name'].value) 
 
 # all item attributes 
 print('\nAll attributes:') 
 for elem in items: 
 print(elem.attributes['name'].value) 
 
 # one specific item's data 
 print('\nItem #2 data:') 
 print(items[1].firstChild.data) 
 print(items[1].childNodes[0].data) 
 
 # all items data 
 print('\nAll item data:') 
 for elem in items: 
 print(elem.firstChild.data) 

Результат такой:

 $ python minidomparser.py 
 Item #2 attribute: 
 item2 
 
 All attributes: 
 item1 
 item2 
 
 Item #2 data: 
 item2abc 
 item2abc 
 
 All item data: 
 item1abc 
 item2abc 

фигура 1

Если мы хотим использовать уже открытый файл, мы можем просто передать наш файловый объект для parse следующим образом:

 datasource = open('items.xml') 
 
 # parse an open file 
 mydoc = parse(datasource) 

Кроме того, если данные XML уже были загружены в виде строки, мы могли бы использовать вместо этого функцию parseString()

Использование ElementTree

ElementTree представляет нам очень простой способ обработки файлов XML. Как всегда, чтобы использовать его, мы должны сначала импортировать модуль. В нашем коде мы используем команду import с as , что позволяет нам использовать упрощенное имя ( ET ) для модуля в коде.

После импорта мы создаем древовидную структуру с parse и получаем ее корневой элемент. Получив доступ к корневому узлу, мы можем легко перемещаться по дереву, потому что дерево - это связный граф.

Используя ElementTree и аналогично предыдущему примеру кода, мы получаем атрибуты узла и текст, используя объекты, связанные с каждым узлом.

Код выглядит следующим образом:

 import xml.etree.ElementTree as ET 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # one specific item attribute 
 print('Item #2 attribute:') 
 print(root[0][1].attrib) 
 
 # all item attributes 
 print('\nAll attributes:') 
 for elem in root: 
 for subelem in elem: 
 print(subelem.attrib) 
 
 # one specific item's data 
 print('\nItem #2 data:') 
 print(root[0][1].text) 
 
 # all items data 
 print('\nAll item data:') 
 for elem in root: 
 for subelem in elem: 
 print(subelem.text) 

Результат будет следующим:

 $ python treeparser.py 
 Item #2 attribute: 
 item2 
 
 All attributes: 
 item1 
 item2 
 
 Item #2 data: 
 item2abc 
 
 All item data: 
 item1abc 
 item2abc 

фигура 2

Как видите, это очень похоже на пример minidom Одно из основных отличий заключается в том, что attrib - это просто объект словаря, что делает его немного более совместимым с другим кодом Python. Нам также не нужно использовать value для доступа к значению атрибута элемента, как мы это делали раньше.

Возможно, вы заметили, что доступ к объектам и атрибутам с помощью ElementTree немного больше похож на Pythonic, как мы упоминали ранее. Это связано с тем, что данные XML анализируются как простые списки и словари, в отличие от minidom где элементы анализируются как пользовательские xml.dom.minidom.Attr и «узлы DOM Text».

Подсчет элементов XML-документа

Использование минидома

Как и в предыдущем случае, minidom нужно импортировать из модуля dom Этот модуль предоставляет функцию getElementsByTagName , которую мы будем использовать для поиска элемента тега. После получения мы используем len() для получения количества подэлементов, подключенных к узлу. Результат, полученный из приведенного ниже кода, показан на рисунке 3 .

 from xml.dom import minidom 
 
 # parse an xml file by name 
 mydoc = minidom.parse('items.xml') 
 
 items = mydoc.getElementsByTagName('item') 
 
 # total amount of items 
 print(len(items)) 

 $ python counterxmldom.py 
 2 

Рисунок 3

Имейте в виду, что при этом будет подсчитано только количество дочерних элементов под примечанием, для которого вы выполняете len() , который в данном случае является корневым узлом. Если вы хотите найти все подэлементы в гораздо большем дереве, вам нужно будет пройти по всем элементам и подсчитать каждого из их дочерних элементов.

Использование ElementTree

Точно так же ElementTree позволяет нам рассчитать количество узлов, подключенных к узлу.

Пример кода:

 import xml.etree.ElementTree as ET 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # total amount of items 
 print(len(root[0])) 

Результат такой:

 $ python counterxml.py 
 2 

Рисунок 4

Написание XML-документов

Использование ElementTree

ElementTree также отлично подходит для записи данных в файлы XML. В приведенном ниже коде показано, как создать XML-файл с той же структурой, что и файл, который мы использовали в предыдущих примерах.

Шаги следующие:

  1. Создайте элемент, который будет работать как наш корневой элемент. В нашем случае тег для этого элемента - «данные».
  2. Когда у нас есть корневой элемент, мы можем создавать подэлементы с помощью функции SubElement Эта функция имеет синтаксис:

SubElement(parent, tag, attrib={}, **extra)

Здесь parent - это родительский узел, к attrib нужно подключиться, attrib - словарь, содержащий атрибуты элемента, а extra - дополнительные аргументы ключевого слова. Эта функция возвращает нам элемент, который можно использовать для присоединения других подэлементов, как мы это делаем в следующих строках, передавая элементы в конструктор SubElement
3. Хотя мы можем добавлять наши атрибуты с SubElement функции SubElement, мы также можем использовать set() , как мы это делаем в следующем коде. Текст элемента создается с помощью свойства text Element .
4. В последних 3 строках приведенного ниже кода мы создаем строку из XML-дерева и записываем эти данные в файл, который мы открываем.

Пример кода:

 import xml.etree.ElementTree as ET 
 
 # create the file structure 
 data = ET.Element('data') 
 items = ET.SubElement(data, 'items') 
 item1 = ET.SubElement(items, 'item') 
 item2 = ET.SubElement(items, 'item') 
 item1.set('name','item1') 
 item2.set('name','item2') 
 item1.text = 'item1abc' 
 item2.text = 'item2abc' 
 
 # create a new XML file with the results 
 mydata = ET.tostring(data) 
 myfile = open("items2.xml", "w") 
 myfile.write(mydata) 

Выполнение этого кода приведет к созданию нового файла «items2.xml», который должен быть эквивалентен исходному файлу «items.xml», по крайней мере, с точки зрения структуры данных XML. Вы, вероятно, заметите, что результирующая строка представляет собой только одну строку и не содержит отступов.

Поиск элементов XML

Использование ElementTree

Модуль ElementTree предлагает findall() , которая помогает нам находить определенные элементы в дереве. Он возвращает все элементы с указанным условием. Кроме того, в модуле есть функция find() , которая возвращает только первый подэлемент, соответствующий указанным критериям. Синтаксис обеих этих функций следующий:

 findall(match, namespaces=None) 

 find(match, namespaces=None) 

Для обеих этих функций match параметр может быть имя тега XML или путь. Функция findall() возвращает список элементов, а find возвращает единственный объект типа Element .

Кроме того, есть еще одна вспомогательная функция, которая возвращает текст первого узла, который соответствует заданному критерию:

 findtext(match, default=None, namespaces=None) 

Вот пример кода, чтобы показать вам, как именно работают эти функции:

 import xml.etree.ElementTree as ET 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # find the first 'item' object 
 for elem in root: 
 print(elem.find('item').get('name')) 
 
 # find all "item" objects and print their "name" attribute 
 for elem in root: 
 for subelem in elem.findall('item'): 
 
 # if we don't need to know the name of the attribute(s), get the dict 
 print(subelem.attrib) 
 
 # if we know the name of the attribute, access it directly 
 print(subelem.get('name')) 

И вот результат выполнения этого кода:

 $ python findtree.py 
 item1 
 {'name': 'item1'} 
 item1 
 {'name': 'item2'} 
 item2 

Рисунок 5.

Изменение элементов XML

Использование ElementTree

Модуль ElementTree представляет несколько инструментов для изменения существующих XML-документов. В приведенном ниже примере показано, как изменить имя узла, изменить имя атрибута и изменить его значение, а также как добавить дополнительный атрибут к элементу.

Текст узла можно изменить, указав новое значение в текстовом поле объекта узла. Имя атрибута можно переопределить с помощью функции set(name, value) . Функция set не должна просто работать с существующим атрибутом, ее также можно использовать для определения нового атрибута.

В приведенном ниже коде показано, как выполнять эти операции:

 import xml.etree.ElementTree as ET 
 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # changing a field text 
 for elem in root.iter('item'): 
 elem.text = 'new text' 
 
 # modifying an attribute 
 for elem in root.iter('item'): 
 elem.set('name', 'newitem') 
 
 # adding an attribute 
 for elem in root.iter('item'): 
 elem.set('name2', 'newitem2') 
 
 tree.write('newitems.xml') 

После выполнения кода результирующий XML-файл newitems.xml будет иметь XML-дерево со следующими данными:

 <data> 
 <items> 
 <item name="newitem" name2="newitem2">new text</item> 
 <item name="newitem" name2="newitem2">new text</item> 
 </items> 
 </data> 

Как мы можем видеть при сравнении с исходным XML-файлом, имена элементов item изменились на «newitem», текст на «новый текст», а атрибут «name2» был добавлен к обоим узлам.

Вы также можете заметить, что запись XML-данных таким способом (вызов tree.write с именем файла) добавляет к XML-дереву дополнительное форматирование, поэтому оно содержит символы новой строки и отступы.

Создание подэлементов XML

Использование ElementTree

У ElementTree есть несколько способов добавить новый элемент. Первый способ, который мы рассмотрим, - это использовать makeelement() , которая имеет имя узла и словарь с его атрибутами в качестве параметров.

Второй способ - через SubElement() , который принимает в качестве входных данных родительский элемент и словарь атрибутов.

В нашем примере ниже мы показываем оба метода. В первом случае у узла нет атрибутов, поэтому мы создали пустой словарь ( attrib = {} ). Во втором случае мы используем заполненный словарь для создания атрибутов.

 import xml.etree.ElementTree as ET 
 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # adding an element to the root node 
 attrib = {} 
 element = root.makeelement('seconditems', attrib) 
 root.append(element) 
 
 # adding an element to the seconditem node 
 attrib = {'name2': 'secondname2'} 
 subelement = root[0][1].makeelement('seconditem', attrib) 
 ET.SubElement(root[1], 'seconditem', attrib) 
 root[1][0].text = 'seconditemabc' 
 
 # create a new XML file with the new element 
 tree.write('newitems2.xml') 

После запуска этого кода результирующий XML-файл будет выглядеть так:

 <data> 
 <items> 
 <item name="item1">item1abc</item> 
 <item name="item2">item2abc</item> 
 </items> 
 <seconditems> 
 <seconditem name2="secondname2">seconditemabc</seconditem> 
 </seconditems> 
 </data> 

Как мы видим при сравнении с исходным файлом, были добавлены элемент «seconditems» и его подэлемент «seconditem». Кроме того, узел «seconditem» имеет атрибут «name2», а его текст - «seconditemabc», как и ожидалось.

Удаление элементов XML

Использование ElementTree

Как и следовало ожидать, ElementTree имеет необходимые функции для удаления атрибутов и подэлементов узла.

Удаление атрибута

В приведенном ниже коде показано, как удалить атрибут узла с помощью функции pop() . Функция применяется к параметру объекта attrib Он определяет имя атрибута и устанавливает для него значение « None .

 import xml.etree.ElementTree as ET 
 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # removing an attribute 
 root[0][0].attrib.pop('name', None) 
 
 # create a new XML file with the results 
 tree.write('newitems3.xml') 

Результатом будет следующий XML-файл:

 <data> 
 <items> 
 <item>item1abc</item> 
 <item name="item2">item2abc</item> 
 </items> 
 </data> 

Как видно из XML-кода выше, первый элемент не имеет атрибута «имя».

Удаление одного подэлемента

Один конкретный подэлемент можно удалить с помощью функции remove Эта функция должна указать узел, который мы хотим удалить.

В следующем примере показано, как его использовать:

 import xml.etree.ElementTree as ET 
 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # removing one sub-element 
 root[0].remove(root[0][0]) 
 
 # create a new XML file with the results 
 tree.write('newitems4.xml') 

Результатом будет следующий XML-файл:

 <data> 
 <items> 
 <item name="item2">item2abc</item> 
 </items> 
 </data> 

Как видно из приведенного выше XML-кода, теперь есть только один узел «элемент». Второй был удален из исходного дерева.

Удаление всех подэлементов

Модуль ElementTree представляет нам clear() , которую можно использовать для удаления всех подэлементов данного элемента.

В приведенном ниже примере показано, как использовать clear() :

 import xml.etree.ElementTree as ET 
 
 tree = ET.parse('items.xml') 
 root = tree.getroot() 
 
 # removing all sub-elements of an element 
 root[0].clear() 
 
 # create a new XML file with the results 
 tree.write('newitems5.xml') 

Результатом будет следующий XML-файл:

 <data> 
 <items /> 
 </data> 

Как видно из приведенного выше XML-кода, все подэлементы элемента «items» были удалены из дерева.

Заключение

Python предлагает несколько вариантов обработки файлов XML. В этой статье мы рассмотрели ElementTree и использовали его для анализа, создания, изменения и удаления файлов XML. Мы также использовали minidom для анализа файлов XML. Лично я бы рекомендовал использовать ElementTree как с ним намного проще работать и он является более современным модулем из двух.

comments powered by Disqus