簡體   English   中英

如何讓 Python 的 ElementTree 漂亮地打印到 XML 文件?

[英]How do I get Python's ElementTree to pretty print to an XML file?

背景

我正在使用 SQLite 來訪問數據庫並檢索所需的信息。 我在 Python 2.6 版中使用 ElementTree 來創建包含該信息的 XML 文件。

代碼

import sqlite3
import xml.etree.ElementTree as ET

# NOTE: Omitted code where I acccess the database,
# pull data, and add elements to the tree

tree = ET.ElementTree(root)

# Pretty printing to Python shell for testing purposes
from xml.dom import minidom
print minidom.parseString(ET.tostring(root)).toprettyxml(indent = "   ")

#######  Here lies my problem  #######
tree.write("New_Database.xml")

嘗試

我嘗試使用tree.write("New_Database.xml", "utf-8")代替上面的最后一行代碼,但它根本沒有編輯 XML 的布局 - 它仍然是一團糟。

我還決定擺弄並嘗試做:
tree = minidom.parseString(ET.tostring(root)).toprettyxml(indent = " ")
而不是將其打印到 Python shell,這會給出錯誤AttributeError: 'unicode' object has no attribute 'write'

問題

When I write my tree to an XML file on the last line, is there a way to pretty print to the XML file as it does to the Python shell?

我可以在這里使用toprettyxml()還是有其他方法可以做到這一點?

無論您的 XML 字符串是什么,您都可以通過打開一個文件來將其寫入您選擇的文件,然后將其寫入文件。

from xml.dom import minidom

xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ")
with open("New_Database.xml", "w") as f:
    f.write(xmlstr)

有一種可能的復雜情況,尤其是在 Python 2 中,它對字符串中的 Unicode 字符既不嚴格也不復雜。 如果您的toprettyxml方法返回一個 Unicode 字符串( u"something" ),那么您可能希望將其轉換為合適的文件編碼,例如 UTF-8。 例如,將一個寫入行替換為:

f.write(xmlstr.encode('utf-8'))

我只是用indent()函數解決了它:

xml.etree.ElementTree.indent(tree, space=" ", level=0)將空格附加到子樹以直觀地縮進樹。 這可用於生成打印精美的 XML 輸出。 樹可以是ElementElementTree space是將為每個縮進級別插入的空白字符串,默認情況下是兩個空格字符。 要在已經縮進的樹內縮進部分子樹,請將初始縮進級別作為level傳遞。

tree = ET.ElementTree(root)
ET.indent(tree, space="\t", level=0)
tree.write(file_name, encoding="utf-8")

請注意, indent()函數是在 Python 3.9 中添加的。

我找到了一種使用直接 ElementTree 的方法,但它相當復雜。

ElementTree 具有編輯元素文本和尾部的功能,例如element.text="text"element.tail="tail" 你必須以特定的方式使用這些來讓事情排成一行,所以要確保你知道你的轉義字符。

作為一個基本示例:

我有以下文件:

<?xml version='1.0' encoding='utf-8'?>
<root>
    <data version="1">
        <data>76939</data>
    </data>
    <data version="2">
        <data>266720</data>
        <newdata>3569</newdata>
    </data>
</root>

要放置第三個元素並使其保持美觀,您需要以下代碼:

addElement = ET.Element("data")             # Make a new element
addElement.set("version", "3")              # Set the element's attribute
addElement.tail = "\n"                      # Edit the element's tail
addElement.text = "\n\t\t"                  # Edit the element's text
newData = ET.SubElement(addElement, "data") # Make a subelement and attach it to our element
newData.tail = "\n\t"                       # Edit the subelement's tail
newData.text = "5431"                       # Edit the subelement's text
root[-1].tail = "\n\t"                      # Edit the previous element's tail, so that our new element is properly placed
root.append(addElement)                     # Add the element to the tree.

要縮進內部標簽(如內部數據標簽),您必須將其添加到父元素的文本中。 如果你想在一個元素之后縮進任何東西(通常是在子元素之后),你把它放在尾部。

當您將其寫入文件時,此代碼會給出以下結果:

<?xml version='1.0' encoding='utf-8'?>
<root>
    <data version="1">
        <data>76939</data>
    </data>
    <data version="2">
        <data>266720</data>
        <newdata>3569</newdata>
    </data> <!--root[-1].tail-->
    <data version="3"> <!--addElement's text-->
        <data>5431</data> <!--newData's tail-->
    </data> <!--addElement's tail-->
</root>

另請注意,如果您希望程序統一使用\t ,您可能需要先將文件解析為字符串,然后將所有縮進空格替換為\t

此代碼是在 Python3.7 中編寫的,但在 Python2.7 中仍然有效。

安裝bs4

pip install bs4

使用此代碼進行漂亮的打印:

from bs4 import BeautifulSoup

x = your xml

print(BeautifulSoup(x, "xml").prettify())

如果要使用 lxml,可以通過以下方式完成:

from lxml import etree

xml_object = etree.tostring(root,
                            pretty_print=True,
                            xml_declaration=True,
                            encoding='UTF-8')

with open("xmlfile.xml", "wb") as writter:
    writter.write(xml_object)`

如果您看到 xml 命名空間,例如py:pytype="TREE" ,可能需要在創建xml_object之前添加

etree.cleanup_namespaces(root) 

這對於您的代碼中的任何調整都應該足夠了。

將本安德森的答案視為一個函數。

def _pretty_print(current, parent=None, index=-1, depth=0):
    for i, node in enumerate(current):
        _pretty_print(node, current, i, depth + 1)
    if parent is not None:
        if index == 0:
            parent.text = '\n' + ('\t' * depth)
        else:
            parent[index - 1].tail = '\n' + ('\t' * depth)
        if index == len(parent) - 1:
            current.tail = '\n' + ('\t' * (depth - 1))

所以在不漂亮的數據上運行測試:

import xml.etree.ElementTree as ET
root = ET.fromstring('''<?xml version='1.0' encoding='utf-8'?>
<root>
    <data version="1"><data>76939</data>
</data><data version="2">
        <data>266720</data><newdata>3569</newdata>
    </data> <!--root[-1].tail-->
    <data version="3"> <!--addElement's text-->
<data>5431</data> <!--newData's tail-->
    </data> <!--addElement's tail-->
</root>
''')
_pretty_print(root)

tree = ET.ElementTree(root)
tree.write("pretty.xml")
with open("pretty.xml", 'r') as f:
    print(f.read())

我們得到:

<root>
    <data version="1">
        <data>76939</data>
    </data>
    <data version="2">
        <data>266720</data>
        <newdata>3569</newdata>
    </data>
    <data version="3">
        <data>5431</data>
    </data>
</root>

看看vkbeautify模塊。

輸入和輸出可以是任何組合的字符串/文件。 它非常緊湊,沒有任何依賴性。

import vkbeautify as vkb

a) pretty_text = vkb.xml(your_xml_text)  #return String   

b) vkb.xml(your_xml_text, 'path/to/dest/file') #save in file 

一個 liner(*) 從名為fname的文件中讀取、解析(一次)和漂亮地打印 XML:

from xml.dom import minidom
print(minidom.parseString(open(fname).read()).toprettyxml(indent="  "))

(* 不包括進口)

使用純 ElementTree 和 Python 3.9+:

def prettyPrint(element):
    encoding = 'UTF-8'
    # Create a copy of the input element: Convert to string, then parse again
    copy = ET.fromstring(ET.tostring(element))
    # Format copy. This needs Python 3.9+
    ET.indent(copy, space="    ", level=0)
    # tostring() returns a binary, so we need to decode it to get a string
    return ET.tostring(copy, encoding=encoding).decode(encoding)

如果您需要一個文件,請將最后一行替換為copy.write(...)以避免額外的開銷。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM