XML(Extensible Markup Language, 확장 마크업 언어)은 HTML과 비슷한 마크업 언어입니다. 이식성이 뛰어나서 SQL 데이터베이스 없이도 소규모~중간 규모의 데이터를 손쉽게 다룰 수 있다는 장점이 있습니다.
파이썬 표준 라이브러리에는 xml 패키지가 포함되어 있으며, 그 안에 ElementTree 모듈이 들어 있습니다. ElementTree는 단순하면서도 가벼운 XML 프로세서 API입니다.
XML은 트리(tree) 형태의 계층적 데이터 구조입니다. 이 모듈에서 'ElementTree'는 XML 문서 전체를 하나의 트리로 다루고, 'Element' 클래스는 트리를 구성하는 개별 노드를 나타냅니다. XML 파일의 읽기·쓰기 작업은 ElementTree 수준에서 수행되며, 개별 요소와 그 하위 요소(sub-element) 간의 상호작용은 Element 수준에서 처리됩니다.
XML 파일 생성하기
트리는 루트(root)에서 시작해 다른 요소들이 아래로 연결되는 계층 구조입니다. 각 요소는 모듈의 Element() 함수로 생성할 수 있습니다.
import xml.etree.ElementTree as et
e = et.Element('name')
모든 요소는 태그(tag)와 딕셔너리 객체인 attrib 속성으로 정의됩니다. 트리의 시작점인 루트 요소의 attrib는 비어 있는 딕셔너리입니다.
>>> root = et.Element('employees')
>>> root.tag
'employees'
>>> root.attrib
{}
이제 루트 요소 아래에 하나 이상의 자식(child) 요소를 추가할 수 있습니다. 각 자식 요소 역시 하나 이상의 하위 요소를 가질 수 있습니다. 자식 요소는 SubElement() 함수로 추가하고, text 속성으로 내용을 지정합니다.
child = et.Element("employee")
nm = et.SubElement(child, "name")
nm.text = employee.get('name')
age = et.SubElement(child, "salary")
age.text = str(employee.get('salary'))
만든 자식 요소는 append() 함수로 루트에 추가합니다.
root.append(child)
필요한 만큼 자식 요소를 추가했다면 ElementTree() 함수로 트리 객체를 생성합니다.
tree = et.ElementTree(root)
트리 객체의 write() 함수를 호출하면 전체 트리 구조가 바이너리 파일로 저장됩니다.
f = open('employees.xml', "wb")
tree.write(f)
다음 예제는 딕셔너리 리스트로부터 트리를 구성하는 전체 과정을 보여줍니다. 각 딕셔너리 항목은 직원 데이터를 key-value 쌍으로 담고 있으며, 완성된 트리는 'employees.xml' 파일에 기록됩니다.
import xml.etree.ElementTree as et
employees = [{'name':'aaa','age':21,'sal':5000},
{'name':'xyz','age':22,'sal':6000}]
root = et.Element("employees")
for employee in employees:
child = et.Element("employee")
root.append(child)
nm = et.SubElement(child, "name")
nm.text = employee.get('name')
age = et.SubElement(child, "age")
age.text = str(employee.get('age'))
sal = et.SubElement(child, "sal")
sal.text = str(employee.get('sal'))
tree = et.ElementTree(root)
with open('employees.xml', "wb") as fh:
tree.write(fh)
생성된 'employees.xml' 파일은 현재 작업 디렉터리에 저장되며, 내용은 다음과 같습니다.
<employees>
<employee>
<name>aaa</name>
<age>21</age>
<sal>5000</sal>
</employee>
<employee>
<name>xyz</name>
<age>22</age>
<sal>6000</sal>
</employee>
</employees>
XML 파일 파싱하기
이번에는 앞서 만든 'employees.xml' 파일을 다시 읽어 보겠습니다. 이를 위해 ElementTree 모듈의 다음 함수들을 사용합니다.
ElementTree() – 파일의 계층적 요소 구조를 읽어 트리 객체로 만드는 함수입니다.
tree = et.ElementTree(file='employees.xml')
getroot() – 트리의 루트 요소를 반환합니다.
root = tree.getroot()
getchildren() – 한 요소 바로 아래 수준의 하위 요소 목록을 반환합니다. 참고로 이 함수는 Python 3.9부터 제거되었으므로, 최신 버전에서는 요소를 직접 순회하거나 list(root)를 사용하는 것이 좋습니다.
children = list(root)
다음 예제는 'employees.xml'의 요소와 하위 요소를 딕셔너리 리스트로 변환하는 코드입니다.
import xml.etree.ElementTree as et
tree = et.ElementTree(file='employees.xml')
root = tree.getroot()
employees = []
for child in root:
employee = {}
for pair in child:
employee[pair.tag] = pair.text
employees.append(employee)
print(employees)
출력 결과
[{'name': 'aaa', 'age': '21', 'sal': '5000'}, {'name': 'xyz', 'age': '22', 'sal': '6000'}]
XML 파일 수정하기
요소를 수정할 때는 Element의 iter() 함수를 사용합니다. 이 함수는 현재 요소를 루트로 삼아 지정된 태그에 대한 트리 반복자(iterator)를 생성하며, 문서 순서(깊이 우선 방식)로 해당 요소와 그 아래의 모든 요소를 순회합니다.
예를 들어 모든 'sal' 태그에 대한 반복자를 만들고, 각 태그의 값을 100씩 증가시켜 보겠습니다.
import xml.etree.ElementTree as et
tree = et.ElementTree(file='employees.xml')
root = tree.getroot()
for x in root.iter('sal'):
s = int(x.text)
s += 100
x.text = str(s)
with open("employees.xml", "wb") as fh:
tree.write(fh)
실행 후 'employees.xml' 파일을 열어보면 모든 급여 값이 100씩 증가해 수정된 것을 확인할 수 있습니다.
또한 set() 메서드를 사용하면 요소의 특정 속성 값을 업데이트할 수 있습니다.
x.set(marks, str(mark))