프로그래밍

Python XML Read, Save, Change (읽고, 쓰고, 값 변경하기)

Rudi 2022. 6. 21. 15:08

1. Introduction

필자는 XML 에 대해서 자세히 모르며, 알고 싶지도 않았지만, XML의 값을 읽고 일부값을 변형시켜서 저장시키는 코드를 만들어야 했습니다. 결론적으로 root.iter() 함수를 통해서, tag 와 attribute 에 접근할 수 있으며, 일부값만 변경하고 다시 저장할 수 있다는 것을 알게 되었습니다. XML 구조에 대해서 알고 있다면 Section 2를 건너뛰고 Section 3 :Read and SaveSection 4: Attribution change 코드를 사용하시면 됩니다.

 

2. XML 구조

XML 은 쉽게 말해서 Tag 와 Attribution으로 이루어져 있다.

  • Tag: Object 이름인 것 같습니다. 하나의 object는 단 하나의 Tag만 가지는 듯합니다.
  • Attribution: Tag가 가지는 여러 개의 값들입니다. 이 값들은 임의로 바꿔줄 수 있습니다. 

아래의 예시 Libraries Tag 안에, library -> book Tag들이 있습니다다.  각 태그에는 여러 개의 attribution을 줄 수 있습니다.

<?xml version='1.0' encoding='utf-8'?>
<libraries>
  <library name="lib1" location="seoul">
    <book name="book1" price="10.0" author="Bumjin"/>
    <book name="book2" price="20.0" author="Apple"/>
    <book name="book3" price="40.0" author="Bumjin"/>
  </library>
  <library name="lib1" location="busan">
    <book name="book2" price="10.0" author="Bumjin"/>
    <book name="book3" price="40.0" author="Apple"/>
    <book name="book4" price="10.0" author="Siso"/>
  </library>
</libraries>

3. 읽고 저장하기 

READ

XML 을 읽기 위해서는 `xml` 의 ElementTree를 import 해야 합니다. ET는 파일을 분석하고, XML 의 root의 값을 가져와서 사용하면 됩니다. 위의 예시에서는 root가 libraries에 해당합니다. 

import xml.etree.ElementTree as ET
xml_path = "src/library.xml" # change!
with open(xml_path) as f :
    tree = ET.parse(f)
    root = tree.getroot()

SAVE

값을 저장하기 위해서는 위에서 정의한 tree에서 tree.write() 함수를 이용하면 된니다. 다음과 같이 path를 주면 저장이 됩니다. 

import xml.etree.ElementTree as ET
xml_save_path = "src/library.xml" # change!
with open(xml_save_path, "wb") as file:
    tree.write(file, encoding='utf-8', xml_declaration=True)

4.  Attribute 값 변경하기 후 저장하기 

XML 은 Tree 구조로 이루어져 있으며, Recursive 하게 모든 태그들을 사용할 수 있습니다. root.iter() 함수가 이 역할을 해줍니다. iter() 에 변수명으로 Tag를 줄 수 있으며, 또한 원하는 Tag를 넣으면 해당 태그만 순환합니다.  아래 코드는 root 하위에 있는 모든 book 태그를 반환해줍니다. 이로부터 book에 있는 price의 attribution 값을 10배로 키우는 예시입니다. 

# root 는 위에서 정의된 root
tag = "book"
for child in root.iter(tag):
    # Do somthing here
    # Change Attribution
    child.attrib['price'] = str(10 * float(child.attrib['price'] ))
    print(child, child.attrib['price'], "-->", str(10 * float(child.attrib['price'] )))

 

+ 모든 태그에 대해서 name attribution 의 값이 포함된 태그만 바꾸기.  (내가 원했던 것)

아래 코드는 root 하위의 태그 중에서 author attribution 값이 Bumjin 인 태그의 Price를 10배로 키웁니다.

for child in root.iter():       # for all iterative children
    if attrib in child.attrib:
        # Do somthing here
        # Change Attribution
        if child.attrib['author'] == "Bumjin":
            child.attrib['price'] = str(10 * float(child.attrib['price'] ))
            print(child, child.attrib['price'], "-->", str(10 * float(child.attrib['price'] )))

전체 코드 

코드를 돌리기 전에, 위의 xml 을 저장하고 돌리시면 됩니다.

import os
import numpy as np 
import xml.etree.ElementTree as ET

def modify_xml_by_tag(xml_path, xml_save_path, tag):
    # Read
    with open(xml_path) as f :
        tree = ET.parse(f)
        root = tree.getroot()

    for child in root.iter(tag):
        # Do somthing here
        # Change Attribution
        child.attrib['price'] = str(10 * float(child.attrib['price'] ))
        print(child, child.attrib['price'], "-->", str(10 * float(child.attrib['price'] )))

    # Save
    with open(xml_save_path, "wb") as file:
        tree.write(file, encoding='utf-8', xml_declaration=True)


def modify_xml_by_attrib(xml_path, xml_save_path, attrib):
    # Read
    with open(xml_path) as f :
        tree = ET.parse(f)
        root = tree.getroot()

    for child in root.iter():       # for all iterative children
        if attrib in child.attrib:
            # Do somthing here
            # Change Attribution
            if child.attrib['author'] == "Bumjin":
                child.attrib['price'] = str(10 * float(child.attrib['price'] ))
                print(child, child.attrib['price'], "-->", str(10 * float(child.attrib['price'] )))

    # Save
    with open(xml_save_path, "wb") as file:
        tree.write(file, encoding='utf-8', xml_declaration=True)


print("--- Find Recursively All by Tags--------")
modify_xml_by_tag("xml_test.xml", "xml_modified_by_tag.xml", tag="book")
print("--- Find Recursively All by Attrib--------")
modify_xml_by_attrib("xml_test.xml", "xml_modified_by_attrib.xml", attrib="author")