파이썬에서 XML 인쇄하기


답변:


379
import xml.dom.minidom

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

35
이것은 당신에게 꽤 XML을 줄 것이지만, 텍스트 노드에서 나오는 것은 실제로 온 것과 다릅니다-텍스트 노드에는 새로운 공백이 있습니다. 피드에 정확히 무엇이 들어 갔는지 예상하면 문제가 발생할 수 있습니다.
토드 홉 킨슨

49
@icnivad : 그 사실을 지적하는 것이 중요하지만, 공간이 그들에게 중요하다면 누군가 XML을 확인하고 싶어하는 것이 이상하게 보입니다!
vaab

18
좋은! 이것을 하나의 라이너로 축소 할 수 있습니다. python -c 'import sys; import xml.dom.minidom; s = sys.stdin.read (); print xml.dom.minidom.parseString (s) .toprettyxml ()'
Anton I. Sipos

11
minidom은 꽤 나쁜 XML 구현으로 널리 퍼져 있습니다. 외부 의존성을 추가 할 수 있다면 lxml이 훨씬 우수합니다.
bukzor

26
XML을 모듈에서 출력 객체로 재정의하는 팬은 아니지만 방법이 작동합니다. 핵심 etree에서 예쁜 인쇄로가는 더 좋은 방법을 찾고 싶습니다. lxml은 멋지지만 가능한 경우 핵심을 유지하고 싶을 때가 있습니다.
Danny Staple

162

lxml은 최근에 업데이트되었으며, 예쁜 인쇄 기능을 포함합니다

import lxml.etree as etree

x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)

lxml 튜토리얼을 확인하십시오 : http://lxml.de/tutorial.html


11
lxml의 단점은 외부 라이브러리에 대한 종속성입니다. 이것은 Windows에서 라이브러리가 모듈과 함께 패키지되어 있기 때문에 그렇게 나쁘지 않다고 생각합니다. 리눅스에서 그들은 aptitude install멀리 있습니다. OS / X에서 확실하지 않습니다.
intuited

4
OS X에서는 작동하는 gcc와 easy_install / pip 만 있으면됩니다.
pkoch

11
lxml pretty 프린터는 신뢰할 수 없으며 lxml FAQ에 설명 된 많은 경우에 XML을 올바르게 인쇄하지 않습니다 . 작동하지 않는 몇 가지 코너 사례 후에 lxml 사용을 중단했습니다 (예 : 버그 # 910018 ). 이 모든 문제는 보존해야하는 공백이 포함 된 XML 값의 사용과 관련이 있습니다.
vaab

1
lxml은 MacPorts의 일부이며 원활하게 작동합니다.
젠스

14
파이썬 3에서는 일반적으로 str (= 파이썬 2의 유니 코드 문자열)로 작업하고 싶기 때문에 이것을 더 잘 사용하십시오 : print(etree.tostring(x, pretty_print=True, encoding="unicode")). 출력 파일에 쓰기는 단 한 줄로 가능하며 중간 변수가 필요하지 않습니다.etree.parse("filename").write("outputfile", encoding="utf-8")
Thor

109

또 다른 해결책은 2.5 이후 파이썬에 내장 된 ElementTree 라이브러리와 함께 사용하기 위해이 indent함수 를 빌리는 것 입니다. 그 모습은 다음과 같습니다.

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 tostring을 사용하십시오!
Stefano

2
여전히 ElementTree 인스턴스 인 tree.write([filename])파일에 쓰기 작업 을 수행 할 수 있습니다 tree.
Bouke

16
이 링크 effbot.org/zone/element-lib.htm#prettyprint 에는 올바른 코드가 있습니다. 여기 코드에 문제가 있습니다. 편집해야합니다.
Aylwyn Lake

elementtree.getroot ()에는 해당 메소드가 없으므로 elementtree 오브젝트에만 있습니다. @bouke
shinzou

1
파일에 쓰는 방법은 다음과 같습니다.tree = ElementTree.parse('file) ; root = tree.getroot() ; indent(root); tree.write('Out.xml');
e-malito

47

추악한 텍스트 노드 문제를 해결하는 내 (해키?) 솔루션이 있습니다.

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>

면책 조항 : 아마도 몇 가지 제한 사항이 있습니다.


감사합니다! 이것은 모든 예쁜 인쇄 방법에 대한 나의 그립이었습니다. 내가 시도한 몇 가지 파일과 잘 작동합니다.
iano

나는 거의 '거의 동일한'솔루션을 찾았지만, 운영 re.compile전에 사용하여 더 직접적입니다 sub( re.findall()두 번 사용 zip하고 for루프가 str.replace()...)
heltonbiker

3
Python 2.7에서는 더 이상 필요하지 않습니다. xml.dom.minidom의 toprettyxml ()은 이제 정확히 하나의 텍스트 자식 노드가있는 노드에 대해 '<id> 1 </ id>'와 같은 출력을 생성합니다.
Marius Gedminas

Python 2.6을 사용해야합니다. 따라서이 정규식 형식 재 지정 트릭은 매우 유용합니다. 문제없이 그대로 작동합니다.
Mike Finch

@Marius Gedminas 2.7.2를 실행 중이며 "default"는 분명히 말한 것과 다릅니다.
posfan12

23

다른 사람들이 지적했듯이 lxml에는 예쁜 프린터가 내장되어 있습니다.

기본적으로 CDATA 섹션을 일반 텍스트로 변경하므로 결과가 불쾌 할 수 있습니다.

다음은 입력 파일을 유지하고 들여 쓰기 만 변경하는 Python 함수입니다 ( strip_cdata=False). 또한 출력에서 ​​기본 ASCII 대신 UTF-8을 인코딩으로 사용하는지 확인하십시오 ( 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')

1
조금 늦었 어 그러나 lxml이 CDATA를 수정했다고 생각합니까? CDATA는 CDATA입니다.
elwc

감사합니다. 지금까지 가장 좋은 답변입니다.
George Chalhoub 2016 년

20

BeautifulSoup에는 사용하기 쉬운 prettify()방법이 있습니다.

들여 쓰기 레벨 당 하나의 공간을 들여 쓰기합니다. lxml의 pretty_print보다 훨씬 잘 작동하며 짧고 달콤합니다.

from bs4 import BeautifulSoup

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

1
이 오류 메시지가 나타납니다 :bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: xml. Do you need to install a parser library?
hadoop

12

당신이 경우 xmllint당신은 하위 프로세스를 생성하고 사용할 수 있습니다.xmllint --format <file>입력 XML을 표준 출력으로 출력합니다.

이 방법은 파이썬 외부의 프로그램을 사용하므로 일종의 해킹이됩니다.

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

11

위의 "ade"답변을 편집하려고 시도했지만 처음에 익명으로 피드백을 제공 한 후에는 Stack Overflow에서 편집 할 수 없었습니다. 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 += '  '

8

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)

자신 만의 pretty-printer없이 다른 것을 사용하고 있거나, 그런 pretty-printer가 원하는 방식으로 그것을하지 않는다면, 아마도 여러분 자신의 serialiser를 작성하거나 서브 클래 싱해야 할 것입니다.


6

minidom의 예쁜 글씨에 문제가있었습니다. 주어진 인코딩 외부의 문자로 문서를 예쁘게 인쇄하려고 할 때마다 유니 코드 오류가 발생합니다 (예 : 문서에 β가 있고 시도한 경우) 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')

5
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 홈페이지에 있습니다.


4

기존 ElementTree를 살펴보고 텍스트 / 꼬리를 사용하여 일반적으로 예상대로 들여 쓰기위한 솔루션을 작성했습니다.

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


3

추악한 줄 바꿈 문제 (수백 개의 공백)를 제거하고 다른 대부분의 구현과 달리 표준 라이브러리 만 사용하는 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)

여기서 일반적인 줄 바꿈 문제를 해결하는 방법을 찾았 습니다 .


2

당신은 인기있는 외부 라이브러리 사용할 수 있습니다 xmltodict를 함께, unparse그리고 pretty=True당신은 최상의 결과를 얻을 것이다 :

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

full_document=False에 대한 <?xml version="1.0" encoding="UTF-8"?>상단.


2

vkbeautify를 살펴 보십시오 모듈을 .

그것은 같은 이름을 가진 매우 인기있는 javascript / nodejs 플러그인의 파이썬 버전입니다. 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') 

이 특정 라이브러리는 추악한 텍스트 노드 문제를 처리합니다.
Cameron Lowell Palmer

1

다른 방법으로 재분석 할 필요가 없다면 xmlpp.py 라이브러리get_pprint()있습니다. lxml ElementTree 객체로 다시 구문 분석하지 않고도 유스 케이스에서 훌륭하고 부드럽게 작동했습니다.


1
minidom과 lxml을 시도했지만 제대로 형식화되고 들여 쓰기 된 xml을 얻지 못했습니다. 이것은 예상대로 작동
david-hoze

1
네임 스페이스 접두사가 붙고 하이픈 (예 : <ns : hyphenated-tag />)이 포함 된 태그 이름이 실패합니다 (예 : <ns : hyphenated-tag />. 하이픈으로 시작하는 부분은 간단히 삭제되어 <ns : hyphenated />)
Endre Both

@EndreBoth 니스 캐치, 테스트하지는 않았지만 xmlpp.py 코드 에서이 문제를 쉽게 해결할 수 있습니까?
gaborous

1

이 변형을 시도해 볼 수 있습니다 ...

설치 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())  

1
'lxml'lxml의 HTML 파서를 사용 합니다 (BS4 문서 참조) . 당신이 필요 'xml'하거나 'lxml-xml'XML 파서합니다.
user2357112는

1
이 댓글은 계속 삭제됩니다. 다시 한 번, StackOverflow를 사용한 사후 변조에 대한 공식적인 불만 사항 (4 개 플래그)을 입력했으며 보안 팀 (액세스 로그 및 버전 기록)이 법 의학적 조사를받을 때까지 중단되지 않습니다. 위의 타임 스탬프는 (연도 별) 잘못된 것이며 내용 일 수도 있습니다.
NYCeyes

1
워드 프로세서에서 다운 투표를 모르는 나를 위해이 가공 한 벌금lxml’s XML parser BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml")
Datanovice

1
@Datanovice 나는 그것이 당신을 도와 기쁘다. :) 의심되는 downvote에 관해서, 누군가가 내 원래의 대답 (정확히 지정되어 있음 lxml-xml)을 무단 변경 한 다음 같은 날 그것을 downvote로 진행했습니다. S / O에 공식 불만을 제출했지만 조사를 거부했습니다. 어쨌든, 나는 이제 내 대답을 "무단 변경"했습니다. 이는 이제 다시 정확합니다 ( lxml-xml원래대로 지정 ). 감사합니다.
NYCeyes

0

나는이 문제가 있었고 다음과 같이 해결했다.

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 들여 쓰기를 변경하는 함수에 대한 etree 또는 매개 변수에 대한 설정을 지정할 수 없습니다. 나는 etree를 사용하는 것이 얼마나 쉬운 지 좋아하지만 이것은 정말 성가신 일이었습니다.


0

전체 xml 문서를 예쁜 xml 문서로 변환하는 경우
(예 : LibreOffice Writer .odt 또는 .ods 파일을 압축 해제 한 후 추악한 "content.xml"파일을 다음과 같이 변환하려는 경우) 자동화 된 자식 버전 제어git difftool.odt / .ods 파일의 ing ( 예 : 여기에서 구현 중 )

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

참고 자료 :
- 이 페이지에서 Ben Noland의 답변 덕분에 저에게 가장 많은 도움이되었습니다.


0
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에 잘 작동합니다!


0

어떤 이유로 다른 사용자가 언급 한 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패키지가 설치된 유닉스 기반 시스템에서 작동합니다 .


xmllint는 이미 또 다른 답변으로 제안되었습니다 : stackoverflow.com/a/10133365/407651
mzjn

@mzjn 나는 대답을했다,하지만 난에 광산을 단순화 check_output하면 오류 검사를 할 필요가 없기 때문에
금요일 스카이

-1

나는 몇 줄의 코드 로이 문제를 해결하고 파일을 열고 여물통을 들여 들여 쓰기를 추가 한 다음 다시 저장합니다. 작은 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()

그것은 나를 위해 작동합니다, 아마도 누군가가 그것을 사용할 것입니다 :)


전후의 스 니펫 스크린 샷을 표시하면 향후 다운 보트를 피할 수 있습니다. 나는 당신의 코드를 시도하지 않았으며 여기에 분명히 다른 대답이 더 좋다고 생각합니다 (그리고 좋은 라이브러리에 의존하기 때문에 더 일반적이고 완전히 형성되었습니다). 사람들은 공감할 때 의견을 남겨야합니다.
Gabriel Staples
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.