지난 포스팅에서는 hwp 파일을 hwpx로 포맷변환하고, 압축을 푸는 과정을 진행해보았습니다.
이번 포스팅은
본격적으로 xml 파헤쳐보기
입니다.
xml파일을 파싱하는 라이브러리는 여러 개가 있지만
우리는 xml파서의 큰형님(?)이라 불릴 수 있는 내장 패키지인 xml을 이용해보겠습니다.
그 중에서도 ElementTree라는 모듈을 사용할건데요.
(ElementTree 외에도 xml 패키지 안에는 dom, parsers, sax 등의 서브패키지가 포함되어 있습니다.)
그 전에 우리가 파헤칠 header.xml 문서를 한 번 열어나 보고 시작하자구요.
포맷변환이나 압축해제 등 이전 과정을 건너뛰고 파싱에만 관심 있으신 분은
아래 첨부한 header.xml 파일만 다운받아서 실습을 진행해주시면 되겠습니다.
분량이 꽤 됩니다. 964줄이네요.
얘는 다운만 받아놓고 잠시 제쳐놓고
xml 패키지의 기본적인 사용방법에 대해 먼저 알아보겠습니다.
위의 header.xml은 너무 복잡하니
아래의 간단한 xml파일을 먼저 예로 들겠습니다.
(파이썬 공식문서 예시 xml문서를 한글로 고쳐놓았습니다.)
출처 : 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 분석으로 넘어갑시다! 라고 말하려고 했는데
포스팅이 너무 길어진 것 같아요. 다음 포스팅에서 이어 설명드리겠습니다!^^
'아래아한글 자동화 > python+hwp 중급' 카테고리의 다른 글
[HDMI] 진짜 본격적으로 header.xml 파헤쳐보기 (0) | 2022.12.07 |
---|---|
[HDMI] hwp를 hwpx로 포맷변환하고 압축 풀기 (0) | 2022.12.07 |
[HDMI] 빈 문서1.hwpx를 분해해보면 (0) | 2022.12.07 |
댓글