KR_XML - somaz94/python-study GitHub Wiki
XML(eXtensible Markup Language)์ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์กฐํํ์ฌ ์ ์ฅํ๊ณ ์ ์กํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋งํฌ์
์ธ์ด์ด๋ค. ์์ฒด ์ค๋ช
์ ์ธ ๊ตฌ์กฐ์ ํ๋ซํผ ๋
๋ฆฝ์ ์ธ ํน์ฑ์ผ๋ก ๋ค์ํ ์์คํ
๊ฐ ๋ฐ์ดํฐ ๊ตํ์ ๋๋ฆฌ ์ฌ์ฉ๋๋ค.
XML์ ์ฃผ์ ํน์ง์ ๋ค์๊ณผ ๊ฐ๋ค:
- ๊ณ์ธต์ ๊ตฌ์กฐ๋ก ๋ณต์กํ ๋ฐ์ดํฐ ํํ ๊ฐ๋ฅ
- ์์ฒด ์ค๋ช ์ ์ธ ํ๊ทธ ์ฌ์ฉ์ผ๋ก ๊ฐ๋ ์ฑ ํฅ์
- ํ์ฅ ๊ฐ๋ฅํ ์ฌ์ฉ์ ์ ์ ํ๊ทธ ์ง์
- ํ
์คํธ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ํ ํ๋ซํผ์์ ํธํ
import xml.etree.ElementTree as ET
# XML ๋ฌธ์์ด ํ์ฑ
xml_string = '''
<root>
<person id="1">
<name>ํ๊ธธ๋</name>
<age>30</age>
<skills>
<skill>Python</skill>
<skill>Java</skill>
</skills>
</person>
<person id="2">
<name>๊น์ฒ ์</name>
<age>25</age>
<skills>
<skill>JavaScript</skill>
</skills>
</person>
</root>
'''
# ๋ฌธ์์ด์์ XML ํ์ฑ
root = ET.fromstring(xml_string)
# ํน์ ์์ ์ ๊ทผ
person = root.find('person')
print(f"์ด๋ฆ: {person.find('name').text}") # ์ด๋ฆ: ํ๊ธธ๋
print(f"๋์ด: {person.find('age').text}") # ๋์ด: 30
# ์์ฑ ์ ๊ทผ
print(f"ID: {person.get('id')}") # ID: 1
# ๋ชจ๋ person ์์ ์ํ
for person in root.findall('person'):
name = person.find('name').text
age = person.find('age').text
print(f"{name}({age}์ธ)")
# ์ค์ฒฉ๋ ์์ ์ ๊ทผ
for person in root.findall('person'):
name = person.find('name').text
skills = [skill.text for skill in person.findall('skills/skill')]
print(f"{name}์ ๊ธฐ์ : {', '.join(skills)}")
โ
ํน์ง:
- ๊ณ์ธต์ ๋ฐ์ดํฐ ๊ตฌ์กฐ ํํ์ ์ ํฉ
- ์ฌ๋์ด ์ฝ๊ณ ์ดํดํ๊ธฐ ์ฌ์ด ํ์
- ์์ฒด ์ค๋ช ์ ์ธ ํ๊ทธ๋ก ๋ฌธ์ ์๋ฏธ ์ ๋ฌ
- ์ ๋์ฝ๋ ์ง์์ผ๋ก ๋ค๊ตญ์ด ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ฐ๋ฅ
- ํ์ฅ์ฑ์ด ๋ฐ์ด๋ ์๋ก์ด ๋ฐ์ดํฐ ์์ ์ถ๊ฐ ์ฉ์ด
- ๋ค์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์ง์ํ๋ ํ์ค ํ์
Python์์๋ xml.etree.ElementTree
๋ชจ๋์ ์ฌ์ฉํ์ฌ XML ํ์ผ์ ์ฝ๊ณ ์ธ ์ ์๋ค. ์ด ๋ชจ๋์ XML์ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ๋ณํํ์ฌ ํจ์จ์ ์ธ ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
XML ํ์ผ ์ฒ๋ฆฌ์ ๊ธฐ๋ณธ ์ํฌํ๋ก์ฐ๋ ๋ค์๊ณผ ๊ฐ๋ค:
- ํ์ผ์์ XML ์ฝ๊ธฐ ๋๋ ์ XML ํธ๋ฆฌ ์์ฑ
- ์์ ๋ฐ ์์ฑ ์กฐ์
- ๋ณ๊ฒฝ์ฌํญ ํ์ผ์ ์ ์ฅ
import xml.etree.ElementTree as ET
import os
# XML ํ์ผ ์ฝ๊ธฐ
def read_xml_file(file_path):
if not os.path.exists(file_path):
print(f"ํ์ผ์ด ์กด์ฌํ์ง ์๋๋ค: {file_path}")
return None
try:
tree = ET.parse(file_path)
root = tree.getroot()
return root
except ET.ParseError as e:
print(f"XML ํ์ฑ ์ค๋ฅ: {e}")
return None
# XML ํ์ผ ์ฐ๊ธฐ
def write_xml_file(root, file_path):
tree = ET.ElementTree(root)
# XML ์ ์ธ๊ณผ ์ธ๋ดํธ ์ค์
tree.write(file_path,
encoding='utf-8',
xml_declaration=True,
method='xml',
short_empty_elements=False)
# ์ฐธ๊ณ : ElementTree๋ ๋ค์ฌ์ฐ๊ธฐ๋ฅผ ์ง์ํ์ง ์์
# ๋ค์ฌ์ฐ๊ธฐ๊ฐ ํ์ํ๋ฉด ์ถ๊ฐ ์ฒ๋ฆฌ ํ์
# ์ XML ๋ฌธ์ ์์ฑ
def create_xml_document():
# ๋ฃจํธ ์์ ์์ฑ
root = ET.Element('library')
# ์ฒซ ๋ฒ์งธ ์ฑ
์ถ๊ฐ
book1 = ET.SubElement(root, 'book')
book1.set('id', '1')
book1.set('available', 'true')
title1 = ET.SubElement(book1, 'title')
title1.text = 'ํ์ด์ฌ ํ๋ก๊ทธ๋๋ฐ'
author1 = ET.SubElement(book1, 'author')
author1.text = 'ํ๊ธธ๋'
year1 = ET.SubElement(book1, 'year')
year1.text = '2023'
# ๋ ๋ฒ์งธ ์ฑ
์ถ๊ฐ
book2 = ET.SubElement(root, 'book')
book2.set('id', '2')
book2.set('available', 'false')
title2 = ET.SubElement(book2, 'title')
title2.text = 'XML ์ฒ๋ฆฌ ๊ธฐ์ด'
author2 = ET.SubElement(book2, 'author')
author2.text = '๊น์ฒ ์'
year2 = ET.SubElement(book2, 'year')
year2.text = '2022'
return root
# ์ฌ์ฉ ์์
if __name__ == "__main__":
# ์ XML ๋ฌธ์ ์์ฑ
library = create_xml_document()
# ํ์ผ๋ก ์ ์ฅ
output_file = 'library.xml'
write_xml_file(library, output_file)
print(f"XML ํ์ผ์ด ์์ฑ๋์๋ค: {output_file}")
# ์ ์ฅ๋ ํ์ผ ๋ค์ ์ฝ๊ธฐ
root = read_xml_file(output_file)
if root is not None:
print(f"๋ฃจํธ ํ๊ทธ: {root.tag}")
print(f"์ฑ
์: {len(root.findall('book'))}")
โ
ํน์ง:
- ํ์ผ ๊ธฐ๋ฐ XML ์ฝ๊ธฐ/์ฐ๊ธฐ ์ง์
- ํธ๋ฆฌ ๊ตฌ์กฐ๋ก XML ๋ฐ์ดํฐ ํํ
- ๋ค์ํ ์ธ์ฝ๋ฉ ์ต์ ์ง์
- ๋์ฉ๋ XML ์ฒ๋ฆฌ ๊ฐ๋ฅ (iterparse ๊ธฐ๋ฅ)
- ์ ์ฐํ ์์ ๋ฐ ์์ฑ ์กฐ์
- ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ํตํ ์์ ์ ์ธ ํ์ผ ์ฒ๋ฆฌ
- XML ์ ์ธ ๋ฐ ์ถ๋ ฅ ํ์ ์ ์ด ๊ฐ๋ฅ
XML ํธ๋ฆฌ ๋ด์ ์์๋ฅผ ์กฐ์ํ๋ ๊ฒ์ XML ์ฒ๋ฆฌ์ ํต์ฌ ๊ธฐ๋ฅ์ด๋ค. Python์ ElementTree ๋ชจ๋์ ์์ ์์ฑ, ์์ , ์ญ์ ๋ฐ ๊ฒ์์ ์ํ ๋ค์ํ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
์์ ์กฐ์์ ์ฃผ์ ์์ ์ ๋ค์๊ณผ ๊ฐ๋ค:
- ์ ์์ ์์ฑ ๋ฐ ํธ๋ฆฌ์ ์ถ๊ฐ
- ์์ฑ ์ค์ ๋ฐ ์์
- ์์ ๋ด์ฉ(ํ ์คํธ) ๋ณ๊ฒฝ
- ํน์ ์์ ๊ฒ์
- ์์ ์ญ์
import xml.etree.ElementTree as ET
# ์์ ์์ฑ๊ณผ ์์ฑ ์ค์
def create_person(name, age, job=None, skills=None):
"""์ฌ๋ ์ ๋ณด๋ฅผ ๋ด์ XML ์์ ์์ฑ"""
person = ET.Element('person')
# ๊ธฐ๋ณธ ์์ฑ ์ถ๊ฐ
person.set('id', str(id(person))[-6:]) # ๊ณ ์ ID ์์ฑ
# ์์ ์์ ์ถ๊ฐ
name_elem = ET.SubElement(person, 'name')
name_elem.text = name
age_elem = ET.SubElement(person, 'age')
age_elem.text = str(age)
# ์ ํ์ ์์ ์ถ๊ฐ
if job:
job_elem = ET.SubElement(person, 'job')
job_elem.text = job
# ์ค์ฒฉ ์์ ๊ตฌ์กฐ ์์ฑ
if skills and len(skills) > 0:
skills_elem = ET.SubElement(person, 'skills')
for skill in skills:
skill_elem = ET.SubElement(skills_elem, 'skill')
skill_elem.text = skill
return person
# ์์ ๊ฒ์ ๋ฐ ์์
def modify_person(person_elem, new_age=None, new_job=None):
"""๊ธฐ์กด person ์์ ์์ """
if new_age:
age_elem = person_elem.find('age')
if age_elem is not None:
age_elem.text = str(new_age)
else:
age_elem = ET.SubElement(person_elem, 'age')
age_elem.text = str(new_age)
if new_job:
job_elem = person_elem.find('job')
if job_elem is not None:
job_elem.text = new_job
else:
job_elem = ET.SubElement(person_elem, 'job')
job_elem.text = new_job
# ์์ ์ญ์
def remove_skill(person_elem, skill_name):
"""ํน์ ์คํฌ ์์ ์ญ์ """
skills_elem = person_elem.find('skills')
if skills_elem is not None:
for skill_elem in skills_elem.findall('skill'):
if skill_elem.text == skill_name:
skills_elem.remove(skill_elem)
return True
return False
# ์์ ๊ฒ์ ๋ฐ ํํฐ๋ง
def find_people_by_criteria(root, min_age=None, job=None, skill=None):
"""์กฐ๊ฑด์ ๋ง๋ ์ฌ๋ ์์ ์ฐพ๊ธฐ"""
results = []
for person in root.findall('person'):
# ๋์ด ์กฐ๊ฑด ํ์ธ
if min_age is not None:
age_elem = person.find('age')
if age_elem is None or int(age_elem.text) < min_age:
continue
# ์ง์
์กฐ๊ฑด ํ์ธ
if job is not None:
job_elem = person.find('job')
if job_elem is None or job_elem.text != job:
continue
# ๊ธฐ์ ์กฐ๊ฑด ํ์ธ
if skill is not None:
skills_found = False
for skill_elem in person.findall('.//skill'):
if skill_elem.text == skill:
skills_found = True
break
if not skills_found:
continue
# ๋ชจ๋ ์กฐ๊ฑด ๋ง์กฑ
results.append(person)
return results
# ์ฌ์ฉ ์์
if __name__ == "__main__":
# ๋ฃจํธ ์์ ์์ฑ
root = ET.Element('people')
# ์ฌ๋ ์์ ์ถ๊ฐ
p1 = create_person('ํ๊ธธ๋', 30, 'developer', ['Python', 'JavaScript'])
p2 = create_person('๊น์ฒ ์', 25, 'designer', ['Photoshop'])
p3 = create_person('์ด์ํฌ', 35, 'manager')
root.append(p1)
root.append(p2)
root.append(p3)
# ์์ ์์
modify_person(p1, new_age=31, new_job='senior developer')
# ์์ ์ญ์
remove_skill(p1, 'JavaScript')
# ์กฐ๊ฑด ๊ฒ์
developers = find_people_by_criteria(root, job='senior developer')
for dev in developers:
print(f"๊ฐ๋ฐ์: {dev.find('name').text}, ๋์ด: {dev.find('age').text}")
โ
ํน์ง:
- ์ง๊ด์ ์ธ ์์ ์์ฑ ๋ฐ ํธ๋ฆฌ ๊ตฌ์ฑ
- ์์ฑ ๊ด๋ฆฌ๋ฅผ ์ํ ๊ฐ๋จํ ๋ฉ์๋
- ์ ์ฐํ ์์ ๋ด์ฉ ์์
- ๋ค์ํ ์กฐ๊ฑด์ผ๋ก ์์ ๊ฒ์ ๊ฐ๋ฅ
- ํธ๋ฆฌ ๊ตฌ์กฐ ์ํ ๋ฐ ์กฐ์ ๊ธฐ๋ฅ
- ์์ ๊ฐ ๊ด๊ณ(๋ถ๋ชจ-์์) ์ ๊ทผ ์ง์
- ์ค์ฒฉ๋ ๋ณต์กํ XML ๊ตฌ์กฐ ์ฒ๋ฆฌ ๋ฅ๋ ฅ
XPath๋ XML ๋ฌธ์ ๋ด์์ ์์์ ์์ฑ์ ์ฐพ๊ธฐ ์ํ ๊ฐ๋ ฅํ ์ฟผ๋ฆฌ ์ธ์ด์ด๋ค. ElementTree๋ ์ ํ๋ XPath ๊ตฌ๋ฌธ์ ์ง์ํ๋ฉฐ, ๋ ์์ ํ ์ง์์ ์ํด์๋ lxml
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
XPath๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์์ ์ด ๊ฐ๋ฅํ๋ค:
- ํน์ ๊ฒฝ๋ก์ ์์ ์ ํ
- ์กฐ๊ฑด์ ๋ฐ๋ฅธ ์์ ํํฐ๋ง
- ์ฌ๋ฌ ๊ฒฝ๋ก๋ฅผ ํตํ ์์ ์ ๊ทผ
- ๋ณต์กํ ๊ณ์ธต ๊ตฌ์กฐ ํ์
import xml.etree.ElementTree as ET
from lxml import etree # ์์ ํ XPath ์ง์์ ์ํด
# ์ํ XML ๋ฐ์ดํฐ
xml_data = '''
<company>
<department name="๊ฐ๋ฐํ">
<employee id="1001" type="์ ๊ท์ง">
<name>ํ๊ธธ๋</name>
<position>์ ์ ๊ฐ๋ฐ์</position>
<salary currency="KRW">70000000</salary>
<skills>
<skill level="expert">Python</skill>
<skill level="intermediate">Java</skill>
</skills>
</employee>
<employee id="1002" type="๊ณ์ฝ์ง">
<name>๊น์ฒ ์</name>
<position>์ฃผ๋์ด ๊ฐ๋ฐ์</position>
<salary currency="KRW">50000000</salary>
<skills>
<skill level="intermediate">JavaScript</skill>
<skill level="beginner">Python</skill>
</skills>
</employee>
</department>
<department name="๋์์ธํ">
<employee id="2001" type="์ ๊ท์ง">
<name>์ด์ํฌ</name>
<position>UI ๋์์ด๋</position>
<salary currency="KRW">65000000</salary>
</employee>
</department>
</company>
'''
# ElementTree์ lxml ๋ชจ๋๋ก ํ์ฑ
root_et = ET.fromstring(xml_data)
root_lxml = etree.fromstring(xml_data.encode('utf-8'))
def xpath_examples_elementtree():
"""ElementTree์ ์ ํ๋ XPath ๊ธฐ๋ฅ ์์"""
print("=== ElementTree XPath ์์ ===")
# 1. ๋ชจ๋ employee ์์ ์ฐพ๊ธฐ
employees = root_et.findall('.//employee')
print(f"์ง์ ์: {len(employees)}")
# 2. ํน์ ์์ฑ์ ๊ฐ์ง ์์ ์ฐพ๊ธฐ
regular_employees = root_et.findall(".//employee[@type='์ ๊ท์ง']")
print(f"์ ๊ท์ง ์ง์ ์: {len(regular_employees)}")
# 3. ํน์ ๋ถ์์ ์ง์ ์ฐพ๊ธฐ
dev_employees = root_et.findall("./department[@name='๊ฐ๋ฐํ']/employee")
print(f"๊ฐ๋ฐํ ์ง์ ์: {len(dev_employees)}")
# 4. ๋ชจ๋ ์คํฌ ์ฐพ๊ธฐ
skills = root_et.findall('.//skill')
print(f"์ด ์คํฌ ์: {len(skills)}")
# 5. ์ด๋ฆ์ผ๋ก ์ง์ ์ฐพ๊ธฐ
for emp in employees:
if emp.find('name').text == 'ํ๊ธธ๋':
print(f"ํ๊ธธ๋์ ์ง๊ธ: {emp.find('position').text}")
def xpath_examples_lxml():
"""lxml์ ์์ ํ XPath ๊ธฐ๋ฅ ์์"""
print("\n=== lxml XPath ์์ ===")
# 1. ๊ธ์ฌ๊ฐ 6์ฒ๋ง์ ์ด์์ธ ์ง์ ์ฐพ๊ธฐ (ElementTree์์๋ ๋ถ๊ฐ๋ฅ)
high_salary = root_lxml.xpath(".//employee[number(salary) >= 60000000]")
print(f"๊ณ ์ก์ฐ๋ด ์ง์ ์: {len(high_salary)}")
for emp in high_salary:
name = emp.xpath("./name/text()")[0]
salary = emp.xpath("./salary/text()")[0]
print(f"- {name}: {salary}์")
# 2. Python ๊ธฐ์ ์ ๊ฐ์ง ์ง์ ์ฐพ๊ธฐ
python_devs = root_lxml.xpath(".//employee[.//skill='Python']")
print(f"\nPython ๊ฐ๋ฐ์ ์: {len(python_devs)}")
for emp in python_devs:
name = emp.xpath("./name/text()")[0]
level = emp.xpath(".//skill[text()='Python']/@level")[0]
print(f"- {name} (๋ ๋ฒจ: {level})")
# 3. ์ง์ ID์ ์ด๋ฆ ํจ๊ป ๊ฐ์ ธ์ค๊ธฐ
print("\n์ง์ ๋ชฉ๋ก:")
for emp in root_lxml.xpath(".//employee"):
emp_id = emp.get('id')
name = emp.xpath("./name/text()")[0]
position = emp.xpath("./position/text()")[0]
print(f"- [{emp_id}] {name} ({position})")
# 4. ์ง๊ธ์ '์ ์' ๋๋ '์๋์ด'๊ฐ ํฌํจ๋ ์ง์ ์ฐพ๊ธฐ
senior_staff = root_lxml.xpath(".//employee[contains(position, '์ ์') or contains(position, '์๋์ด')]")
print(f"\n์ ์๊ธ ์ง์ ์: {len(senior_staff)}")
for emp in senior_staff:
print(f"- {emp.xpath('./name/text()')[0]}")
# 5. ํน์ ๋ถ์์ ํ๊ท ๊ธ์ฌ ๊ณ์ฐ
dev_salaries = root_lxml.xpath(".//department[@name='๊ฐ๋ฐํ']/employee/salary/text()")
if dev_salaries:
avg_salary = sum(float(salary) for salary in dev_salaries) / len(dev_salaries)
print(f"\n๊ฐ๋ฐํ ํ๊ท ๊ธ์ฌ: {avg_salary:,.0f}์")
# ์คํ
xpath_examples_elementtree()
xpath_examples_lxml()
โ
ํน์ง:
- ๊ฐ๋ ฅํ XML ์ฟผ๋ฆฌ ๊ธฐ๋ฅ ์ ๊ณต
- ๋ณต์กํ ์กฐ๊ฑด์์ผ๋ก ์์ ํํฐ๋ง
- ๊ฒฝ๋ก ๊ธฐ๋ฐ์ ์ง๊ด์ ์ธ ์์ ์ ๊ทผ
- ๊ณ์ธต ๊ตฌ์กฐ ํ์์ ์ํ ๋ค์ํ ์ฐ์ฐ์
- ์์ฑ ๊ฐ ๊ธฐ๋ฐ ๊ฒ์ ์ง์
- ElementTree๋ ๊ธฐ๋ณธ์ ์ธ XPath๋ฅผ ์ง์
- lxml์ ์์ ํ XPath 1.0/2.0 ์ง์
XML ์คํค๋ง๋ XML ๋ฌธ์์ ๊ตฌ์กฐ์ ๋ด์ฉ์ ์ ์ํ๋ ๋ฐฉ๋ฒ์ด๋ค. XML ๋ฌธ์๊ฐ ์คํค๋ง์ ์ ์๋ ๊ท์น์ ์ค์ํ๋์ง ๊ฒ์ฆํจ์ผ๋ก์จ ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ ์ ์๋ค.
XML ์คํค๋ง ๊ฒ์ฆ์ ์ฃผ์ ์ด์ ์ ๋ค์๊ณผ ๊ฐ๋ค:
- ๋ฌธ์ ๊ตฌ์กฐ์ ์ ํจ์ฑ ๊ฒ์ฌ
- ๋ฐ์ดํฐ ํ์ ๊ฒ์ฆ
- ํ์ ์์ ๋ฐ ์์ฑ ํ์ธ
- ๋น์ฆ๋์ค ๊ท์น ์ ์ฉ
import xmlschema
from lxml import etree
import os
# XSD ํ์ผ์ ์ฌ์ฉํ ๊ฒ์ฆ
def validate_with_xmlschema(xml_file, xsd_file):
"""xmlschema ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ XML ๊ฒ์ฆ"""
try:
schema = xmlschema.XMLSchema(xsd_file)
is_valid = schema.is_valid(xml_file)
if is_valid:
print(f"XML ํ์ผ์ด ์คํค๋ง์ ์ ํจํ๋ค: {xml_file}")
else:
print(f"XML ํ์ผ์ด ์คํค๋ง์ ์ ํจํ์ง ์๋ค: {xml_file}")
# ์์ธ ์ค๋ฅ ์ ๋ณด
validator = schema.iter_errors(xml_file)
for error in validator:
print(f"- ์ค๋ฅ: {error}")
return is_valid
except Exception as e:
print(f"๊ฒ์ฆ ๊ณผ์ ์์ ์ค๋ฅ ๋ฐ์: {e}")
return False
# lxml์ ์ฌ์ฉํ ๊ฒ์ฆ
def validate_with_lxml(xml_file, xsd_file):
"""lxml ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ XML ๊ฒ์ฆ"""
try:
# ์คํค๋ง ๋ก๋
xmlschema_doc = etree.parse(xsd_file)
xmlschema = etree.XMLSchema(xmlschema_doc)
# XML ํ์ผ ๋ก๋
xml_doc = etree.parse(xml_file)
# ๊ฒ์ฆ
is_valid = xmlschema.validate(xml_doc)
if is_valid:
print(f"XML ํ์ผ์ด ์คํค๋ง์ ์ ํจํ๋ค: {xml_file}")
else:
print(f"XML ํ์ผ์ด ์คํค๋ง์ ์ ํจํ์ง ์๋ค: {xml_file}")
# ์์ธ ์ค๋ฅ ์ ๋ณด
log = xmlschema.error_log
for error in log:
print(f"- ์ค {error.line}, ์ด {error.column}: {error.message}")
return is_valid
except Exception as e:
print(f"๊ฒ์ฆ ๊ณผ์ ์์ ์ค๋ฅ ๋ฐ์: {e}")
return False
# XML ์คํค๋ง(XSD) ์์
def create_sample_xsd():
"""์ํ XSD ํ์ผ ์์ฑ"""
xsd_content = '''<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="employees">
<xs:complexType>
<xs:sequence>
<xs:element name="employee" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:positiveInteger"/>
<xs:element name="department" type="xs:string"/>
<xs:element name="position" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
'''
with open("employees.xsd", "w", encoding="utf-8") as f:
f.write(xsd_content)
return "employees.xsd"
# ์ ํจํ XML ์์
def create_valid_xml():
"""XSD์ ์ ํจํ XML ํ์ผ ์์ฑ"""
xml_content = '''<?xml version="1.0" encoding="UTF-8"?>
<employees>
<employee id="e1">
<name>ํ๊ธธ๋</name>
<age>30</age>
<department>๊ฐ๋ฐ</department>
<position>์ ์ ๊ฐ๋ฐ์</position>
</employee>
<employee id="e2">
<name>๊น์ฒ ์</name>
<age>25</age>
<department>๋์์ธ</department>
</employee>
</employees>
'''
with open("valid_employees.xml", "w", encoding="utf-8") as f:
f.write(xml_content)
return "valid_employees.xml"
# ์ ํจํ์ง ์์ XML ์์
def create_invalid_xml():
"""XSD์ ์ ํจํ์ง ์์ XML ํ์ผ ์์ฑ"""
xml_content = '''<?xml version="1.0" encoding="UTF-8"?>
<employees>
<employee id="e1">
<name>ํ๊ธธ๋</name>
<age>-30</age> <!-- ์์๋ positiveInteger์ ์ ํจํ์ง ์์ -->
<department>๊ฐ๋ฐ</department>
</employee>
<employee> <!-- id ์์ฑ ๋๋ฝ -->
<name>๊น์ฒ ์</name>
<age>25</age>
<!-- department ์์ ๋๋ฝ -->
</employee>
</employees>
'''
with open("invalid_employees.xml", "w", encoding="utf-8") as f:
f.write(xml_content)
return "invalid_employees.xml"
# ์คํ ์์
if __name__ == "__main__":
# ์ํ ํ์ผ ์์ฑ
xsd_file = create_sample_xsd()
valid_xml = create_valid_xml()
invalid_xml = create_invalid_xml()
print("xmlschema ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊ฒ์ฆ:")
validate_with_xmlschema(valid_xml, xsd_file)
validate_with_xmlschema(invalid_xml, xsd_file)
print("\nlxml ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊ฒ์ฆ:")
validate_with_lxml(valid_xml, xsd_file)
validate_with_lxml(invalid_xml, xsd_file)
# ์์ ํ์ผ ์ ๋ฆฌ
for file in [xsd_file, valid_xml, invalid_xml]:
if os.path.exists(file):
os.remove(file)
โ
ํน์ง:
- XML ์คํค๋ง(XSD)๋ฅผ ์ฌ์ฉํ ๊ตฌ์กฐ ๊ฒ์ฆ
- ๋ฐ์ดํฐ ํ์ ๋ฐ ์ ์ฝ์กฐ๊ฑด ์ ์ฉ ๊ฐ๋ฅ
- ์ค๋ฅ ์์น ๋ฐ ์์ธ ์์ธ ๋ณด๊ณ
- ๋ณต์กํ ๋น์ฆ๋์ค ๊ท์น ์ ์ฉ ๊ฐ๋ฅ
- DTD, RelaxNG ๋ฑ ๋ค์ํ ์คํค๋ง ์ธ์ด ์ง์
- ๋์ฉ๋ XML ํ์ผ ์ ์ง์ ๊ฒ์ฆ ์ง์
- API ํตํฉ ๋ฐ ๋ฐ์ดํฐ ๊ตํ ์ ์ ๋ขฐ์ฑ ๋ณด์ฅ
XML ๋ฐ์ดํฐ๋ ์ข
์ข
๋ค๋ฅธ ํ์์ผ๋ก ๋ณํํ๊ฑฐ๋ ์ฒ๋ฆฌ ์ฑ๋ฅ์ ์ต์ ํํด์ผ ํ๋ค. Python์์๋ ๋ค์ํ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ XML์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ๋ค๋ฅธ ํ์์ผ๋ก ๋ณํํ ์ ์๋ค.
์ฃผ์ XML ๋ณํ ๋ฐ ์ต์ ํ ๊ธฐ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค:
- XML์์ ๋ค๋ฅธ ํ์(JSON, CSV ๋ฑ)์ผ๋ก ๋ณํ
- XSLT๋ฅผ ์ฌ์ฉํ XML ๋ณํ
- ๋์ฉ๋ XML ํ์ผ์ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ฒ๋ฆฌ
- ๋ฐ์ดํฐ ์์ถ ๋ฐ ์ต์ ํ
import xml.etree.ElementTree as ET
import json
import csv
from lxml import etree
import io
# XML์ JSON์ผ๋ก ๋ณํ
def xml_to_json(xml_data, root_name='root'):
"""XML ๋ฐ์ดํฐ๋ฅผ JSON ํ์์ผ๋ก ๋ณํ"""
def _element_to_dict(element):
result = {}
# ์์ฑ ์ฒ๋ฆฌ
for key, value in element.attrib.items():
result[f"@{key}"] = value
# ์์ ์์ ์ฒ๋ฆฌ
children = list(element)
if len(children) == 0:
# ํ
์คํธ๋ง ์๋ ๊ฒฝ์ฐ
if element.text and element.text.strip():
if len(result) == 0:
return element.text.strip()
result['#text'] = element.text.strip()
else:
# ์์ ์์๋ค ์ฒ๋ฆฌ
child_elements = {}
for child in children:
child_dict = _element_to_dict(child)
tag = child.tag
if tag in child_elements:
# ๋์ผ ํ๊ทธ๊ฐ ์ด๋ฏธ ์กด์ฌํ๋ฉด ๋ฆฌ์คํธ๋ก ๋ณํ
if not isinstance(child_elements[tag], list):
child_elements[tag] = [child_elements[tag]]
child_elements[tag].append(child_dict)
else:
child_elements[tag] = child_dict
result.update(child_elements)
return result
# XML ํ์ฑ
root = ET.fromstring(xml_data) if isinstance(xml_data, str) else xml_data
return {root.tag: _element_to_dict(root)}
# XML์ CSV๋ก ๋ณํ
def xml_to_csv(xml_data, mapping, output_file):
"""XML ๋ฐ์ดํฐ๋ฅผ CSV๋ก ๋ณํ"""
root = ET.fromstring(xml_data) if isinstance(xml_data, str) else xml_data
items = root.findall(mapping['row_xpath'])
# CSV ํ์ผ ์์ฑ
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=mapping['headers'])
writer.writeheader()
for item in items:
row = {}
for csv_col, xpath in mapping['columns'].items():
element = item.find(xpath)
if element is not None and element.text:
row[csv_col] = element.text.strip()
else:
row[csv_col] = ''
writer.writerow(row)
# XSLT๋ฅผ ์ฌ์ฉํ XML ๋ณํ
def transform_with_xslt(xml_data, xslt_data):
"""XSLT๋ฅผ ์ฌ์ฉํ์ฌ XML ๋ณํ"""
# XML ๋ฐ XSLT ํ์ฑ
xml_doc = etree.parse(io.StringIO(xml_data)) if isinstance(xml_data, str) else xml_data
xslt_doc = etree.parse(io.StringIO(xslt_data)) if isinstance(xslt_data, str) else xslt_data
# XSLT ๋ณํ๊ธฐ ์์ฑ
transform = etree.XSLT(xslt_doc)
# ๋ณํ ์คํ
result = transform(xml_doc)
return etree.tostring(result, pretty_print=True, encoding='utf-8').decode('utf-8')
# ๋์ฉ๋ XML ํ์ผ ์ฒ๋ฆฌ (์ดํฐ๋ ์ดํฐ ํจํด)
def process_large_xml(xml_file, element_tag, callback):
"""๋์ฉ๋ XML ํ์ผ์ ํน์ ์์๋ฅผ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ"""
# ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ์ ์ฌ์ฉ
context = etree.iterparse(xml_file, events=('end',), tag=element_tag)
count = 0
try:
for event, elem in context:
# ์ฝ๋ฐฑ ํจ์๋ก ์์ ์ฒ๋ฆฌ
callback(elem)
count += 1
# ์ฒ๋ฆฌ ํ ๋ฉ๋ชจ๋ฆฌ ํด์
elem.clear()
# ๋ถ๋ชจ ์์์ ์ฐธ์กฐ๋ ์ ๊ฑฐ
while elem.getprevious() is not None:
del elem.getparent()[0]
except Exception as e:
print(f"์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {e}")
return count
# ์ํ XML ๋ฐ์ดํฐ
sample_xml = '''
<employees>
<employee id="1001" department="๊ฐ๋ฐ">
<name>ํ๊ธธ๋</name>
<position>์ ์ ๊ฐ๋ฐ์</position>
<skills>
<skill>Python</skill>
<skill>XML</skill>
</skills>
<projects>
<project>
<name>๋ฐ์ดํฐ ๋ถ์ ์์คํ
</name>
<duration>6๊ฐ์</duration>
</project>
<project>
<name>์น ์๋น์ค ๊ฐ๋ฐ</name>
<duration>3๊ฐ์</duration>
</project>
</projects>
</employee>
<employee id="1002" department="๋ง์ผํ
">
<name>๊น์ฒ ์</name>
<position>๋ง์ผํ
๋งค๋์ </position>
<skills>
<skill>๋ฐ์ดํฐ ๋ถ์</skill>
<skill>์์
๋ฏธ๋์ด</skill>
</skills>
</employee>
</employees>
'''
# ์ํ XSLT ๋ณํ
sample_xslt = '''
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>์ง์ ๋ชฉ๋ก</title>
</head>
<body>
<h1>์ง์ ๋ช
๋จ</h1>
<table border="1">
<tr>
<th>ID</th>
<th>์ด๋ฆ</th>
<th>๋ถ์</th>
<th>์ง์ฑ
</th>
<th>์คํฌ</th>
</tr>
<xsl:for-each select="//employee">
<tr>
<td><xsl:value-of select="@id"/></td>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="@department"/></td>
<td><xsl:value-of select="position"/></td>
<td>
<xsl:for-each select="skills/skill">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
'''
# ์คํ ์์
if __name__ == "__main__":
# XML์ JSON์ผ๋ก ๋ณํ
json_data = xml_to_json(sample_xml)
print("JSON ๋ณํ ๊ฒฐ๊ณผ:")
print(json.dumps(json_data, indent=2, ensure_ascii=False))
# XML์ CSV๋ก ๋ณํ
mapping = {
'row_xpath': './/employee',
'headers': ['ID', '์ด๋ฆ', '๋ถ์', '์ง์ฑ
', '์คํฌ'],
'columns': {
'ID': '@id',
'์ด๋ฆ': 'name',
'๋ถ์': '@department',
'์ง์ฑ
': 'position',
'์คํฌ': 'skills' # ์ด ๋ถ๋ถ์ ๋ณ๋ ์ฒ๋ฆฌ ํ์ (๊ฐ๋จํ ์์)
}
}
# XML์ XSLT๋ก HTML๋ก ๋ณํ
html_output = transform_with_xslt(sample_xml, sample_xslt)
print("\nXSLT ๋ณํ ๊ฒฐ๊ณผ:")
print(html_output[:500] + "...") # ๊ฒฐ๊ณผ์ ์ผ๋ถ๋ง ์ถ๋ ฅ
โ
ํน์ง:
- ๋ค์ํ ๋ฐ์ดํฐ ํ์์ผ๋ก ์ ์ฐํ ๋ณํ
- XSLT๋ฅผ ํตํ ๊ฐ๋ ฅํ ๋ณํ ๋ฐ ์คํ์ผ๋ง
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ๋์ฉ๋ XML ์ฒ๋ฆฌ
- ์คํธ๋ฆฌ๋ฐ ํ์๋ฅผ ํตํ ์ ์ง์ ์ฒ๋ฆฌ
- ๋ณต์กํ XML ๊ตฌ์กฐ์ ๋จ์ํ
- ๋ฐ์ดํฐ ๋ง์ด๊ทธ๋ ์ด์ ๋ฐ ํตํฉ ์ง์
- ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ๋ค์ํ ๊ธฐ๋ฒ ์ ์ฉ
โ
๋ชจ๋ฒ ์ฌ๋ก:
-
์ ์ ํ ํ์ ์ ํ: ์์ XML ํ์ผ์๋ ElementTree, ๋ณต์กํ ์์ ์ด๋ ๋์ฉ๋ ํ์ผ์๋ lxml์ ์ฌ์ฉํ์.
-
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ: ๋์ฉ๋ XML ์ฒ๋ฆฌ ์
iterparse
๋๋ SAX ํ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ์. -
์ ์ ํ ์ธ์ฝ๋ฉ ์ง์ : XML ํ์ผ ์์ฑ ์ ํญ์ ๋ช ์์ ์ผ๋ก ์ธ์ฝ๋ฉ(๋ณดํต UTF-8)์ ์ง์ ํ์.
-
XPath ํ์ฉ: ๋ณต์กํ ์์ ๊ฒ์ ์ XPath๋ฅผ ํ์ฉํ์ฌ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์ ์งํ์.
-
์๋ฌ ์ฒ๋ฆฌ ๊ตฌํ: XML ํ์ฑ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฌ์ฐ๋ฏ๋ก, ํญ์ ์ ์ ํ ์์ธ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ์.
-
์คํค๋ง ๊ฒ์ฆ ํ์ฉ: ์ค์ํ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋๋ ์คํค๋ง ๊ฒ์ฆ์ ํตํด ๋ฐ์ดํฐ ์ ํจ์ฑ์ ๋ณด์ฅํ์.
-
๋ค์์คํ์ด์ค ์ฒ๋ฆฌ: XML ๋ค์์คํ์ด์ค๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ์ฌ ์ถฉ๋์ ๋ฐฉ์งํ๊ณ ํ์ค์ ์ค์ํ์.
-
๋ณด์ ์ทจ์ฝ์ ์ฃผ์: ์ธ๋ถ ์ํฐํฐ ์ฐธ์กฐ(XXE)์ ๊ฐ์ XML ํ์ฑ ๊ด๋ จ ๋ณด์ ์ทจ์ฝ์ ์ ์ฃผ์ํ์.
-
์ฑ๋ฅ ์ต์ ํ: ์์ฃผ ์ ๊ทผํ๋ ์์๋ ์บ์ฑํ๊ณ , ํ์ํ ๋ถ๋ถ๋ง ํ์ฑํ์ฌ ์ฒ๋ฆฌ ์๋๋ฅผ ํฅ์์ํค์.
-
์ฝ๋ ๊ฐ๋ ์ฑ ์ ์ง: XML ์ฒ๋ฆฌ ๋ก์ง์ ๋ชจ๋ํํ๊ณ ๋ช ํํ ํจ์๋ช ๊ณผ ์ฃผ์์ ์ฌ์ฉํ์ฌ ์ ์ง๋ณด์์ฑ์ ๋์ด์.