Tworzenie prostego pliku XML za pomocą języka Python


161

Jakie mam opcje, jeśli chcę utworzyć prosty plik XML w Pythonie? (pod względem biblioteki)

Plik XML, który chcę, wygląda następująco:

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

</root>

Odpowiedzi:


310

Obecnie najbardziej popularną (i bardzo prostą) opcją jest API ElementTree , które jest dołączone do standardowej biblioteki od czasu Pythona 2.5.

Dostępne opcje to:

  • ElementTree (podstawowa, czysto Pythonowa implementacja ElementTree. Część biblioteki standardowej od 2.5)
  • cElementTree (zoptymalizowana implementacja ElementTree w C. Od 2.5 dostępna również w bibliotece standardowej)
  • LXML (oparty na libxml2. Oferuje bogaty nadzbiór API ElementTree, a także XPath, selektory CSS i nie tylko)

Oto przykład tego, jak wygenerować przykładowy dokument przy użyciu cElementTree w standardowej bibliotece:

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")

Przetestowałem to i działa, ale zakładam, że białe spacje nie mają znaczenia. Jeśli potrzebujesz wcięcia „prettyprint”, daj mi znać, a sprawdzę, jak to zrobić. (Może to być opcja specyficzna dla LXML. Nie używam zbyt często implementacji standardowej biblioteki)

Aby uzyskać więcej informacji, oto kilka przydatnych linków:

Na koniec, albo cElementTree, albo LXML powinny być wystarczająco szybkie dla wszystkich twoich potrzeb (oba są zoptymalizowanym kodem C), ale jeśli jesteś w sytuacji, w której musisz wycisnąć każdy ostatni kawałek wydajności, testy porównawcze witryna LXML wskazuje, że:

  • LXML wyraźnie wygrywa w serializacji (generowaniu) XML
  • Jako efekt uboczny implementacji prawidłowego przechodzenia przez rodzica, LXML jest nieco wolniejszy niż cElementTree pod względem analizy.

1
@Kasper: Nie mam Maca, więc nie mogę spróbować powtórzyć problemu. Podaj mi wersję Pythona, a zobaczę, czy uda mi się ją odtworzyć w systemie Linux.
ssokolow

4
@nonsensickle Naprawdę powinieneś był zadać nowe pytanie, a następnie wysłać mi link do niego, aby każdy mógł z niego skorzystać. Jednak wskażę ci właściwy kierunek. Biblioteki DOM (Document Object Model) zawsze budują model w pamięci, więc zamiast tego potrzebujesz implementacji SAX (Simple API for XML). Nigdy nie zaglądałem do implementacji SAX, ale tutaj jest samouczek dotyczący używania biblioteki w standardowej bibliotece jako wyjście zamiast wejścia.
ssokolow

1
@YonatanSimson Nie wiem, jak dodać ten dokładny ciąg, ponieważ ElementTree wydaje się być posłuszny tylko xml_declaration=Truewtedy, gdy określisz kodowanie ... ale aby uzyskać równoważne zachowanie, wywołaj w tree.write()ten sposób: tree.write("filename.xml", xml_declaration=True, encoding='utf-8')Możesz użyć dowolnego kodowania, o ile wyraźnie określisz jeden. ( asciiwymusi na wszystkich znakach Unicode spoza 7-bitowego zestawu ASCII kodowanie encji, jeśli nie ufasz prawidłowej konfiguracji serwera WWW).
ssokolow

1
Tylko przypomnieniem dla każdego, kto stara się poprawić vlaue2na value2: the typo jest w wymaganym wyjściu XML w oryginalnym pytanie. Dopóki to się nie zmieni, literówka tutaj jest poprawna.
ssokolow

3
Zgodnie z dokumentacją , cElementTreezostał zdeprecjonowany w Pythonie 3.3
Stevoisiak

63

Biblioteka lxml zawiera bardzo wygodny składni XML generacji, zwany e-fabryka . Oto jak zrobiłbym przykład, który podasz:

#!/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)

Wynik:

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

Obsługuje również dodawanie do już utworzonego węzła, np. Po powyższym można by powiedzieć

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

3
Jeśli nazwa tagu nie jest zgodna z regułami identyfikatora Pythona, możesz użyć getattrnp getattr(E, "some-tag").
haridsv

dla mnie print lxml.etree.tostring powodował błąd AttributeError: obiekt „lxml.etree._Element” nie ma atrybutu „etree”. Działało bez uruchamiania „lxml”. jak: etree.tostring (the_doc, pretty_print = True)
kodlan

19

Yattag http://www.yattag.org/ lub https://github.com/leforestier/yattag udostępnia interesujące API do tworzenia takich dokumentów XML (a także dokumentów HTML).

Używa menedżera kontekstu i withsłowa kluczowego.

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)

więc otrzymasz:

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


4

W przypadku tak prostej struktury XML możesz nie chcieć angażować pełnego modułu XML. Rozważ szablon łańcucha dla najprostszych struktur lub Jinja dla czegoś nieco bardziej złożonego. Jinja może obsłużyć zapętlenie listy danych w celu utworzenia wewnętrznego XML listy dokumentów. Jest to nieco trudniejsze w przypadku szablonów surowych ciągów znaków Pythona

Aby zapoznać się z przykładem Jinja, zobacz moją odpowiedź na podobne pytanie .

Oto przykład generowania pliku XML za pomocą szablonów ciągów.

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

Wynik:

<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>

Wadą podejścia opartego na szablonach jest to, że nie dostaniesz ucieczki <i >za darmo. Tańczyłem wokół tego problemu, ściągając narzędzie zxml.sax


1

Właśnie skończyłem pisać generator XML, używając metody szablonów bigh_29 ... to dobry sposób na kontrolowanie tego, co generujesz, bez zbyt wielu obiektów przeszkadzających.

Jeśli chodzi o tag i wartość, użyłem dwóch tablic, jednej, która podawała nazwę i pozycję znacznika w wyjściowym pliku XML, a drugiej, która odwoływała się do pliku parametrów z tą samą listą tagów. Plik parametrów zawiera jednak również numer pozycji w odpowiednim pliku wejściowym (csv), z którego zostaną pobrane dane. W ten sposób, jeśli nastąpią jakiekolwiek zmiany w pozycji danych przychodzących z pliku wejściowego, program się nie zmieni; dynamicznie ustala pozycję pola danych z odpowiedniego tagu w pliku parametrów.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.