Создание простого XML-файла с использованием Python


161

Каковы мои варианты, если я хочу создать простой файл XML в Python? (библиотека мудрая)

XML, который я хочу, выглядит так:

<root>
 <doc>
     <field1 name="blah">some value1</field1>
     <field2 name="asdfasd">some vlaue2</field2>
 </doc>

</root>

Ответы:


310

В наши дни наиболее популярным (и очень простым) вариантом является API ElementTree , который был включен в стандартную библиотеку начиная с Python 2.5.

Доступные варианты для этого:

  • ElementTree (Базовая реализация ElementTree на чистом Python. Часть стандартной библиотеки начиная с 2.5)
  • cElementTree (Оптимизированная реализация ElementTree на C. Также предлагается в стандартной библиотеке с 2.5)
  • LXML (на основе libxml2. Предлагает богатый расширенный набор API ElementTree, а также XPath, CSS-селекторы и многое другое)

Вот пример того, как сгенерировать ваш пример документа с использованием in-stdlib cElementTree:

import xml.etree.cElementTree as ET

root = ET.Element("root")
doc = ET.SubElement(root, "doc")

ET.SubElement(doc, "field1", name="blah").text = "some value1"
ET.SubElement(doc, "field2", name="asdfasd").text = "some vlaue2"

tree = ET.ElementTree(root)
tree.write("filename.xml")

Я проверил это, и оно работает, но я предполагаю, что пробелы не имеют существенного значения. Если вам нужен отступ "prettyprint", дайте мне знать, и я посмотрю, как это сделать. (Это может быть специфичная для LXML опция. Я не очень часто использую реализацию stdlib)

Для дальнейшего чтения вот несколько полезных ссылок:

В заключение отметим, что cElementTree или LXML должны быть достаточно быстрыми для всех ваших потребностей (оба оптимизированных кода C), но в случае, если вам нужно выжать все до последней черты производительности, тесты производительности сайт LXML указывает, что:

  • LXML явно выигрывает за сериализацию (генерацию) XML
  • Как побочный эффект от реализации правильного родительского обхода, LXML немного медленнее, чем cElementTree для анализа.

1
@Kasper: У меня нет Mac, поэтому я не могу попытаться повторить проблему. Скажите мне версию Python, и я посмотрю, смогу ли я воспроизвести ее в Linux.
ssokolow

4
@nonsensickle Вы действительно должны были задать новый вопрос, а затем отправить мне ссылку на него, чтобы каждый мог извлечь из этого пользу. Тем не менее, я укажу вам в правильном направлении. Библиотеки DOM (Document Object Model) всегда строят модель в памяти, поэтому вы хотите использовать реализацию SAX (Simple API for XML). Я никогда не изучал реализации SAX, но вот руководство по использованию in-stdlib для вывода, а не для ввода.
Ssokolow

1
@YonatanSimson Я не знаю, как добавить эту точную строку, поскольку ElementTree, кажется, подчиняется только xml_declaration=Trueесли вы задаете кодировку ... но, чтобы получить эквивалентное поведение, вызовите tree.write()так: tree.write("filename.xml", xml_declaration=True, encoding='utf-8')Вы можете использовать любую кодировку, если вы явно укажете один. ( asciiпринудительно закодирует все символы Unicode за пределами 7-битного набора ASCII, если вы не доверяете правильной настройке веб-сервера.)
ssokolow

1
Только напоминание кому - либо , кто пытается исправить vlaue2на value2: опечатка в запрашиваемом выводе XML в исходном вопросе. До этого изменения, опечатка здесь на самом деле не является правильной.
Соколов

3
Согласно документации , cElementTreeбыл объявлен
устаревшим

63

Библиотека lxml включает в себя очень удобный синтаксис для генерации XML, называемый E-factory . Вот как я приведу пример, который вы приводите:

#!/usr/bin/python
import lxml.etree
import lxml.builder    

E = lxml.builder.ElementMaker()
ROOT = E.root
DOC = E.doc
FIELD1 = E.field1
FIELD2 = E.field2

the_doc = ROOT(
        DOC(
            FIELD1('some value1', name='blah'),
            FIELD2('some value2', name='asdfasd'),
            )   
        )   

print lxml.etree.tostring(the_doc, pretty_print=True)

Вывод:

<root>
  <doc>
    <field1 name="blah">some value1</field1>
    <field2 name="asdfasd">some value2</field2>
  </doc>
</root>

Он также поддерживает добавление к уже созданному узлу, например, после вышесказанного можно сказать

the_doc.append(FIELD2('another value again', name='hithere'))

3
Если имя тега не соответствует правилам идентификаторов Python, то вы могли бы использовать getattr, например, getattr(E, "some-tag").
haridsv

для меня печать lxml.etree.tostring вызывала AttributeError: у объекта 'lxml.etree._Element' нет атрибута 'etree'. Это работало без запуска "lxml". как: etree.tostring (the_doc, pretty_print = True)
кодлан

19

Yattag http://www.yattag.org/ или https://github.com/leforestier/yattag предоставляет интересный API для создания такого XML-документа (а также HTML-документов).

Он использует менеджер контекста и withключевое слово.

from yattag import Doc, indent

doc, tag, text = Doc().tagtext()

with tag('root'):
    with tag('doc'):
        with tag('field1', name='blah'):
            text('some value1')
        with tag('field2', name='asdfasd'):
            text('some value2')

result = indent(
    doc.getvalue(),
    indentation = ' '*4,
    newline = '\r\n'
)

print(result)

так вы получите:

<root>
    <doc>
        <field1 name="blah">some value1</field1>
        <field2 name="asdfasd">some value2</field2>
    </doc>
</root>

6

Для простейшего выбора я бы выбрал минидом: http://docs.python.org/library/xml.dom.minidom.html . Он встроен в стандартную библиотеку Python и прост в использовании в простых случаях.

Вот довольно простой учебник: http://www.boddie.org.uk/python/XML_intro.html


6
Этот ответ должен включать пример использования минидома.
Stevoisiak

4

Для такой простой XML-структуры вы можете не использовать полноценный XML-модуль. Рассмотрим строковый шаблон для простейших структур или Jinja для чего-то более сложного. Jinja может обрабатывать циклы по списку данных для создания внутреннего XML-списка вашего документа. Это немного сложнее с необработанными шаблонами строк Python

Для примера Jinja, см. Мой ответ на аналогичный вопрос .

Вот пример создания вашего xml с помощью строковых шаблонов.

import string
from xml.sax.saxutils import escape

inner_template = string.Template('    <field${id} name="${name}">${value}</field${id}>')

outer_template = string.Template("""<root>
 <doc>
${document_list}
 </doc>
</root>
 """)

data = [
    (1, 'foo', 'The value for the foo document'),
    (2, 'bar', 'The <value> for the <bar> document'),
]

inner_contents = [inner_template.substitute(id=id, name=name, value=escape(value)) for (id, name, value) in data]
result = outer_template.substitute(document_list='\n'.join(inner_contents))
print result

Вывод:

<root>
 <doc>
    <field1 name="foo">The value for the foo document</field1>
    <field2 name="bar">The &lt;value&gt; for the &lt;bar&gt; document</field2>
 </doc>
</root>

Недостатком шаблонного подхода является то, что вы не сможете избежать <и >бесплатно. Я танцевал вокруг этой проблемы, вытаскивая утилиту изxml.sax


1

Я только что закончил писать генератор xml, используя метод шаблонов bigh_29 ... это хороший способ контролировать то, что вы выводите без слишком большого количества объектов, попадающих «в пути».

Что касается тега и значения, я использовал два массива, один из которых давал имя и позицию тега в выходном xml, а другой - ссылку на файл параметров, содержащий тот же список тегов. Файл параметров, однако, также имеет номер позиции в соответствующем файле ввода (CSV), откуда данные будут взяты. Таким образом, если есть какие-либо изменения в положении данных, поступающих из входного файла, программа не изменится; он динамически определяет положение поля данных из соответствующего тега в файле параметров.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.