使用Python创建一个简单的XML文件

223

如果我想要在Python中创建一个简单的XML文件,有哪些库可以使用?

我想要的XML文件的格式如下:

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

</root>
6个回答

405
最流行(也是非常简单)的选项是ElementTree API,它自Python 2.5以来已经包含在标准库中。
可用的选项有:
  • ElementTree(基本的、纯Python实现的ElementTree。自2.5版本起已成为标准库的一部分)
  • cElementTree(ElementTree的优化C实现。自2.5版本以来也被提供在标准库中。自3.3版本起已被弃用并自动合并到常规ElementTree中)
  • LXML(基于libxml2。提供了ElementTree API的丰富超集,还有XPath、CSS选择器等更多功能)
以下是使用标准库中的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实现)
进一步阅读,请参考以下有用的链接:
- Python标准库中实现的API文档 - 入门教程(来自原始作者的网站) - LXML etree教程(附带有从所有主要ElementTree实现中加载最佳选项的示例代码)
最后,cElementTree或LXML都足够快以满足您的所有需求(两者都是优化过的C代码),但是在您需要挤出每一点性能的情况下,LXML网站上的基准测试表明:
- LXML在序列化(generating) XML方面明显优于cElementTree - 由于实现了适当的父级遍历,LXML在解析方面比cElementTree稍慢。

1
@Kasper:我没有Mac,所以无法尝试复制这个问题。告诉我Python的版本,我会看看是否可以在Linux上复制它。 - ssokolow
4
@nonsensickle,你真的应该提出一个新问题并向我发送链接,这样每个人都可以受益。然而,我会指引你朝着正确的方向前进。DOM(文档对象模型)库总是构建内存模型,因此你需要一个SAX(XML简单API)实现。我从未研究过SAX实现,但是这里有一个教程,用于使用内置标准库进行输出而不是输入。 - ssokolow
1
@YonatanSimson 我不知道如何添加那个确切的字符串,因为ElementTree似乎只在指定编码时遵守xml_declaration=True... 但是,要获得等效的行为,请像这样调用tree.write()tree.write("filename.xml", xml_declaration=True, encoding='utf-8')只要您明确指定一个编码,就可以使用任何编码。(如果您不信任Web服务器的配置,则ascii将强制实体编码超出7位ASCII集的所有Unicode字符。) - ssokolow
2
提醒其他试图把 vlaue2 改正为 value2 的人:原问题中请求的 XML 输出中有错别字。在那个改变之前,这里的错别字实际上是正确的。 - ssokolow
5
根据官方文档cElementTree在Python 3.3中已被弃用。 - Stevoisiak
显示剩余6条评论

78

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
对我来说,print lxml.etree.tostring 导致 AttributeError: 'lxml.etree._Element' object has no attribute 'etree'。不加 "lxml." 就可以正常工作,如:etree.tostring(the_doc, pretty_print=True)。 - kodlan

30

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>

4
这是一种非常干净的制作标记的方法。我敢说它比任何内置方式都更符合Pythonic的风格。 - FlippingBinary

5
对于如此简单的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中引入一个实用程序来解决了这个问题。

5

21
这个回答需要包括一个minidom使用示例的翻译:这个答案应该包含一个minidom的使用示例。示例:XML文件内容如下: Harry Potter J.K. Rowling 1997 29.99 Learning XML Erik T. Ray 2003 39.95 使用Python的minidom模块解析XML文件并输出所有标题为英文的书籍:from xml.dom import minidom # 打开XML文档并解析 doc = minidom.parse("books.xml") # 获取所有名为“book”的元素列表 books = doc.getElementsByTagName("book") # 遍历每一本书,并输出标题为英文的书籍信息 for book in books: # 获取书籍标题元素 title = book.getElementsByTagName("title")[0] # 如果书籍标题语言是英语,则输出书籍信息 if title.getAttribute("lang") == "en": print("Title: {}".format(title.firstChild.data)) print("Author: {}".format(book.getElementsByTagName("author")[0].firstChild.data)) print("Year: {}".format(book.getElementsByTagName("year")[0].firstChild.data)) print("Price: ${}".format(book.getElementsByTagName("price")[0].firstChild.data)) print("\n")输出结果:Title: Harry Potter Author: J.K. Rowling Year: 1997 Price: $29.99 Title: Learning XML Author: Erik T. Ray Year: 2003 Price: $39.95 - Stevoisiak

0
我刚完成了一个XML生成器的编写,使用了bigh_29的模板方法... 这是一种很好的控制输出内容而不会有太多对象干扰的方法。
至于标签和值,我使用了两个数组,一个给出标签名称和在输出XML中的位置,另一个引用了一个参数文件,其中包含相同列表的标签。然而,参数文件还包含相应输入(CSV)文件中数据将被取自的位置编号。 这样,如果输入文件中的数据位置发生任何更改,程序不会改变;它会动态地从参数文件中的适当标签中解析出数据字段位置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接