XML 읽기(파싱)
ElementTree는 외부라이브러리로 존재하다가 파이썬 2.5부터 통합되었다.
XML 샘플 데이타 ( http://goo.gl/VAWy4t )
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
XML 로드 : 파일
import xml.etree.ElementTree as ET
# parse xml file
tree = ET.parse(file_name)
# get root node
root = doc.getroot()
XML 로드 : 문자열
root = ET.fromstring(country_data_as_string)
XML 태그 구성
- tag : 태그의 이름
- text : 태그의 Text
- attrib : 노드의 attribute 맵 (key, value)
테스트 소스
import xml.etree.ElementTree as ET
country_data_as_string = """<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
"""
root = ET.fromstring(country_data_as_string)
for country in root.findall('country'):
rank = country.find('rank').text
name = country.get('name')
print(name, rank)
특정 태그 찾기
# root 하위에 "country"와 일치하는 첫번째 태그를 찾아서 리턴한다. 없으면 None을 리턴한다.
country_tag = root.find("country")
# root 하위에 "country"와 일치하는 모든 태그를 리스트로 리턴한다.
country_tags = root.findall("country")
# root 하위에 "country"와 일치하는 첫번째 태그를 찾아서 해당 태그의 text를 리턴한다.
country_text = root.findtext("country")
# findtext는 find().text와 동일하다
country_text = root.find("country").text
# year 태그는 country의 자식이지, root의 자식이 아니다
country_tags = root.findall("year")
특정 태그를 찾은 뒤 text, 속성 출력
>>> for country in root.findall('country'):
... rank = country.find('rank').text
... name = country.get('name')
... print(name, rank)
...
Liechtenstein 1
Singapore 4
Panama 68
관심있는 요소 찾기
# root 태그에서도 iter("neighbor")만 모두 순회가 가능하다
for neighbor in root.iter("neighbor"):
print neighbor.attrib
자식 순회 1
for child in root.iter()
print(child.tag)
# root 이하 country 태그들에 대해 태드명을 프린트한다
for country in root.iter("country")
print(country.tag)
자식 순회 2
>>> for child in root:
... print(child.tag, child.attrib)
...
country {'name': 'Liechtenstein'}
country {'name': 'Singapore'}
country {'name': 'Panama'}
인덱스로 자식 접근하기
>>> root[0][1].text
'2008'
모두 출력
# 모든 country에 대해
for country in root.iter("country"):
print("=" * 60)
# country의 name attribute 출력
print("Country : ", country.attrib["name"])
# country의 child "rank" 출력
print("Rank : ", country.findtext("rank"))
# country의 child "year" 출력
print("Year : ", country.findtext("year"))
# country의 모든 child "neighbor" 출력
for neighbor in country.iter("neighbor"):
# neighbor의 attribute map 출력
print("Neighbor : ", neighbor.attrib)
XML 편집
ElementTree는 간단하게 XML 문서를 수정하는 방법을 제공한다.
ElementTree.write() 메서드이다.
태그변경 1
>>> for rank in root.iter('rank'):
... new_rank = int(rank.text) + 1
... rank.text = str(new_rank)
... rank.set('updated', 'yes')
...
>>> tree.write('output.xml')
태그 변경2
for rank in root.iter("rank"):
# 기존의 rank값을 정수형으로 변환한 뒤 1 더해서 변수에 대입하고
new_rank = int(rank.text) + 1
# 이를 rank 태그의 text로 갱신
rank.text = str(new_rank)
# 그리고, rank 태그에 {"updated":"yes"} attribute를 추가한다
rank.attrib["updated"] = "yes"
# 위 attribute 추가는 아래과 같이 할 수도 있다.
# rank.set("updated", "yes")
# dump 함수는 인자로 넘어온 tag 이하를 print 해준다
ET.dump(root)
태그 삭제
for country in root.findall('country'):
rank = int(country.find('rank').text)
# rank가 50보다 크면
if rank > 50:
# 해당 태그를 삭제한다
root.remove(country)
# print
ET.dump(root)
태그 추가
import datetime
# 방법 1 : ElementTree.Element + append
# 모든 county에 대해...
for country in root.iter('country'):
e = datetime.datetime.now()
# last_updated 엘리먼트를 만들고
last_updated = ET.Element("last_updated")
# last_updated의 text를 지정한다
last_updated.text = str(e)
# 그리고 last_updated 엘리먼트를 country 태그에 child로 추가한다
country.append(last_updated)
# 방법 2 : ElementTree.SubElement
for country in root.findall('country'):
e = datetime.datetime.now()
# country의 서브 엘리먼트 last_updated를 만들고
last_updated = ET.SubElement(country, "last_updated")
# text를 지정한다
last_updated.text = str(e)
ET.dump(root)
파일로 쓰기 1
>>> for rank in root.iter('rank'):
... new_rank = int(rank.text) + 1
... rank.text = str(new_rank)
... rank.set('updated', 'yes')
...
>>> tree.write('output.xml')
파일로 쓰기 2
import xml.etree.ElementTree as ET
# parse xml file
doc = ET.parse(file_name)
# get root node
root = doc.getroot()
# XML 엘리먼트 수정 또는 삭제 등등
# 첫번째 인자는 출력할 파일명
# encoding : 출력할 xml 파일의 인코딩 지정
# xml_declaration : True
# encoding 지정이 있고 xml_declaration이 True여야만
# xml 선언 헤더인 <?xml version='1.0' encoding='utf-8'?>이 파일에 써진다
doc.write("output.xml", encoding="utf-8", xml_declaration=True)