티스토리 뷰

Python

Python, XML사용하기

hwangyoungjae 2016. 5. 20. 21:53
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

XML(eXtensible Markup language) W3C에서 1998년에 표준으로 채택한 다목적 마크업언어이다.

XML은 수많은 종류의 데이터를 표현하기 위한 기술로 현재광범위하게 쓰이고 있다.

인터넷, RSS, Open API는 물론이고 워드프로그램각종 어플리케이션의 데이터를 XML로 저장하고 여러 데이터베이스에서 정식으로 XML을 지원하고 있다.

 

Python에서도 XML처리를 하기 위하여 DOM(Document Object Model), SAX(Simple API for XML)등을 지원한다.

이번장에서는 XML자체에 대한 자세한 설명대신 도서관리프로그램을 만들며 파이썬에서 어떻게 XML을 처리하는지 알아보도록 하겠다.

 

>> Python XML <<

Python 2.0부터 표준라이브러리에서 sax dom을 지원하기 시작했다.

Python3에서는 기본적으로 Fast XML parsing using Expat, DOM API, SAX, The Elementree XML API를 지원한다.

Python XML툴이 들어있는 이름공간을 나타내면 아래와 같다.

패키지 이름

이름공간

Fast XML parsing using Expat

xml.parsers.expat

DOM API

xml.dom

SAX

xml.sax

The ElementTree XML API

xml.etree.ElementTree

 

아래는 각 패키지들의 설명이다.

>> Fast XML parsing using Expat <<

패키지명에서도 알수 있듯이 빠른 XML문서 파싱이 장점이다. DOM방식보다 시간은 1/6정도만 소요되고 메모리도 1/5정도만 사용한다. xml.parsers.expat xmlparser라는 단하나의 확장 모듈을 제공하는데 xml.parsers.expat.ParserCreate([인코딩[, 이름공간 구별자]])를 이용해서 xmlparser객체를 생성하고 xmlparser객체를 이용해 파싱을 수행한다.

빠른 파싱을 위해 문서의 유효성을 검사하지 않는다(non-validating)

 

아래는 xmlparser객체의 주요 메서드이다.

메서드 이름

설명

xmlparser.Parse(data[, isfinal])

data를 파싱한다.

만약 더 이상 파싱하지 않을것이라면 isfinal True로 설정해서 호출해야 한다.

xmlparser.ParseFile(file)

파일의 데이터를 파싱한다.

xml.parsers.expat.ErrorString(errno)

errno에 해당하는 에러문을 리턴한다.

 

아래는 xmlparser의 주요 속성이다.

속성 이름

설명

xmlparser.buffer_text

True로 설정하면 문자 엘리먼트를 처리할때 버퍼를 사용한다.

xmlparser.buffer_size

buffer_text속성이 true일때 버퍼의 크기를 설정할수 있다.

xmlparser.ErrorLineNumber

XML문서 파싱중 에러가 발생했을때 문서의 라인넘버가 기록된다.

xmlparser.CurrentLineNumber

현재 파싱하고 있는 라인의 넘버이다.

xmlparser.ordered_attributes

0이 아니면, XML이 파싱될때 속성(Attribute)이 발견되는 순서대로 리스트형식의 속성이름과 속성값을 저장한다기본값은 False이다.

xmlparser.specified_attributes

0이 아니면 명시된 속성일때만 처리를 한다.

명시되지 않은 속성은 무시된다기본값은 False이다.

xmlparser.ErrorCode

에러가 발생했을때 에러 넘버가 저장된다.

ErrorString(errno)메서드를 사용해 에러문을 확인할수 있다.

 

아래는 xmlparser의 핸들러에 대한 표이다.

핸들러를 사용자가 만든 함수와 연결하는 방법은 아래와 같다.

xmlparser객체가 obj라고 할때

obj.handler=함수이름

이때 연결하는 함수의 핸들러의 인수의 개수가 같아야 한다.

핸들러 이름

설명

xmlparser.XmlDecHandler(version, encoding, standalone)

<?xml version="1.0"?>와 같은 XML선언문이 파싱될때 호출된다.

xmlparser.ElementDeclHandler(name, model)

파싱 도중 처음 사용되는 엘리먼트를 발견하면 호출된다. name은 엘리먼트의 이름이다.

xmlparser.StartElementHandler(name, attributes)

모든 엘리먼트의 시작부분에서 호출된다. name은 엘리먼트 이름이고, attribute에는 엘리먼트의 속성값이 사전형식으로 전달된다.

xmlparser.EndElementHandler(name)

모든 엘리먼트의 끝부분에서 호출된다.

xmlparser.CharacterDataHandler(data)

문자 데이터를 발견하면 호출된다. data에 문자 정보가 전달된다.

xmlparser.CommentHandler(data)

주석부분에서 호출된다. data에 주서ㄱ내용이 전달된다.

<!--으로 시작되고 -->으로 끝나는 부분은 data에서 제외된다.

 

>> DOM API <<

XML문서의 각 성분을 객체로 표현하고 모든 객체를 메모리에 저장하고 처리하는 방법이다이같은 XML문서 처리방식을 객체기반의 문서 모델(DOM : Document Object Model)이라고 부른다.

 

DOM은 최근에 XML관리를 위해서 가장 많이 쓰는 방법이고 어플리케이션에서 XML에 접근할때 바로 접근할수 있으며 연관된 데이터를 연속적으로 참고할수 있다.(SAX는 한번에 한 데이터만 가져올수 있다.)

모양이 좋은(well-formed) XML문서는 DOM에서 트리형태로 표현될수 있다. Python3에서 DOM W3C에서 표준으로 정의한 DOM레벨권고안을 기반으로 만들었다.

 

Python3에서는 크게 2개의 기본모듈을 제공하는데 minidom pulldom이다.

대부분의 경우 minidom을 사용하고 메모리가 부족하거나 XML문서가 아주 클 경우 pulldom을 사용하기도 한다.

 

※ Well-formed XML Valid XML

Well-formed XML은 모양이 좋은 혹은 적격XML이라고 부르며 XML Specification 1.0을 따르고 XML표준에 맞는 XML을 의미한다.

예를 들어 아래의 XML문서는 well-formed를 만족하지 못한다.

<?xml version="1.0" ?>

<item>

  <tel>010-1234-5678</tel>

</item>

<item/>

xml의 표준에 따르면 루트엘리먼트는 단 한개만 허용하지만 위의 XML문서는 2개의 root엘리먼트(item)을 가지고 있다. Valid XML Well-formed XML DTD(Document Type Definition) XML Schema같은 XML문서에 대한 문법을 따르는 문서를 의미한다.

 

DOM안에는 아래와 같은 객체들이 있다.

객체 이름

설명

DOMImplementation

DOM을 만드는 기본적인 인터페이스들이 들어 있는 객체

Node

도큐먼트 대부분 객체들의 부모 객체

NodeList

노드 리스트 객체

DocumentType

도큐먼트를 처리하는데 필요한 선언들(시스템ID, 엔티티(entities), 노테이션(notation))들을 위한 객체

Document

도큐먼트 전체를 나타내는 객체

Element

노드 엘리먼트 인터페이스 객체

Attr

엘리먼트 안의 속성값 노드 객체

Comment

소스 XML문서에서 주석부분을 처리하기 위한 객체

Text

도큐먼트에서 문자 정보를 포함하고 있는 노드 객체

ProcessingInstruction

도큐먼트처리를 위한 도구들의 객체

 

>> SAX(Simple API for XML) <<

XML문서를 파싱할때 구성요소(엘리먼트속성문자열)를 발견할때 마다 이벤트를 발생시켜 XML문서를 처리하는 방법이다.

SAX DOM과 다르게 XML문서의 내용을 변경할수 없고파싱은 문서의 첫 부분에서 시작해 아래부분으로만 진행된다.

사용자는 원하는 요소를 처리하기 위한 함수를 생성하고 SAX의 이벤트 핸들러에 함수를 연결한다객체 기반 방식(예를 들면, DOM)보다는 빠르지만 한번 처리한 문서는 다시 사용할수 없다. SAX와 같이 XML문서를 처리하는 방식을 이벤트기반의 문서처리라고 부르고보통 매우 큰 XML문서를 처리할때 많이 사용된다.

 

아래는 xml.sax에 포함된 메서드이다.

-. xml.sax.make_parser([parser_list])

SAX XMLReader를 생성하고 리턴한다. parser_list가 선언되어 있으면 parser_list의 파서들을 먼저 생성시킨다. parser_list create_parser()메서드가 포함된 모듈의 리스트이다여러개의 파서들을 한꺼번에 생성할때 parser_list를 사용하면 유용하다.

 

-. xml.sax.parser(filename_or_stream, handler[, error_handler])

파일이나 스트림으로부터 입력받은 XML문서를 파싱한다핸들러는 SAX ContentHandler인스턴스여야 한다. Error_handler SAX ErrorHandler인스턴스와 연결해야 하고 만약 생략하면 에러가 발생한다. SAXParseException가 발생한다.

 

-. xml.sax.parseString(string, handler[, error_handler])

parse()메서드와 비슷하다단지 XML문서 입력을 문자열로 받는다.

 

일반적인 SAX응용 프로그램은 크게 세부분으로 구성된다리더(Reader)와 핸들러(Handler) 그리고 XML문서이다.

리더는 xml.sax.reader에서 핸들러는 xml.sax.handler에서 지원을 한다.

 

우선 핸들러부분이다.

xml.sax.handler에는 컨텐츠핸들러, DTD핸들러에러핸들러가 있고 각 핸들러에 대한 설명은 아래와 같다.

-. class xml.sax.handler.ContentHandler

SAX중에서 가장 많이 사용되는 핸들러이다도큐먼트 안의 정보들이 파싱되면서 순서대로 이벤트가 ContentHandler을 통해서 호출된다.

 

-. class xml.sax.handler.DTDHandler

DTD이벤트를 위한 핸들러이다.

 

-. class xml.sax.handler.ErrorHandler

에러 혹은 경고가 발생했을때 호출된다에러가 발생했을때 처리할 함수를 만들고 ErrorHandler와 연결을 하면 XML문서 처리 중 발생하는 에러 혹은 경고에 대처할수 있다.

 

SAX parser는 사실 XMLReader인터페이스를 사용해 수현되었다. xml.sax.xmlreader에는 파서를 위한 여러가지 객체가 있다.

클래스이름

설명

class xml.sax.xmlreader.XMLReader

SAX파서들의 기본클래스이다.

class xml.sax.xmlreader.Locator

도큐먼트 내부의 로케이터와 SAX이벤트와의 연결을 위한 클래스이다.

class xml.sax.xmlreader.InputSource([systemId])

XML문서의 인코딩을 파서에 알려주거나 바이트스트림 혹은 캐릭터 스트림을 가져오거나 설정할수 있다.

 

아래는 가장 많이 사용되는 XMLReader의 주요 메서드이다.

-. XMLReader.parse(source)

source로 입력된 XML도큐먼트를 파싱하면서 SAX이벤트를 발생시킨다.

 

-. XMLReader.getContentHandler()

현재 ContentHandler객체를 가져온다.

 

-. XMLReader.setContentHandler(handler)

인수로 전달된 ContentHandler인스턴스를 현재 ContentHandler로 설정한다.

 

-. XMLReader.setLocale(locale)

경고나 에러 출력을 위한 로케일 정보를 설정한다.

 

>> The ElementTree XML API <<

XML문서는 엘리먼트들로 이루어져 있다엘리먼트는 객체를 담을수 있는 유연한 구조이고 데이터들의 계층을 표현할수 있게 설계되었다.(보통 엘리먼트의 계층을 트리로 표현한다.)

이 엘리먼트를 파이썬의 리스트와 사전으로 표현할수 있다.

The ElementTree XML API XML문서의 엘리먼트들을 리스트와 사전으로 다룰수 있는 인터페이스를 제공한다.

 

 

※ 아래에 나오는 예제는 book.py의 일부분을 참고로 설명하도록 하겠다.

#ex_book.py

  1 # -*- coding : cp949 -*-

  2 from xml.dom.minidom import parse, parseString

  3 from xml.etree import ElementTree

  4

  5 #### global

  6 loopFlag=1

  7 xmlFD=-1

  8 BooksDoc=None

  9

 10 #### Menu implementation

 11 def printMenu():

 12     print("\nWelcome! Book Manager Program (xml version)")

 13     print("==========Menu==========")

 14     print("Load xml          : l")

 15     print("Print dom to xml  : p")

 16     print("Quit program      : q")

 17     print("Print Book list   : b")

 18     print("Add new book      : a")

 19     print("Search Book Title : e")

 20     print("Make html         : m")

 21     print("========================")

 22

 23 def launcherFunction(menu):

 24     global BooksDoc

 25     if menu == 'l':

 26         BooksDoc=LoadXMLFromFile()

 27     elif menu == 'q':

 28         QuitBookMgr()

 29     elif menu == 'p':

 30         PrintDOMtoXML()

 31     elif menu == 'b':

 32         PrintBookList(["title",])

 33     elif menu == 'a':

 34         ISBN = str(input('insert ISBN : '))

 35         title = str(input('insert Title : '))

 36         AddBook({'ISBN':ISBN, 'title':title})

 37     elif menu == 'e':

 38         keyword = str(input('input keyword to search : '))

 39         printBookList(SearchBookTitle(keyword))

 40     elif menu == 'm':

 41         keyword = str(input('input keyword code to the html : '))

 42         html = MakeHtmlDoc(SearchBookTitle(keyword))

 43         print("--------------------")

 44         print(html)

 45         print("--------------------")

 46     else:

 47         print("error : unknown menu key")

 48

 49 #### xml function implementation

 50 def LoadXMLFromFile():

 51     filename = str(input("please input file name to load :"))

 52     global xmlFD

 53

 54     try:

 55         xmlFD = open(filename)

 56     except IOError:

 57         print('invalid file name or path')

 58         return None

 59     else:

 60         try:

 61             dom = parse(xmlFD)

 62         except Exception:

 63             print('loading fail!!!')

 64         else:

 65             print("XML Document loading complete")

 66             return dom

 67     return None

 68

 69 def BooksFree():

 70     if checkDocument():

 71         BookDoc.unlink()

 72

 73 def QuitBookMgr():

 74     global loopFlag

 75     loopFlag = 0

 76     BooksFree()

 77

 78 def PrintDOMtoXML():

 79     if checkDocument():

 80         print(BooksDoc.toxml())

 81

 82 def PrintBookList(tags):

 83     global BooksDoc

 84     if not checkDocument():

 85         return None

 86

 87     booklist=BooksDoc.childNodes

 88     book=booklist[0].childNodes

 89     for item in book:

 90         if item.nodeName == 'book':

 91             subitems=item.childNodes

 92             for atom in subitems:

 93                 if atom.nodeName in tags:

 94                     print('title=',atom.firstChild.nodeValue)

 95

 96 def AddBook(bookdata):

 97     global BooksDoc

 98     if not checkDocument():

 99         return None

100

101     # Book 엘리먼트를 만듬

102     newBook=BooksDoc.createElement('book')

103     newBook.setAttribute('ISBN',bookdata['ISBN'])

104     # Title 엘리먼트를 만듬

105     titleEle=BooksDoc.createElement('title')

106     텍스트 엘리먼트를 만듬

107     titleNode=BooksDoc.createTextNode(bookdata['title'])

108     텍스트 노드와 Title 엘리먼트를 연결

109     try:

110         titleEle.appendChild(titleNode)

111     except Exception:

112         print('append child fail- please, check the parent element & node!!!')

113         return None

114     else:

115         titleEle.appendChild(titleNode)

116

117     # Title book엘리먼트와 연결

118     try:

119         newBook.appendChild(titleEle)

120         booklist=BooksDoc.firstChild

121     except Exception:

122         print('append child fail- please, check the parent element node!!!')

123         return None

124     else:

125         if booklist != None:

126             booklist.appendChild(newBook)

127

128 def SearchBookTitle(keyword):

129     global BooksDoc

130     retlist = []

131     if not checkDocument():

132         return None

133

134     try:

135         tree=ElementTree.fromstring(str(BooksDoc.toxml()))

136     except Exception:

137         print("Element Tree parsing Error : maybe the xml document is not corrected.")

138         return None

139

140     # Book 엘리먼트 리스트를 가져옴

141     bookElements = tree.getiterator('book')

142     for item in bookElements:

143         strTitle = item.find('title')

144         if (strTitle.text.find(keyword) >= 0):

145             retlist.append((item.attrib["ISBN"], strTitle.text))

146

147     return retlist

148

149 def MakeHtmlDoc(BookList):

150     from xml.dom.minidom import getDOMImplementation

151     # DOM 개체를 생성

152     impl = getDOMImplementation()

153

154     newdoc=impl.createDocument(None, 'html', None)

155     top_element = newdoc.documentElement

156     header = newdoc.createElement('header')

157     top_element.appendChild(header)

158

159     #Body 엘리먼트를 생성

160     body = newdoc.createElement('body')

161

162     for bookitem in BookList:

163         # Bold 엘리먼트 생성

164         b=newdoc.createElement('b')

165         텍스트 노드 생성

166         ibsnText=newdoc.createTextNode("ISBN:" + bookitem[0])

167         b.appendChild(ibsnText)

168

169         body.appendChild(b)

170

171         # <br> 부분 생성

172         br = newdoc.createElement('br')

173

174         body.appendChild(br)

175

176         # title 부분 생성

177         p = newdoc.createElement('p')

178         텍스트 노드를 만듭니다.

179         titleText=newdoc.createTextNode('Title:' + bookitem[1])

180         p.appendChild(titleText)

181

182         body.appendChild(p)

183         body.appendChild(br)

184

185     # Body 엘리먼트를 취상위 엘리먼트에 추가

186     top_element.appendChild(body)

187

188     return newdoc.toxml()

189

190 def printBookList(blist):

191     for res in blist:

192         print(res)

193

194 def checkDocument():

195     global BooksDoc

196     if BooksDoc == None:

197         print("Error : Document is empty")

198         return False

199     return True

200

201 #### run ####

202 while(loopFlag > 0):

203     printMenu()

204     menuKey=str(input("select menu :"))

205     launcherFunction(menuKey)

206 else:

207     print("Thank you! Good Bye")

 

>> XML문서 파싱하기 <<

XML을 이용한 프로그램은 대부분 XML문서를 파싱하는것부터 시작한다파이썬으로 XML을 파싱하는 방법은 여러가지가 있다. XML문서의 일종인 HTML문서라면 html.parser를 통해서 할수 있고아래와 같이 Expat를 통해서 파싱할수도 있다.

아래는 xmlparser객체를 이용해 XML문서를 파싱하는 과정이다.

import xml.parsers.expat

def start_element(name,attrs): #파싱할때 새로운 엘리먼트가 발견되면 이 함수가 실행된다.

    print('Start element:',name, attrs)

def char_data(data):

    print('Character data:',repr(data))

pa=xml.parsers.expat.ParserCreate()

pa.StartElementHandler=start_element #이벤트 핸들러를 연결해준다.

pa.CharacterDataHandler=char_data

pa.Parse("""<?xml version="1.0"?><book ISBN="1111">

<title>Loving Python</title></book>""")

실행

Start element: book {'ISBN': '1111'}

Character data: '\n'

Start element: title {}

Character data: 'Loving Python'

DOM을 이용해서 XML문서를 파싱하는 가장 간단한 방법은 minidom을 이용하는 방법이다. xml.dom.minidom Document Object Model 인터페이스를 가볍게 구현해 놓은것이다.

minidom은 전체 DOM모듈보다 사용하기 간편하고 크기도 상당히 작다.

minidom은 파싱을 위해 parse() parseString()라는 2개의 메서드를 가지고 있다.

-. xml.dom.minidom.parse(file, parse)

file로부터 XML문서를 읽어서 Document객체를 반환한다. parser인수가 주어지면 minidom의 기본 파서가 아닌 사용자가 원하는 파서를 사용할수 있다. (파서는 SAX2 parser객체만 사용가능하다.)

 

-. xml.dom.minidom.parseString(data, parser)

parse()와 비슷하지만 입력으로 파일 대신 문자열을 받는다.

 

minidom을 이용해 간단한 xml문서를 파싱해보도록 하겠다.

from xml.dom.minidom import *

xmlsrc="""<item>

<name>test</name>

</item>

"""

doc=parseString(xmlsrc)

names=doc.getElementsByTagName("name"#name이라는 엘리먼트를 검색

print('doc : ',doc)

print(doc.toxml())

print('names : ',names)

print('검색된 엘리먼트갯수 :',names.length)

실행

doc :  <xml.dom.minidom.Document object at 0x00C73730>

<?xml version="1.0" ?><item>

<name>test</name>

</item>

names :  [<DOM Element: name at 0xcf1890>]

검색된 엘리먼트갯수 : 1

파싱이 성공하면 Document객체를 리턴해준다. Document객체는 XML문서의 전체를 나타내는 객체로엘리먼트(Element), 속성(Attribute), 주석(comment), 기타 모든것을 포함하고 있다.

 

아래는 Document객체의 주요 메서들이다.

-. Document.createElement(tagName)

새로운 엘리먼트 객체를 생성한다. tagName은 생성할 엘리먼트의 이름이 된다.

 

-. Document.createTextNode(data)

인수 data로부터 문자 노드를 생성하고 리턴해준다.

 

-. Document.createAttribute(name)

속성(attribute)노드를 생성하고 생성된 객체를 리턴해준다이 메서드는 단지 속성 객체를 만들 뿐이지 원하는 엘리먼트와 연결을 시킬수 없다엘리먼트와 연결하기 위해선 setAttributeNode()메서드를 사용해야 한다.

 

-. Document.getElementsByTagName(tagName)

인수 tagName과 같은 이름을 가진 모든 엘리먼트들을 검색한다결과값으로 nodeList가 전달된다.

 

이제부터 만들 도서관리 프로그램은 도서정보를 담고 있는 XML문서를 읽어서 도서 목록을 출력하거나새로운 도서를 XML문서에 저장하고 도서 제목을 검색하는 기능을 가진 텍스트기반 프로그램이다도서 정보를 읽는다는 것은 위에서 말한 파싱을 뜻한다.

 

일반적으로 XML문서를 작성할때 DTD, XML Schema를 먼저 작성하거나 참조해야하지만이번 예제에서는 Python으로 XML처리방법에 집중하기 위하여 간단한 XML문서를 작성하여 진행하도록 한다.

 

ex) book.xml : 책에 대한 정보를 xml문서로 표현

<?xml version="1.0" ?>

<booklist cnt="3">

    <book ISBN="0399250395">

        <title>The Very Hungry Caterpillar Pop-Up Book</title>

        <auther name="Eric Carle"/>

    <auther name="Keith Finch"/>

    <publisher> Philomel Books </publisher>

    <description> Celebrating the 40th anniverary of one of the most popular chren's books ever created</description>

    </book>

    <book ISBN="0964729237">

        <title lang="english">The Shack</title>

    </book>

    <book ISBN="0553281097">

        <title>You Can Negotiate Anything</title>

        <auther name="Herb Cohen"/>

        <categoty cid="12">Negotiate and narrative skill</categoty>

    </book>

</booklist>

XML문서는 booklist라는 루트 엘리먼트가 있고 그 아래 책정보를 가지고 있는 book엘리먼트들이 있다책 정보는 책제목, ISBN, 저자출판사설명들이 있다.

LoadXMLFromFile()함수는 이 도서의 XML문서를 파싱하는 역할을 한다.

book.py 001 ~ 071

 

LoadXMLFromFIle()함수의 내부에서는 minidom의 메서드를 이용해서 파일에서 XML문서를 읽어와 파싱을 한다.

파싱한 데이터는 여러곳에서 사용될것이기 때문에 전역변수 BooksDoc에 저장한다.

 

minidom을 사용한 다음엔 반드시 unlink()메서드를 호출하여 DOM객체 내부의 참조를 제거하여 가비지컬렉션이 가능하도록 한다.

그렇게 하지 않으면 메모리 누수현상이 일어날수 있다.

BookFree()함수가 그런 역할을 하게 된다.

 

ex) LoadXMLFromFIle()함수 테스트

Welcome! Book Manager Program (xml version)

==========Menu==========

Load xml          : l

Print dom to xml  : p

Quit program      : q

Print Book list   : b

Add new book      : a

Search Book Title : e

Make html         : m

========================

select menu :l

please input file name to load :book.xml

XML Document loading complete

로딩을 하겠다는 l명령어를 입력하고 원하는 파일을 지정하면 XML로딩은 완료된다.

 

이번에는 toxml()메서드를 이용해서 파싱된 DOM객체를 반대로 XML문서로 만들어보겠다.

book.py 078 ~ 080

 

위 함수는 DOM객체가 None이 아니면 toxml()메서드를 이용해 로딩한 XML문서를 문자열로 출력한다.

 

>> 노드 <<

노드(Node) DOM에서 가장 중요한 자료형이다우선 DOM의 모든 컴포넌트들은 노드의 서브클래스이다그렇기 때문에 노드는 엘리먼트(Element) 노드속성(Attribute) 노드텍스트(Text) 노드기타 다른 노드들이 될수 있다.

노드에 대해서 조금 더 자세히 공부해야할 필요가 있는데 대부분의 DOM을 사용한 응용프로그램들은 노드를 모르고는 구현할수가 없기 때문이다.

아래는 Python에서 node에 대한 메서드와 속성에 관한 정리이다.

-. Node.nodeType

노드타입을 나타내는 정수값이다.

 

-. Node.parentNode

현재 노드의 부모노드

만약 parentNode값이 None이면 현재 엘리먼트는 루트엘리먼트이다.

 

-. Node.attributes

속성(Attributes)객체의 NamedNodeMap

read-only값이다.

 

-. Node.nextSibling

같은 부모를 가지고 있는 다른 노드들 중 현재 노드의 다음 노드를 가리킨다.

 

-. Node.previousSibling

같은 부모를 가지고 있는 다른 노드들 중 현재 노드의 이전 노드를 가리킨다.

 

-. Node.childNodes

현재 노드의 자식 노드 리스트

 

-. Node.firstChild

자식 노드들 중 첫번째 노드

 

-. Node.lastChild

자식 노드들 중 마지막 노드

 

-. Node.localName

태그네임에 콜론(:)이 있을 경우 콜론이후에 오는 부분을 의미

 

-. Node.prefix

태그네임에 콜론(:)이 있을 경우 콜론 앞쪽을 의미

 

-. Node.namespaceURI

노드가 엘리먼트 타입일때이름공간이 지정되어 있으면 이름공간값(문자열)이 들어가고 이름공간이 없는 경우 None값이다.

 

-. Node.hasAttributes()

노드가 있으면 True를 리턴

 

-. Node.hasChildNodes()

자식 노드가 있으면 True를 리턴

 

-. Node.isSameNode(other)

인수로 받은 other노드와 현재 노드가 같으면 참을 리턴

 

-. Node.appendChild(newChild)

새로운 노드를 현재 노드의 자식 노드로 추가

 

-. Node.insertBefore(newChild, refChild)

새로운 노드(newChild)를 지정한 노드(refChild)앞쪽에 삽입

 

-. Node.removeChild(oldChild)

지정된 자식노드(oldChild)를 삭제

 

-. Node.replaceChild(newChild, oldChild)

기존의 자식노드(oldChild)를 새노드(newChild)로 바꾼다.

 

-. Node.normalize()

노드의 테스트정보를 정규화해준다.

빈칸은 삭제해주고비어있는 엘리먼트는 빈 태그로 바꿔준다.

예를 들면 '<test><item></item><title>타이틀</title></test>'를 정규화 하면

'<test><item/><title>타이틀</title></test>'처럼 된다.

 

-. Node.cloneNode(deep)

현재 노드(자식 노드 포함)을 복사해서 리턴해준다.

 

계속해서 노드를 이용해 DOM을 탐색해 보도록 하겠다.

현재 노드에서 하위 엘리먼트를 참고하기 위해서는 현재 노드에 연결된 하위 노드들을 얻어야 한다.

이때 이용하는 속성이 childNodes이다.

>>> import xml.dom.minidom

>>> books=xml.dom.minidom.parse('book.xml')

>>> booklist=books.childNodes

>>> type(booklist)

<class 'xml.dom.minicompat.NodeList'>

>>> len(booklist)

1

>>> booklist[0]

<DOM Element: booklist at 0x1063c50>

>>> booklist[0].nodeType

1

>>> book=booklist[0].childNodes

>>> book

[<DOM Text node "'\n    '">, <DOM Element: book at 0x1095890>, <DOM Text node "'\n    '">, <DOM Element: book at 0x109fa90>, <DOM Text node "'\n    '">, <DOM Element: book at 0x109fbd0>, <DOM Text node "'\n'">]

>>> len(book)

7

childNodes를 통해 얻어온 노드에는 현재 자기와 연결된 하위노드가 포함되어 있다.

이때 클래스는 xml.dom.minicompat.NodeList이며노드는 여러개 존재할수 있으므로 리스트 형식을 가진다.

 

노드 리스트는 말 그대로 리스트형식으로 되어 있기 때문에 배열을 사용해 각각의 노드에 접근할수 있다.

 

※ Node Type

다양한 종류의 정보들을 처리하기 위해서 노드들에게는 고유의 타입이 있다.

예를 들면 위의 book.xml노드이 타입은 아래와 같다.

ELEMENT_NODE = 1,

ATTRIBUTE_NOE = 2,

TEXT_NODE = 3,

CDATA_SECTION_NODE = 4,

ENTITY_NODE = 5,

PROCESSING_INSTRUCTION_NODE = 6,

COMMENT_NODE = 7,

DOCUMENT_NODE = 8,

DOCUMENT_TYPE_NODE = 9,

NOTATION_NODE = 10

각각 노드 타입에 대한 설명은 간단한 예제로 대신한다.

>>> for bookitem in book:

             print("Type=", bookitem.nodeType, "Name=",bookitem.nodeName)

             if (bookitem.nodeType) == 3:

                           print("Value=", bookitem.nodeValue)

 

Type= 3 Name= #text

Value=

   

Type= 1 Name= book

Type= 3 Name= #text

Value=

   

Type= 1 Name= book

Type= 3 Name= #text

Value=

   

Type= 1 Name= book

Type= 3 Name= #text

Value=

 

 

노드를 탐색하는 방법을 알아봤으니 도서관리 프로그램으로 돌아와서 책목록을 출력하는 함수를 보도록 하겠다.

book.py 082 ~ 094

 PrintBookList()함수는 노드를 이용해 자식노드를 계속 탐색하면서 xml에 등록된 책제목을 출력하고 있다.

PrintBookList()함수는 인수로 리스트를 입력받는데, Book의 하위 엘리먼트 이름 중리스트에 들어 있는 무자열과 매칭되는 엘리먼트를 출력한다.

여기서 책 제목만 출력할 예정이기 때문에 아래와 같이 사용하였다.

 32 PrintBookList(["title",])

직접 책 제목을 출력해보도록 하겠다.

select menu :b

title= The Very Hungry Caterpillar Pop-Up Book

title= The Shack

title= You Can Negotiate Anything

 

>> 엘리먼트 <<

도서 데이터에 새로운 도서 정보를 등록시키는 기능을 추가하는 방법으로 엘리먼트를 보도록 하겠다.

하나의 도서데이터는 Book엘리먼트와 그 하위 엘리먼트로 표현된다.

새로운 도서 정보를 등록시킨다는 것은 Book엘리먼트가 하나 더 늘어나는 것을 의미한다.

파이썬에서 지원하는 엘리먼트 관련 메서드들은 아래와 같다.

-. Element.getElementsByTagName(tagName)

엘리먼트 이름중 tagName과 매칭되는 엘리먼트들을 반환

 

-. Element.getElementsByTagNameNS(namespaceURI, localName)

XML문서에 이름공간이 지정되어 있는 경우,

namespaceURI안에 localName과 매칭되는 엘리먼트들을 반환

 

-. Element.hasAttribute(name)

엘리먼트 속성 중 name에 해당하는 속성이 있으면 참을 반환

 

-. Element.hasAttributeNS(namespaceURI, localName)

XML문서에 이름공간이 지정되어 있는 경우,

namespaceURI안에 localName과 매칭되는 엘리먼트가 있으면 참을 반환

 

-. Element.getAttribute(name)

name에 해당하는 속성값을 출력

 

-. Element.getAttributeNode(attrname)

attrname에 해당하는 속성과 객체 Attr을 반환

 

-. Element.getAttributeNS(namespaceURI, localName)

XML문서에 이름공간이 지정되어 있는 경우,

namespaceURI안에 localName과 매칭되는 속성값을 반환

 

-. Element.getAttributeNodeNS(namespaceURI, localName)

XML문서에 이름공간이 지정되어 있는 경우,

namespaceURI안에 localName과 매칭되는 속성 객체 Attr을 반환

 

-. Element.removeAttribute(name)

name에 해당하는 속성을 삭제

 

-. ElementremoveAttributeNode(oldAttr)

속성 리스트들 중에서 oldAttr에 해당하는 속성을 삭제하고 삭제한 속성을 리턴함

 

-. Element.removeAttributeNS(namespaceURI, localName)

XML문서에 이름공간이 지정되어 있는 경우,

namespaceURI안에 localName과 매칭되는 속성을 삭제

 

-. Element.setAttribute(name, value)

name에 해당하는 속성 값을 value로 치환

 

-. Element.setAttributeNode(newAttr)

newAttr에 해당하는 속성이 이미 있다면 기존의 속성과 교체가 되고없다면 새로운 속성이 추가된다.

 

-. Element.setAttributeNodeNS(namespaceURI, localName, newAttr)

XML문서에 이름공간이 지정되어 있는 경우,

namespaceURI안에 localName과 매칭되는 속성이 이미 있다면 기존의 속성과 교체가 되고없다면 새로운 속성이 추가된다.

 

우선 빈 Book엘리먼트를 생성한다.

엘리먼트를 만들때는 Document객체의 createElement()메서드를 이용한다생성한 엘리먼트들은 노드를 조작해 원하는곳에 추가할수 있다.

book.py 096 ~ 126

 

책 제목 데이터를 입력하려면 텍스트 타입의 엘리먼트가 필요하다텍스트 타입은 다른 타입과 다르게 엘리먼트나 속성에 대한 정보가 아닌 실제정보가 저장하는 곳이다.

createTextNode()를 호출하면 텍스트엘리먼트가 생성된다.

생성된 텍스트 엘리먼트는 appendChild()메서드를 통해서 Title엘리먼트와 연결한다.

 

setAttribute()메서드는 엘리먼트의 속성값을 설정하는 메서드이다.

여기서는 book엘리먼트의 ISBN 속성값을 설정해주는데 사용되었다.

 

책을 추가해 보도록 하겠다책 추가 메뉴는 'a'이다

Welcome! Book Manager Program (xml version)

==========Menu==========

Load xml          : l

Print dom to xml  : p

Quit program      : q

Print Book list   : b

Add new book      : a

Search Book Title : e

Make html         : m

========================

select menu :a

insert ISBN : 123456

insert Title : test book

 

Welcome! Book Manager Program (xml version)

==========Menu==========

Load xml          : l

Print dom to xml  : p

Quit program      : q

Print Book list   : b

Add new book      : a

Search Book Title : e

Make html         : m

========================

select menu :b

title= The Very Hungry Caterpillar Pop-Up Book

title= The Shack

title= You Can Negotiate Anything

title= test book

 

>> 엘리먼트 쉽게 다루기 <<

위 엘리먼트를 조금더 쉽게 다루기 위해 Python에서 제공하는 기본클래스가 xml.etree.ElementTree이다.

ElementTree클래스는 엘리먼트의 생성값변경검색 등의 기능과 엘리먼트를  XML로 변환 시켜주는 기능을 가지고 있다.

아래는 ElementTree의 주요 모듈함수들을 정리한 내용이다.

-. xml.etree.ElementTree.parse(file[, parser])

파일로부터 XML문서를 읽어와 파싱한다. parser가 입력되면 사용자가 지정한 파서를 사용한다지정하지 않으면 기본적으로 XMLBuildTree파서를 사용한다성공하면 xml.etree.ElementTree.ElementTree객체를 리턴한다.

 

-. xml.etree.ElementTree.fromstring(text)

문자열 text를 파싱한다. xml.etree.ElementTree.ElementTree객체를 리턴한다.

 

-. xml.etree.ElementTree.Element(tag[, attrib][, **extra])

tag의 이름을 가진 엘리먼트를 생성한다.

사전 형식으로 속성값을 지정할수 있다키워드 인수로 속성값을 설정할수 있다성공하면 생성된 엘리먼트 인스턴트를 리턴한다.

 

-. xml.etree.ElementTree.SubElement(parent, tag[, attrib[, **extra]])

Element()와 비슷하지만 parent의 자식 엘리먼트로 만들어진다.

 

-. xml.etree.ElementTree.tostring(element[, encoding])

element객체를 XML문자열로 변환한다. encoding을 지정하지 않으면 UTF-8로 인코딩 된다.

 

 

ElementTree객체 중 자주 사용되는 메서드는 아래와 같다.

-. find(path)

path에 매칭되는 엘리먼트들을 리턴한다예를 들어 path body/p이면 부모 엘리먼트 이름이 body이고 엘리먼트 이름이 p인 엘리먼트를 검색한다리턴값이 None이면 해당하는 엘리먼트가 없다는 뜻이다.

 

-. getiterator([tag])

현재 엘리먼트의 하위 엘리먼트를 모두 가져올수 있다.

만약 tag가 지정되어 있으면 tag에 해당하는 엘리먼트들만 리턴한다.

 

-. getroot()

현재 XML문서중 가장 상위 엘리먼트 객체를 리턴한다.

 

-. write(file[, encoding])

현재 ElementTree객체를 file에 저장한다. encoding을 지정할 경우 지정된 인코딩으로 저장된다.

 

아래는 ElementTree를 이용해 검색기능을 추가한것이다.

사용자로부터 키워드를 입력받고 책 제목을 검색해주는 기능이다.

book.py 128 ~ 147

함수 초반에 fromstring()을 사용해 파싱을 하고, getiterator()메서드를 사용해 book엘리먼트만 추출하였다.

검색기능을 수행해보자검색메뉴는 'e'이다.

Welcome! Book Manager Program (xml version)

==========Menu==========

Load xml          : l

Print dom to xml  : p

Quit program      : q

Print Book list   : b

Add new book      : a

Search Book Title : e

Make html         : m

========================

select menu :e

input keyword to search : The

('0399250395', 'The Very Hungry Caterpillar Pop-Up Book')

('0964729237', 'The Shack')

만약 앞에서처럼 노드를 따라가면서 검색을 했다면 상당히 복잡한 코드가 나올수 있었지만 엘리먼트 트리를 이용해서 간단하게 해결되었다.

 

>> XML문서를 HTML로 변환하기 <<

HTML XML의 한 종류이다보통 XML문서를 다른 XML문서로 변환 혹은 일반 문서데이터로 변환하는데 XSLT를 사용한다.

하지만 Python모듈에는 이른 기능이 없기 때문에 아래에서 DOM객체를 생성하고 부모 엘리먼트와 자식 엘리먼트들을 생성해서 DOM객체에 추가시키는 방법으로 사용했다.

 

우선 MakeHtmlDOc()함수이다.

book.py 149 ~ 188

코드가 조금 길지만 내용은 간단하다. createDocument()메서드를 이용해 DOM 객체를 생성한다.

이후 HTML의 태그가 될 엘리먼트들을 생성하고 생성한 DOM객체에 삽입한다리턴값으로 생성한 DOM객체를 XML문자열로 반환한다.

아래는 실행결과이다.

Welcome! Book Manager Program (xml version)

==========Menu==========

Load xml          : l

Print dom to xml  : p

Quit program      : q

Print Book list   : b

Add new book      : a

Search Book Title : e

Make html         : m

========================

select menu :m

input keyword code to the html : The

--------------------

<?xml version="1.0" ?><html><header/><body><b>ISBN:0399250395</b><p>Title:The Very Hungry Caterpillar Pop-Up Book</p><br/><b>ISBN:0964729237</b><p>Title:The Shack</p><br/></body></html>

--------------------

 

>> XML을 위한 Python의 외부모듈 <<

>PyXML (http://pyxml.sourceforge.net/topics/)

Python SIG(Special Interest Group)커뮤니티에서 만든 패키지로 XPath XSLT변환 같은 기능을 제공한다파이썬 뿐만 아니라 XML에 대해서도 정리가 잘 되어 있다.

 

>4suite (http://4suite.org)

Fourthought 소프트웨어 회사의 가장 많은 기능과 강력한 성능을 자랑하는 패키지 이다.

DOM, XPath, XPointer, XLink, XUpdate, RDF를 지원한다.

 

>libxml_saxlib, libxml_domlib (http://www.rexx.com/~dkuhlman/)

GNOME libxml SAX, DOM, 그리고 XSLT를 Python으로 래핑(wrapping)한 패키지이다.

 GNOME libxml라이브러리를 Python에서 사용할수 있다.

 

>BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/)

XML/HTML을 위한 간편한 파서이다.

무엇보다 사용하기 간편한 메서드를 제공한다.

 

 

참조 : 빠르게 활용하는 파이썬프로그래밍

 

'Python' 카테고리의 다른 글

Python, bytes를 문자열로 변환하기  (0) 2016.05.24
Python, str(), repr(), ascii(),eval()  (0) 2016.05.24
Python,module weakref [약한참조]  (0) 2016.05.20
Python,module queue  (0) 2016.05.20
Python,module threading [멀티스레드]  (0) 2016.05.19
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함