Criando um arquivo XML simples usando python

160

Quais são minhas opções se eu quiser criar um arquivo XML simples em python? (biblioteca)

O xml que eu quero se parece com:

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

</root>
Blankman
fonte

Respostas:

309

Atualmente, a opção mais popular (e muito simples) é a API ElementTree , que foi incluída na biblioteca padrão desde o Python 2.5.

As opções disponíveis para isso são:

  • ElementTree (Implementação básica e pura em Python do ElementTree. Parte da biblioteca padrão desde 2.5)
  • cElementTree (implementação C otimizada do ElementTree. Também oferecido na biblioteca padrão desde 2.5)
  • LXML (Baseado na libxml2. Oferece um superconjunto rico da API ElementTree, bem como XPath, seletores de CSS e mais)

Aqui está um exemplo de como gerar seu documento de exemplo usando o 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")

Eu testei e funciona, mas estou assumindo que o espaço em branco não é significativo. Se você precisar do recuo "prettyprint", informe-me e vou procurar como fazer isso. (Pode ser uma opção específica para LXML. Não uso muito a implementação stdlib)

Para mais leitura, aqui estão alguns links úteis:

Como observação final, cElementTree ou LXML devem ser rápidos o suficiente para todas as suas necessidades (ambos são código C otimizado), mas no caso de você estar em uma situação em que precisa extrair todo o desempenho, os benchmarks em o site LXML indica que:

  • O LXML vence claramente por serializar (gerar) XML
  • Como efeito colateral da implementação da travessia pai adequada, o LXML é um pouco mais lento que o cElementTree para análise.
ssokolow
fonte
1
@ Kasper: Eu não tenho um Mac, então não posso tentar duplicar o problema. Diga-me a versão do Python e vou ver se consigo replicá-la no Linux.
ssokolow
4
@ nonsensickle Você realmente deveria ter feito uma nova pergunta e depois me enviado um link para que todos possam se beneficiar. No entanto, vou apontar na direção certa. As bibliotecas DOM (Document Object Model) sempre constroem um modelo na memória para que você deseje uma implementação SAX (API simples para XML). Eu nunca olhei para implementações SAX, mas aqui está um tutorial para usar o in-stdlib para saída em vez de entrada.
Ssokolow 29/05
1
@YonatanSimson Não sei como adicionar essa sequência exata , pois o ElementTree parece obedecer apenas xml_declaration=Truese você especificar uma codificação ... mas, para obter um comportamento equivalente, chame tree.write()assim: tree.write("filename.xml", xml_declaration=True, encoding='utf-8')Você pode usar qualquer codificação desde que especifique explicitamente 1. ( asciiForçará todos os caracteres Unicode fora do conjunto ASCII de 7 bits para ser codificado-entidade, se você não confiar em um servidor web para ser configurado corretamente.)
ssokolow
1
Apenas um lembrete para qualquer outra pessoa que tenta corrigir vlaue2para value2: O erro tipográfico é na saída XML solicitado na pergunta original. Até que isso mude, o erro de digitação aqui está correto.
ssokolow
3
De acordo com a documentação , cElementTreefoi depreciado em Python 3.3
Stevoisiak 26/10/17
63

A biblioteca lxml inclui uma sintaxe muito conveniente para geração de XML, chamada E-factory . Aqui está como eu daria o exemplo que você dá:

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

Resultado:

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

Ele também suporta a adição a um nó já criado, por exemplo, após o acima, você pode dizer

the_doc.append(FIELD2('another value again', name='hithere'))
rescdsk
fonte
3
Se o nome da tag não estiver em conformidade com as regras do identificador Python, você poderá usar getattr, por exemplo getattr(E, "some-tag"),.
haridsv
para mim, imprimir lxml.etree.tostring estava causando o AttributeError: 'lxml.etree._Element' o objeto não tem atributo 'etree'. Funcionou sem iniciar "lxml". como: etree.tostring (the_doc, pretty_print = True)
kodlan 27/06
19

O Yattag http://www.yattag.org/ ou https://github.com/leforestier/yattag fornece uma API interessante para criar esse documento XML (e também documentos HTML).

Está usando o gerenciador de contexto e a withpalavra - chave.

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)

então você terá:

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

Para uma estrutura XML tão simples, talvez você não queira envolver um módulo XML completo. Considere um modelo de string para as estruturas mais simples ou Jinja para algo um pouco mais complexo. O Jinja pode manipular o loop sobre uma lista de dados para produzir o xml interno da sua lista de documentos. Isso é um pouco mais complicado com modelos de strings python brutos

Para um exemplo de Jinja, veja minha resposta para uma pergunta semelhante .

Aqui está um exemplo de geração de seu xml com modelos de string.

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

Resultado:

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

O infortúnio da abordagem de modelo é que você não vai conseguir escapar de <e >para livre. Eu dancei em torno desse problema, puxando um utilitário dexml.sax

bigh_29
fonte
1

Acabei de escrever um gerador de xml, usando o método de modelos de bigh_29 ... é uma ótima maneira de controlar o que você produz sem que muitos objetos atrapalhem.

Quanto à tag e ao valor, usei duas matrizes, uma que forneceu o nome e a posição da tag no xml de saída e outra que referenciou um arquivo de parâmetro com a mesma lista de tags. O arquivo de parâmetro, no entanto, também possui o número da posição no arquivo de entrada (csv) correspondente de onde os dados serão obtidos. Dessa forma, se houver alguma alteração na posição dos dados provenientes do arquivo de entrada, o programa não será alterado; ele trabalha dinamicamente a posição do campo de dados a partir do tag apropriado no arquivo de parâmetros.

Cloughie
fonte