본문 바로가기
아래아한글 자동화/python+hwp 중급

[HDMI] 본격적으로 xml 파헤쳐보기

by 일코 2022. 12. 7.

지난 포스팅에서는 hwp 파일을 hwpx로 포맷변환하고, 압축을 푸는 과정을 진행해보았습니다.

 

이번 포스팅은

본격적으로 xml 파헤쳐보기

입니다.

 

xml파일을 파싱하는 라이브러리는 여러 개가 있지만

우리는 xml파서의 큰형님(?)이라 불릴 수 있는 내장 패키지인 xml을 이용해보겠습니다.

그 중에서도 ElementTree라는 모듈을 사용할건데요.

(ElementTree 외에도 xml 패키지 안에는 dom, parsers, sax 등의 서브패키지가 포함되어 있습니다.)

 

그 전에 우리가 파헤칠 header.xml 문서를 한 번 열어나 보고 시작하자구요.

포맷변환이나 압축해제 등 이전 과정을 건너뛰고 파싱에만 관심 있으신 분은

아래 첨부한 header.xml 파일만 다운받아서 실습을 진행해주시면 되겠습니다.

header.xml
0.06MB

분량이 꽤 됩니다. 964줄이네요.

 

얘는 다운만 받아놓고 잠시 제쳐놓고

xml 패키지의 기본적인 사용방법에 대해 먼저 알아보겠습니다.

위의 header.xml은 너무 복잡하니 

아래의 간단한 xml파일을 먼저 예로 들겠습니다.

(파이썬 공식문서 예시 xml문서를 한글로 고쳐놓았습니다.)

country.xml
0.00MB

출처 : xml.etree.ElementTree — The ElementTree XML API — Python 3.11.0 문서

<?xml version="1.0"?>
<데이터>
    <국가 이름="리히텐슈타인">
        <순위>1</순위>
        <연도>2008</연도>
        <인당국내총생산>141100</인당국내총생산>
        <인접국 이름="오스트리아" direction="동"/>
        <인접국 이름="스위스" direction="서"/>
    </국가>
    <국가 이름="싱가포르">
        <순위>4</순위>
        <연도>2011</연도>
        <인당국내총생산>59900</인당국내총생산>
        <인접국 이름="Malaysia" direction="북"/>
    </국가>
    <국가 이름="파나마">
        <순위>68</순위>
        <연도>2011</연도>
        <인당국내총생산>13600</인당국내총생산>
        <인접국 이름="코스타리카" direction="서"/>
        <인접국 이름="콜롬비아" direction="동"/>
    </국가>
</데이터>

이 파일을 파싱하기 위해 우리가 사용할 모듈은 ElementTree 입니다.

모듈의 이름 그대로 요소들을 트리처럼 구성해 탐색할 수 있는 객체를 만들어줍니다.

파싱을 시작하려면 최상위 태그에서부터 시작하는데,

최상위(root) 태그인 <데이터>에 접근하는 코드는 아래와 같습니다.

>>> import xml.etree.ElementTree as ET  # 여기서 ET는 클래스가 아니고 모듈의 이름

>>> tree = ET.parse("./country.xml")  # tree는 ElementTree 클래스의 인스턴스
>>> root = tree.getroot()  # root는 Element(요소)
>>> print(root.tag)  # 루트요소의 태그명을 출력
데이터

>>> print(root.attrib)
[]

이제 데이터 요소를 root 변수에 넣었습니다.

태그이름은 root.tag으로, 속성은 root.attrib으로 조회할 수 있는데

루트에는 부여된 속성이 없어서 빈 리스트를 리턴했습니다.

 

자식노드를 조회하는 가장 기초적인 방법은 for문을 이용하는 것입니다.

 

또한 재미있게도 리스트 인덱싱처럼 접근할 수도 있습니다.

 

이제 본격적으로 탐색을 위한 메서드를 알아봅시다.

 

① iter(태그) : 모든 하위 엘리먼트를 전부 탐색해서 제너레이터로 리턴

데이터 트리의 인접국 태그의 속성을 전부 출력하려면?

root.iter() 와 요소.attrib을 이용하면 되겠네요.

 

② find(태그) : 바로아래 자식요소에서 태그가 일치하는 첫 번째 요소만 리턴

③ findall(태그) : 위와 같지만 한 개가 아니라 모든 자식요소를 리스트로 리턴

모든 "국가" 태그 하위에서 "순위" 태그의 텍스트만 추출해서 국가이름과 같이 출력하고 싶다면?

- root.findall("국가")로 모든 국가 요소를 순회하면서

- find("순위").text로 순위값을 추출하면 되겠네요.

- 그리고 국가이름은 속성에 들어있으니까 국가태그.get("이름")으로 추출할 수 있겠네요.

아래처럼요.

④ iterfind(태그) : findall()과 아주 비슷하지만 리스트가 아닌 시퀀스(이터레이터)를 리턴함.

                              리스트를 생성하는 findall()에 비하면 메모리공간을 훨씬 적게 차지한다는 장점이 있음

 

위 네가지 메서드를 요약해보면,

어떤 요소가 어떤 위치에 있는지 모르지만 태그이름만 알고 있다면

① 선택지 없이 iter()로 탐색하는 게 제일 간편할 것 같고요. (네 개 중에 가장 오래 걸리긴 하겠죠?)

② 자식요소들 중에 특정 태그가 하나밖에 없다는 확신이 있으면 find()를 쓰면 되고

③ 자식들 중에 특정 태그가 여러 개가 있다면 findall()을 쓰면 되는데,

④ 요소가 엄청나게 많거나, 성능을 고려해야 한다면 가급적 iterfind()를 쓰면 되겠습니다.

 

기본 문법은 이 정도만 배우면 될 것 같아요.

부족한 대로 header.xml 분석으로 넘어갑시다! 라고 말하려고 했는데

포스팅이 너무 길어진 것 같아요. 다음 포스팅에서 이어 설명드리겠습니다!^^

댓글