확장 가능한 마크업 언어(XML)는 구조화된 데이터를 저장하고 교환하기 위해 널리 사용되는 형식입니다. XML 파일은 일반적으로 구성 파일, 데이터 교환 형식, 웹 서비스 응답, 웹 사이트맵과 같은 계층적 데이터를 표현하는 데 사용됩니다.
Python에서 XML 파일을파싱하는 것은 웹 API에서 가져온 데이터 처리나 웹 스크래핑과 같은 수동 프로세스를 자동화하는 데 특히 흔한 작업입니다.
이 글에서는 ElementTree 모듈, lxml 라이브러리, minidom, Simple API for XML(SAX), untangle 등 Python에서 XML을 파싱하는 데 사용할 수 있는 라이브러리에 대해 알아봅니다.
XML 파일의 핵심 개념
파이썬에서 XML을 파싱하는 방법을 배우기 전에, XML 스키마 정의(XSD)가 무엇이며 XML 파일을 구성하는 요소가 무엇인지 이해해야 합니다. 이러한 이해는 파싱 작업에 적합한 파이썬 라이브러리를 선택하는 데 도움이 될 수 있습니다.
XSD는 XML 문서에서 허용되는 구조, 내용 및 데이터 유형을 정의하는 스키마 사양입니다. 이는 미리 정의된 규칙 세트에 따라 XML 파일의 구조와 내용을 검증하는 구문 역할을 합니다.
XML 파일은 일반적으로 네임스페이스, 루트, 속성, 요소 및 텍스트 콘텐츠 요소를 포함하며, 이들이 함께 구조화된 데이터를 나타냅니다.
네임스페이스루트속성요소텍스트 콘텐츠
예를 들어, Bright Data 사이트맵은 다음과 같은 XML 구조를 가집니다:
urlset은루트요소입니다.<urlset xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd>는urlset요소에 특화된 네임스페이스 선언으로, 이 선언의 규칙이urlset요소에까지 적용됨을 의미합니다. 이 요소 아래의 모든 요소는 이 네임스페이스가 정의한 스키마를 준수해야 합니다.url은루트요소의 첫 번째 자식 요소입니다.loc은url요소의 자식 요소입니다.
XSD와 XML 파일 요소에 대해 조금 더 알게 되었으니, 이제 그 정보를 활용하여 몇 가지 라이브러리를 사용하여 XML 파일을 파싱해 보겠습니다.
Python에서 XML을 파싱하는 다양한 방법
데모 목적으로, 이 튜토리얼에서는 XML 형식으로 제공되는 Bright Data 사이트맵을 사용할 것입니다. 다음 예시에서는Python requests 라이브러리를 사용하여 Bright Data 사이트맵 콘텐츠를 가져옵니다.
Python requests 라이브러리는 기본 제공되지 않으므로 진행 전에 설치해야 합니다. 다음 명령어를 사용해 설치할 수 있습니다:
pip install requests
ElementTree
ElementTree XML API는Python에서 XML 데이터를 파싱하고 생성하기 위한 간단하고 직관적인 API를 제공합니다. 이는 Python 표준 라이브러리의 내장 모듈이므로 별도로 설치할 필요가 없습니다.
예를 들어,findall()메서드를 사용하여 루트에서 모든url요소를 찾고loc요소의 텍스트 값을 다음과 같이 출력할 수 있습니다:
import xml.etree.ElementTree as ET
import requests
url = 'https://brightdata.com/post-sitemap.xml'
response = requests.get(url)
if response.status_code == 200:
root = ET.fromstring(response.content)
for url_element in root.findall('.//{http://www.sitemaps.org/schemas/sitemap/0.9}url'):
loc_element = url_element.find('{http://www.sitemaps.org/schemas/sitemap/0.9}loc')
if loc_element is not None:
print(loc_element.text)
else:
print("URL에서 XML 파일을 가져오는 데 실패했습니다.")
사이트맵의 모든 URL이 출력됩니다:
https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/addressing-brand-protection-from-every-angle
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations
ElementTree는 Python에서 XML 데이터를 파싱하는 사용자 친화적인 방법으로, XML 구조를 탐색하고 조작하기 쉬운 직관적인 API를 제공합니다. 그러나 ElementTree에는 한계가 있습니다. 스키마 유효성 검사에 대한 강력한 지원이 부족하며, 파싱 전에 스키마 사양을 엄격히 준수해야 하는 경우에는 적합하지 않습니다.
RSS 피드를 읽는 소규모 스크립트의 경우, ElementTree의 사용자 친화적인 API는 각 피드 항목에서 제목, 설명, 링크를 추출하는 데 유용한 도구입니다. 그러나 복잡한 검증이나 대용량 파일을 다루는 사용 사례라면 lxml과 같은 다른 라이브러리를 고려하는 것이 더 나을 것입니다.
lxml
lxml은Python에서 XML 파일을 파싱하기 위한 빠르고 사용하기 쉬우며 기능이 풍부한 API입니다. 다만 Python의 사전 구축된 라이브러리는 아닙니다. 일부 Linux 및 Mac 플랫폼에는 lxml 패키지가 이미 설치되어 있지만, 다른 플랫폼에서는 수동 설치가 필요합니다.
lxml은PyPI를통해 배포되며, 다음pip명령어로설치할수 있습니다:
pip install lxml
설치 후lxml을사용하여find(),findall(),findtext(),get(),get_element_by_id() 등의다양한 API메서드로 XML 파일을 파싱할 수 있습니다.
예를 들어,findall()메서드를 사용해url요소를 반복 처리하고,해당 요소의자식 요소인loc요소를 찾은 후 위치 텍스트를 출력하는 코드는 다음과 같습니다:
from lxml import etree
import requests
url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)
if response.status_code == 200:
root = etree.fromstring(response.content)
for url in root.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}url"):
loc = url.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text.strip()
print(loc)
else:
print("URL에서 XML 파일을 가져오는 데 실패했습니다.")
출력 결과는 사이트맵에서 발견된 모든 URL을 표시합니다:
https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/addressing-brand-protection-from-every-angle
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations
지금까지 요소를 찾고 그 값을 출력하는 방법을 배웠습니다. 이제 XML을 파싱하기 전에 스키마 유효성 검사를 살펴보겠습니다. 이 과정은 파일이 스키마에 정의된 지정된 구조를 준수하는지 확인합니다.
사이트맵의 XSD는 다음과 같습니다:
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
elementFormDefault="qualified"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xs:element name="urlset">
<xs:complexType>
<xs:sequence>
<xs:element ref="url" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="url">
<xs:complexType>
<xs:sequence>
<xs:element name="loc" type="xs:anyURI"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
스키마 유효성 검사에 사이트맵을 사용하려면 수동으로 복사하여 schema.xsd라는 파일을 생성하십시오.
이 XSD를 사용하여 XML 파일을 검증하려면 다음 코드를 사용하십시오:
from lxml import etree
import requests
url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)
if response.status_code == 200:
root = etree.fromstring(response.content)
try:
print("스키마 검증:")
schema_doc = etree.parse("schema.xsd")
schema = etree.XMLSchema(schema_doc)
schema.assertValid(root)
print("XML is valid according to the schema.")
except etree.DocumentInvalid as e:
print("XML validation error:", e)
여기서는etree.parse()메서드를 사용하여 XSD 파일을 파싱합니다. 그런 다음 파싱된 XSD 문서 콘텐츠를 사용하여 XML 스키마를 생성합니다. 마지막으로assertValid()메서드를 사용하여 XML 루트 문서를 XML 스키마에 대해 검증합니다. 스키마 검증이 통과되면 출력에 “XML is valid according to the schema.”와 유사한 메시지가 포함됩니다. 그렇지 않으면DocumentInvalid예외가 발생합니다.
출력 결과는 다음과 같아야 합니다:
스키마 유효성 검사:
XML이 스키마에 따라 유효합니다.
이제 xpath 메서드를 사용하여 경로로 요소를 찾는 XML 파일을 읽어 보겠습니다.
xpath() 메서드를 사용하여 요소를 읽으려면 다음 코드를 사용하세요:
from lxml import etree
import requests
url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)
if response.status_code == 200:
root = etree.fromstring(response.content)
print("XPath 지원:")
root = etree.fromstring(response.content)
namespaces = {"ns": "http://www.sitemaps.org/schemas/sitemap/0.9"}
for url in root.xpath(".//ns:url/ns:loc", namespaces=namespaces):
print(url.text.strip())
이 코드에서는 네임스페이스 접두사 ns를 등록하고 이를 네임스페이스 URI http://www.sitemaps.org/schemas/sitemap/0.9에 매핑합니다. XPath 표현식에서 ns 접두사를 사용하여 해당 네임스페이스 내의 요소를 지정합니다. 마지막으로, 표현식 .//ns:url/ns:loc은 해당 네임스페이스 내에서 url 요소의 자식인 모든 loc 요소를 선택합니다.
출력 결과는 다음과 같습니다:
XPath 지원:
https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/addressing-brand-protection-from-every-angle
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations
보시다시피, find() 및 findall() 메서드는 xpath 메서드보다 빠릅니다. xpath는 결과를 반환하기 전에 모든 결과를 메모리에 수집하기 때문입니다. XPath 쿼리를 사용해야 할 특별한 이유가 없는 한 find() 메서드를 사용하는 것이 좋습니다.
lxml은 XML 및 HTML 파싱과 조작을 위한 강력한 기능을 제공합니다.XPath 표현식을 사용한 복잡한 쿼리를 지원하고, 스키마에 대한 문서 유효성 검사를 수행하며, 심지어확장 가능한 스타일시트 언어 변환(XSLT)까지 허용합니다. 이는 성능과 고급 기능이 중요한 시나리오에 이상적입니다. 다만 lxml은 Python 핵심 패키지의 일부가 아니므로 별도로 설치해야 한다는 점을 유의하세요.
고성능과 고급 조작이 모두 필요한 대규모 또는 복잡한 XML 데이터를 다루는 경우 lxml 사용을 고려해야 합니다. 예를 들어, XML 형식의 금융 데이터 피드를 처리할 때 XPath 표현식을 사용하여 주가 같은 특정 요소를 추출하고, 정확성을 보장하기 위해 금융 스키마에 따라 데이터를 검증하며, 추가 분석을 위해 XSLT를 사용하여 데이터를 변환해야 할 수 있습니다.
minidom
minidom은Python 표준 라이브러리에 포함된 가볍고 간단한 XML 파싱 라이브러리입니다. lxml로 파싱하는 것만큼 기능이 풍부하거나 효율적이지는 않지만, Python에서 XML 데이터를 파싱하고 조작하는 간단한 방법을 제공합니다.
DOM 객체에서 제공하는 다양한 메서드를 사용하여 요소에 접근할 수 있습니다. 예를 들어,getElementsByTagName()메서드를사용하면 태그 이름을 통해 요소의 값을 가져올 수 있습니다.
다음 예제는 minidom 라이브러리를 사용하여 XML 파일을 파싱하고 태그 이름을 통해 요소를 가져오는 방법을 보여줍니다:
import requests
import xml.dom.minidom
url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)
if response.status_code == 200:
dom = xml.dom.minidom.parseString(response.content)
urlset = dom.getElementsByTagName("urlset")[0]
for url in urlset.getElementsByTagName("url"):
loc = url.getElementsByTagName("loc")[0].firstChild.nodeValue.strip()
print(loc)
else:
print("URL에서 XML 파일을 가져오는 데 실패했습니다.")
출력 결과는 다음과 같습니다:
https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/addressing-brand-protection-from-every-angle
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations
minidom은 XML 데이터를 DOM 트리로 표현하여 처리합니다. 이 트리 구조는 데이터 탐색 및 조작을 용이하게 하며, 간단한 XML 구조 읽기, 변경 또는 구축과 같은 기본 작업에 가장 적합합니다.
프로그램에서 XML 파일의 기본 설정을 읽어야 하는 경우, minidom의 DOM 접근 방식을 사용하면 자식 노드나 속성을 찾는 등의 메서드를 통해 XML 파일 내 특정 설정에 쉽게 접근할 수 있습니다. minidom을 사용하면 폰트 크기 ( font-size ) 노드와 같은 특정 설정을 XML 파일에서 쉽게 검색하고 애플리케이션 내에서 해당 값을 활용할 수 있습니다.
SAX 파서
SAX 파서는Python의 이벤트 기반 XML 파싱 방식으로, XML 문서를 순차적으로 처리하며 문서의 다양한 부분을 만나면 이벤트를 생성합니다. 전체 XML 문서를 메모리에 트리 구조로 구축하는 DOM 기반 파서와 달리, SAX 파서는 문서의 완전한 표현을 구축하지 않습니다. 대신 문서를 파싱하면서 시작 태그, 종료 태그, 텍스트 콘텐츠 등의 이벤트를 발생시킵니다.
SAX 파서는 전체 문서를 메모리에 로드하지 않고 XML 데이터를 점진적으로 처리하므로 메모리 효율성이 중요한 대용량 XML 파일이나 스트림 처리 시 적합합니다.
SAX 파서를 사용할 때는 파서가 발행하는 startElement 및 endElement와 같은 특정 XML 이벤트에 응답하는 이벤트 핸들러를 정의해야 합니다. 이러한 이벤트 핸들러는 XML 문서의 구조와 내용에 기반하여 작업을 수행하도록 맞춤 설정할 수 있습니다.
다음 예제는 startElement 및 endElement 이벤트를 정의하고 사이트맵 파일에서 URL 정보를 추출하여 SAX 파서로 XML 파일을 파싱하는 방법을 보여줍니다:
import requests
import xml.sax.handler
from io import BytesIO
class MyContentHandler(xml.sax.handler.ContentHandler):
def __init__(self):
self.in_url = False
self.in_loc = False
self.url = ""
def startElement(self, name, attrs):
if name == "url":
self.in_url = True
elif name == "loc" and self.in_url:
self.in_loc = True
def characters(self, content):
if self.in_loc:
self.url += content
def endElement(self, name):
if name == "url":
print(self.url.strip())
self.url = ""
self.in_url = False
elif name == "loc":
self.in_loc = False
url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)
if response.status_code == 200:
xml_content = BytesIO(response.content)
content_handler = MyContentHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(content_handler)
parser.parse(xml_content)
else:
print("URL에서 XML 파일을 가져오는 데 실패했습니다.")
출력 결과는 다음과 같습니다:
https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/addressing-brand-protection-from-every-angle
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations
전체 파일을 메모리에 로드하는 다른 파서와 달리, SAX는 파일을 점진적으로 처리하여 메모리를 절약하고 성능을 향상시킵니다. 그러나 SAX는 각 데이터 세그먼트를 동적으로 관리하기 위해 더 많은 코드를 작성해야 합니다. 또한 나중에 데이터의 특정 부분을 다시 방문하여 분석할 수 없습니다.
특정 정보(예: 오류 메시지)를 추출하기 위해 대용량 XML 파일(예: 다양한 이벤트가 포함된 로그 파일)을 스캔해야 하는 경우, SAX를 사용하면 파일을 효율적으로 탐색할 수 있습니다. 그러나 분석 과정에서 서로 다른 데이터 세그먼트 간의 관계를 이해해야 하는 경우에는 SAX가 최선의 선택이 아닐 수 있습니다.
untangle
언탱글(untangle)은 Python용 경량 XML 파싱 라이브러리로, XML 문서에서 데이터를 추출하는 과정을 단순화합니다. 계층적 구조를 탐색해야 하는 기존 XML 파서와 달리, 언탱글은 XML 요소와 속성을 Python 객체로 직접 접근할 수 있게 합니다.
언탱글을 사용하면 XML 문서를 중첩된 Python 사전으로 변환할 수 있습니다. 여기서 XML 요소는 사전 키로 표현되며, 해당 속성과 텍스트 콘텐츠는 대응하는 값으로 저장됩니다. 이 접근 방식은 Python 데이터 구조를 사용하여 XML 데이터에 쉽게 접근하고 조작할 수 있게 합니다.
untangle은 Python에서 기본적으로 제공되지 않으며, 다음 PyPI 명령을 사용하여 설치해야 합니다:
pip install untangle
다음 예시는 untangle 라이브러리를 사용하여 XML 파일을 파싱하고 XML 요소에 접근하는 방법을 보여줍니다:
import untangle
import requests
url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)
if response.status_code == 200:
obj = untangle.parse(response.text)
for url in obj.urlset.url:
print(url.loc.cdata.strip())
else:
print("Failed to retrieve XML file from the URL.")
출력 결과는 다음과 같습니다:
https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/addressing-brand-protection-from-every-angle
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations
언탱글(untangle)은 파이썬에서 XML 데이터를 다루는 사용자 친화적인 접근법을 제공합니다. 명확한 구문으로 파싱 과정을 단순화하고 XML 구조를 사용하기 쉬운 파이썬 객체로 자동 변환하여 복잡한 탐색 기술이 필요 없도록 합니다. 다만, 언탱글은 파이썬 핵심 패키지의 일부가 아니므로 별도 설치가 필요하다는 점을 유의하세요.
잘 구성된 XML 파일을 보유하고 있으며 추가 처리를 위해 이를 신속하게 Python 객체로 변환해야 하는 경우 untangle 사용을 고려해 보십시오. 예를 들어, XML 형식으로 날씨 데이터를 다운로드하는 프로그램이 있다면, untangle을 사용하여 XML을 파싱하고 현재 온도, 습도, 예보를 나타내는 Python 객체를 생성하는 것이 적합할 수 있습니다. 이러한 객체는 이후 애플리케이션 내에서 쉽게 조작하고 표시할 수 있습니다.
결론
이 글에서는 XML 파일과 Python에서 XML 파일을 파싱하는 다양한 방법에 대해 모두 알아보았습니다.
작은 구성 파일을 다루든, 대규모 웹 서비스 응답을 파싱하든, 방대한 사이트맵에서 데이터를 추출하든, Python은 XML 파싱 작업을 자동화하고 간소화할 수 있는 다용도 라이브러리를 제공합니다. 그러나 프록시 관리 없이 requests 라이브러리를 사용하여 웹에서 파일에 접근할 경우 할당량 예외 및 속도 제한 문제를 겪을 수 있습니다.Bright Data는원활한 데이터 검색 및 파싱을 보장하는 안정적이고 효율적인프록시 솔루션을제공하는 수상 경력에 빛나는 프록시 네트워크입니다. Bright Data를 사용하면 제한이나 중단에 대한 걱정 없이 XML 파싱 작업을 처리할 수 있습니다. 자세한 내용은 영업 문의를 통해 문의하세요.
스크래핑과 파싱 과정 전체를 건너뛰고 싶으신가요? 저희 데이터셋 마켓플레이스를 무료로 이용해 보세요!