用Python打印XML非常漂亮

用Python打印XML的最佳方法(或各种方法)是什么?

499843 次浏览

XML漂亮的python打印看起来很适合这个任务。(名字也很恰当。)

另一种方法是使用pyXML,它有PrettyPrint函数

LXML是最近更新的,包含一个漂亮的打印函数

import lxml.etree as etree


x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
查看lxml教程: http://lxml.de/tutorial.html < / p >

如果你使用的是DOM实现,每一个都有自己的内置漂亮打印形式:

# minidom
#
document.toprettyxml()


# 4DOM
#
xml.dom.ext.PrettyPrint(document, stream)


# pxdom (or other DOM Level 3 LS-compliant imp)
#
serializer.domConfig.setParameter('format-pretty-print', True)
serializer.writeToString(document)

如果您正在使用其他没有自己的漂亮打印机的东西——或者那些漂亮打印机不能完全按照您想要的方式来做——您可能必须编写或子类化您自己的序列化器。

我看不懂迷你dom的漂亮印花。每当我尝试用给定编码之外的字符漂亮地打印文档时,我都会得到一个UnicodeError,例如,如果我在文档中有一个β,并且我尝试doc.toprettyxml(encoding='latin-1')。以下是我的解决方法:

def toprettyxml(doc, encoding):
"""Return a pretty-printed XML document in a given encoding."""
unistr = doc.toprettyxml().replace(u'<?xml version="1.0" ?>',
u'<?xml version="1.0" encoding="%s"?>' % encoding)
return unistr.encode(encoding, 'xmlcharrefreplace')
import xml.dom.minidom


dom = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = dom.toprettyxml()

下面是我解决难看的文本节点问题的解决方案。

uglyXml = doc.toprettyxml(indent='  ')


text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
prettyXml = text_re.sub('>\g<1></', uglyXml)


print prettyXml

上面的代码将产生:

<?xml version="1.0" ?>
<issues>
<issue>
<id>1</id>
<title>Add Visual Studio 2005 and 2008 solution files</title>
<details>We need Visual Studio 2005/2008 project files for Windows.</details>
</issue>
</issues>

而不是这样:

<?xml version="1.0" ?>
<issues>
<issue>
<id>
1
</id>
<title>
Add Visual Studio 2005 and 2008 solution files
</title>
<details>
We need Visual Studio 2005/2008 project files for Windows.
</details>
</issue>
</issues>

可能有一些限制。

另一个解决方案是借用这个indent函数,用于自2.5起内置到Python中的ElementTree库。 下面是它的样子:

from xml.etree import ElementTree


def indent(elem, level=0):
i = "\n" + level*"  "
j = "\n" + (level-1)*"  "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + "  "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for subelem in elem:
indent(subelem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = j
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = j
return elem


root = ElementTree.parse('/tmp/xmlfile').getroot()
indent(root)
ElementTree.dump(root)

正如其他人指出的那样,lxml内置了一个漂亮的打印机。

请注意,在默认情况下,它会将CDATA部分更改为普通文本,这可能会产生糟糕的结果。

下面是一个Python函数,它保留输入文件,只改变缩进(注意strip_cdata=False)。此外,它确保输出使用UTF-8作为编码,而不是默认的ASCII(注意encoding='utf-8'):

from lxml import etree


def prettyPrintXml(xmlFilePathToPrettyPrint):
assert xmlFilePathToPrettyPrint is not None
parser = etree.XMLParser(resolve_entities=False, strip_cdata=False)
document = etree.parse(xmlFilePathToPrettyPrint, parser)
document.write(xmlFilePathToPrettyPrint, pretty_print=True, encoding='utf-8')

使用示例:

prettyPrintXml('some_folder/some_file.xml')

如果你有xmllint,你可以生成一个子进程并使用它。xmllint --format <file>将其输入的XML漂亮地打印到标准输出。

注意,这个方法使用了python之外的程序,这使得它有点像黑客。

def pretty_print_xml(xml):
proc = subprocess.Popen(
['xmllint', '--format', '/dev/stdin'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
)
(output, error_output) = proc.communicate(xml);
return output


print(pretty_print_xml(data))

我试图编辑上面“ade”的回答,但在我最初匿名提供反馈后,Stack Overflow不让我编辑。这是一个bug较少的函数版本,用于漂亮地打印一个ElementTree。

def indent(elem, level=0, more_sibs=False):
i = "\n"
if level:
i += (level-1) * '  '
num_kids = len(elem)
if num_kids:
if not elem.text or not elem.text.strip():
elem.text = i + "  "
if level:
elem.text += '  '
count = 0
for kid in elem:
indent(kid, level+1, count < num_kids - 1)
count += 1
if not elem.tail or not elem.tail.strip():
elem.tail = i
if more_sibs:
elem.tail += '  '
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
if more_sibs:
elem.tail += '  '

我用几行代码解决了这个问题,打开文件,通过它并添加缩进,然后再次保存它。我使用的是小的xml文件,不想添加依赖项,也不想为用户安装更多的库。总之,这是我最后得出的结论:

    f = open(file_name,'r')
xml = f.read()
f.close()


#Removing old indendations
raw_xml = ''
for line in xml:
raw_xml += line


xml = raw_xml


new_xml = ''
indent = '    '
deepness = 0


for i in range((len(xml))):


new_xml += xml[i]
if(i<len(xml)-3):


simpleSplit = xml[i:(i+2)] == '><'
advancSplit = xml[i:(i+3)] == '></'
end = xml[i:(i+2)] == '/>'
start = xml[i] == '<'


if(advancSplit):
deepness += -1
new_xml += '\n' + indent*deepness
simpleSplit = False
deepness += -1
if(simpleSplit):
new_xml += '\n' + indent*deepness
if(start):
deepness += 1
if(end):
deepness += -1


f = open(file_name,'w')
f.write(new_xml)
f.close()

这对我来说很有用,也许有人会用到它:)

from yattag import indent


pretty_string = indent(ugly_string)

它不会在文本节点中添加空格或换行,除非你要求它:

indent(mystring, indent_text = True)

您可以指定缩进单位和换行符的样式。

pretty_xml_string = indent(
ugly_xml_string,
indentation = '    ',
newline = '\r\n'
)

该文档在http://www.yattag.org主页上。

我遇到了这个问题,我是这样解决的:

def write_xml_file (self, file, xml_root_element, xml_declaration=False, pretty_print=False, encoding='unicode', indent='\t'):
pretty_printed_xml = etree.tostring(xml_root_element, xml_declaration=xml_declaration, pretty_print=pretty_print, encoding=encoding)
if pretty_print: pretty_printed_xml = pretty_printed_xml.replace('  ', indent)
file.write(pretty_printed_xml)

在我的代码中,这个方法是这样调用的:

try:
with open(file_path, 'w') as file:
file.write('<?xml version="1.0" encoding="utf-8" ?>')


# create some xml content using etree ...


xml_parser = XMLParser()
xml_parser.write_xml_file(file, xml_root, xml_declaration=False, pretty_print=True, encoding='unicode', indent='\t')


except IOError:
print("Error while writing in log file!")

这只是因为etree默认使用two spaces来缩进,我发现这不是很强调缩进,因此不漂亮。我找不到任何树的设置或任何函数的参数来改变标准树缩进。我喜欢使用etree的简单性,但这真的让我很恼火。

我编写了一个解决方案来遍历现有的ElementTree,并使用text/tail将其缩进。

def prettify(element, indent='  '):
queue = [(0, element)]  # (level, element)
while queue:
level, element = queue.pop(0)
children = [(level + 1, child) for child in list(element)]
if children:
element.text = '\n' + indent * (level+1)  # for child open
if queue:
element.tail = '\n' + indent * queue[0][0]  # for sibling open
else:
element.tail = '\n' + indent * (level-1)  # for parent close
queue[0:0] = children  # prepend so children come before siblings

你可以使用流行的外部库xmltodict,使用unparsepretty=True你会得到最好的结果:

xmltodict.unparse(
xmltodict.parse(my_xml), full_document=False, pretty=True)

full_document=False相对于顶部的<?xml version="1.0" encoding="UTF-8"?>

你有几个选择。

xml.etree.ElementTree.indent() .elementtree. indent(

包括电池,使用简单,输出漂亮。

但需要Python 3.9+

import xml.etree.ElementTree as ET


element = ET.XML("<html><body>text</body></html>")
ET.indent(element)
print(ET.tostring(element, encoding='unicode'))

BeautifulSoup.prettify()

BeautifulSoup可能是Python <最简单的解决方案;3.9.

from bs4 import BeautifulSoup


bs = BeautifulSoup(open(xml_file), 'xml')
pretty_xml = bs.prettify()
print(pretty_xml)

输出:

<?xml version="1.0" encoding="utf-8"?>
<issues>
<issue>
<id>
1
</id>
<title>
Add Visual Studio 2005 and 2008 solution files
</title>
</issue>
</issues>

这是我要回答的。默认实参按原样工作。但是文本内容在单独的行上展开,就好像它们是嵌套的元素一样。

lxml.etree.parse() .parse(

更漂亮的输出,但是带有参数。

from lxml import etree


x = etree.parse(FILE_NAME)
pretty_xml = etree.tostring(x, pretty_print=True, encoding=str)

生产:

  <issues>
<issue>
<id>1</id>
<title>Add Visual Studio 2005 and 2008 solution files</title>
<details>We need Visual Studio 2005/2008 project files for Windows.</details>
</issue>
</issues>

这对我来说没什么问题。


xml.dom. mindom .parse()

没有外部依赖,只有后处理。

import xml.dom.minidom as md


dom = md.parse(FILE_NAME)
# To parse string instead use: dom = md.parseString(xml_string)
pretty_xml = dom.toprettyxml()
# remove the weird newline issue:
pretty_xml = os.linesep.join([s for s in pretty_xml.splitlines()
if s.strip()])

输出与上面相同,但是代码更多。

看看vkbeautify模块。

它是我非常流行的同名javascript/nodejs插件的python版本。它可以漂亮地打印/缩小XML, JSON和CSS文本。输入和输出可以是字符串/文件的任意组合。它非常紧凑,没有任何依赖关系。

例子:

import vkbeautify as vkb


vkb.xml(text)
vkb.xml(text, 'path/to/dest/file')
vkb.xml('path/to/src/file')
vkb.xml('path/to/src/file', 'path/to/dest/file')

如果你不想重新解析,还有带有get_pprint()函数的xmlpp.py图书馆。它在我的用例中工作得很好,很顺利,不需要重新解析为lxml ElementTree对象。

< p > 用于将整个xml文档转换为漂亮的xml文档 < br > (例如:假设你已经提取了一个LibreOffice Writer .odt或.ods文件,并且你想要将丑陋的“content.xml”文件转换为自动化git版本控制git difftooling .odt/。ods文件的漂亮文件,例如我正在实现在这里)

import xml.dom.minidom


file = open("./content.xml", 'r')
xml_string = file.read()
file.close()


parsed_xml = xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = parsed_xml.toprettyxml()


file = open("./content_new.xml", 'w')
file.write(pretty_xml_as_string)
file.close()
< p >引用:< br > -感谢本·诺兰德在本页给出了答案,它让我在那里的大部分方式
from lxml import etree
import xml.dom.minidom as mmd


xml_root = etree.parse(xml_fiel_path, etree.XMLParser())


def print_xml(xml_root):
plain_xml = etree.tostring(xml_root).decode('utf-8')
urgly_xml = ''.join(plain_xml .split())
good_xml = mmd.parseString(urgly_xml)
print(good_xml.toprettyxml(indent='    ',))

它的工作良好的xml与中国!

你可以试试这种变化……

安装BeautifulSoup和后端lxml(解析器)库:

user$ pip3 install lxml bs4

处理XML文档:

from bs4 import BeautifulSoup


with open('/path/to/file.xml', 'r') as doc:
for line in doc:
print(BeautifulSoup(line, 'lxml-xml').prettify())

下面是一个Python3解决方案,它消除了难看的换行符问题(大量空白),并且与大多数其他实现不同,它只使用标准库。

import xml.etree.ElementTree as ET
import xml.dom.minidom
import os


def pretty_print_xml_given_root(root, output_xml):
"""
Useful for when you are editing xml data on the fly
"""
xml_string = xml.dom.minidom.parseString(ET.tostring(root)).toprettyxml()
xml_string = os.linesep.join([s for s in xml_string.splitlines() if s.strip()]) # remove the weird newline issue
with open(output_xml, "w") as file_out:
file_out.write(xml_string)


def pretty_print_xml_given_file(input_xml, output_xml):
"""
Useful for when you want to reformat an already existing xml file
"""
tree = ET.parse(input_xml)
root = tree.getroot()
pretty_print_xml_given_root(root, output_xml)

我找到了如何修复常见换行问题在这里

如果由于某种原因你无法获得其他用户提到的任何Python模块,我建议使用以下Python 2.7解决方案:

import subprocess


def makePretty(filepath):
cmd = "xmllint --format " + filepath
prettyXML = subprocess.check_output(cmd, shell = True)
with open(filepath, "w") as outfile:
outfile.write(prettyXML)

据我所知,这个解决方案将在安装了xmllint包的基于unix的系统上工作。

从Python 3.9开始,ElementTree有一个indent()函数,用于漂亮地打印XML树。

看到https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.indent

示例用法:

import xml.etree.ElementTree as ET


element = ET.XML("<html><body>text</body></html>")
ET.indent(element)
print(ET.tostring(element, encoding='unicode'))

好处是它不需要任何额外的库。有关更多信息,请查看https://bugs.python.org/issue14465https://github.com/python/cpython/pull/15200

我在寻找如何漂亮地打印html;时发现了这个问题

使用这篇文章中的一些想法,我调整了XML解决方案,以适用于XML或HTML:

from xml.dom.minidom import parseString as string_to_dom


def prettify(string, html=True):
dom = string_to_dom(string)
ugly = dom.toprettyxml(indent="  ")
split = list(filter(lambda x: len(x.strip()), ugly.split('\n')))
if html:
split = split[1:]
pretty = '\n'.join(split)
return pretty


def pretty_print(html):
print(prettify(html))

使用时是这样的:

html = """\
<div class="foo" id="bar"><p>'IDK!'</p><br/><div class='baz'><div>
<span>Hi</span></div></div><p id='blarg'>Try for 2</p>
<div class='baz'>Oh No!</div></div>
"""


pretty_print(html)

返回:

<div class="foo" id="bar">
<p>'IDK!'</p>
<br/>
<div class="baz">
<div>
<span>Hi</span>
</div>
</div>
<p id="blarg">Try for 2</p>
<div class="baz">Oh No!</div>
</div>

使用etree.indentetree.tostring

import lxml.etree as etree


root = etree.fromstring('<html><head></head><body><h1>Welcome</h1></body></html>')
etree.indent(root, space="  ")
xml_string = etree.tostring(root, pretty_print=True).decode()
print(xml_string)

输出

<html>
<head/>
<body>
<h1>Welcome</h1>
</body>
</html>

删除名称空间和前缀

import lxml.etree as etree




def dump_xml(element):
for item in element.getiterator():
item.tag = etree.QName(item).localname


etree.cleanup_namespaces(element)
etree.indent(element, space="  ")
result = etree.tostring(element, pretty_print=True).decode()
return result




root = etree.fromstring('<cs:document xmlns:cs="http://blabla.com"><name>hello world</name></cs:document>')
xml_string = dump_xml(root)
print(xml_string)

输出

<document>
<name>hello world</name>
</document>

我发现了一个快速简单的方法来格式化和打印一个xml文件:

import xml.etree.ElementTree as ET


xmlTree = ET.parse('your XML file')
xmlRoot = xmlTree.getroot()
xmlDoc =  ET.tostring(xmlRoot, encoding="unicode")


print(xmlDoc)

Outuput:

<root>
<child>
<subchild>.....</subchild>
</child>
<child>
<subchild>.....</subchild>
</child>
...
...
...
<child>
<subchild>.....</subchild>
</child>
</root>