Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python SAX API로 XML 구문 분석하기: ContentHandler부터 실전 예제까지

SAX(Simple API for XML)는 이벤트 기반(event-driven) 방식으로 XML 문서를 구문 분석하기 위한 표준 인터페이스입니다. DOM처럼 문서 전체를 메모리에 올려 트리로 다루는 것이 아니라, 문서를 처음부터 끝까지 순차적으로 읽으면서 태그의 시작·종료·문자 데이터 등의 이벤트가 발생할 때마다 핸들러를 호출합니다. 그래서 대용량 XML 파일도 적은 메모리로 빠르게 처리할 수 있다는 장점이 있습니다.

Python에서 SAX로 XML을 파싱하려면 일반적으로 xml.sax.ContentHandler를 상속받아 자신만의 ContentHandler 클래스를 작성해야 합니다. 이 핸들러가 처리하려는 XML의 태그와 속성에 맞춰 각종 파싱 이벤트를 담당하며, 파서가 XML 파일을 읽어 나가는 동안 적절한 시점에 ContentHandler의 메서드들을 호출합니다.

ContentHandler의 주요 콜백 메서드

  • startDocument() — XML 문서의 시작 시점에 호출됩니다.
  • endDocument() — XML 문서의 끝 시점에 호출됩니다.
  • characters(text) — XML 내부의 문자 데이터가 매개변수 text로 전달되며 호출됩니다.
  • startElement(tag, attributes) / endElement(tag) — 각 요소의 시작과 끝에서 호출됩니다. tag는 요소의 태그 이름이고, attributes는 Attributes 객체입니다.
  • startElementNS() / endElementNS() — 파서가 네임스페이스 모드로 동작하는 경우 위 메서드 대신 호출되는 네임스페이스 버전입니다.

본격적인 예제로 들어가기 전에, 반드시 알아두어야 할 세 가지 핵심 메서드를 살펴보겠습니다.

1. make_parser 메서드

새로운 파서 객체를 생성하여 반환합니다. 생성되는 파서는 시스템이 찾은 첫 번째 파서 타입의 객체입니다.

xml.sax.make_parser( [parser_list] )
  • parser_list — 선택적 인자로, 사용할 파서 목록(list)을 지정합니다. 목록의 모든 파서는 make_parser 메서드를 구현하고 있어야 합니다.

2. parse 메서드

SAX 파서를 생성한 뒤, 이를 사용해 XML 문서를 구문 분석합니다.

xml.sax.parse( xmlfile, contenthandler[, errorhandler])
  • xmlfile — 읽어 들일 XML 파일의 이름(경로)입니다.
  • contenthandler — ContentHandler 객체여야 합니다.
  • errorhandler — 지정할 경우 SAX ErrorHandler 객체여야 합니다.

3. parseString 메서드

파일이 아닌 XML 문자열을 대상으로 파서를 생성하고 구문 분석을 수행하는 메서드입니다.

xml.sax.parseString(xmlstring, contenthandler[, errorhandler])
  • xmlstring — 읽어 들일 XML 문자열입니다.
  • contenthandler — ContentHandler 객체여야 합니다.
  • errorhandler — 지정할 경우 SAX ErrorHandler 객체여야 합니다.

실전 예제: 영화 정보 XML 파싱하기

다음은 영화 목록 XML(movies.xml)을 SAX로 파싱하는 완전한 예제입니다. 아래와 같은 구조의 XML 파일이 있다고 가정합니다.

<?xml version="1.0"?>
<collection>
   <movie title="Enemy Behind">
      <type>War, Thriller</type>
      <format>DVD</format>
      <year>2003</year>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Talk about a US-Japan war</description>
   </movie>
   <!-- 이하 movie 요소 반복 -->
</collection>

파싱 코드는 다음과 같습니다. 원문 예제는 Python 2 스타일(print 문)이었지만, 최신 환경에 맞게 Python 3 문법으로 정리했습니다.

#!/usr/bin/python3
import xml.sax

class MovieHandler(xml.sax.ContentHandler):
    def __init__(self):
        self.CurrentData = ""
        self.type = ""
        self.format = ""
        self.year = ""
        self.rating = ""
        self.stars = ""
        self.description = ""

    # 요소가 시작될 때 호출됨
    def startElement(self, tag, attributes):
        self.CurrentData = tag
        if tag == "movie":
            print("*****Movie*****")
            title = attributes["title"]
            print("Title:", title)

    # 요소가 끝날 때 호출됨
    def endElement(self, tag):
        if self.CurrentData == "type":
            print("Type:", self.type)
        elif self.CurrentData == "format":
            print("Format:", self.format)
        elif self.CurrentData == "year":
            print("Year:", self.year)
        elif self.CurrentData == "rating":
            print("Rating:", self.rating)
        elif self.CurrentData == "stars":
            print("Stars:", self.stars)
        elif self.CurrentData == "description":
            print("Description:", self.description)
        self.CurrentData = ""

    # 문자 데이터를 읽을 때 호출됨
    def characters(self, content):
        if self.CurrentData == "type":
            self.type = content
        elif self.CurrentData == "format":
            self.format = content
        elif self.CurrentData == "year":
            self.year = content
        elif self.CurrentData == "rating":
            self.rating = content
        elif self.CurrentData == "stars":
            self.stars = content
        elif self.CurrentData == "description":
            self.description = content

if __name__ == "__main__":
    # XMLReader 생성
    parser = xml.sax.make_parser()
    # 네임스페이스 기능 비활성화
    parser.setFeature(xml.sax.handler.feature_namespaces, 0)
    # 기본 ContentHandler를 커스텀 핸들러로 교체
    Handler = MovieHandler()
    parser.setContentHandler(Handler)
    parser.parse("movies.xml")

이 코드를 실행하면 다음과 같은 결과가 출력됩니다.

*****Movie*****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Year: 2003
Rating: PG
Stars: 10
Description: Talk about a US-Japan war
*****Movie*****
Title: Transformers
Type: Anime, Science Fiction
Format: DVD
Year: 1989
Rating: R
Stars: 8
Description: A scientific fiction
*****Movie*****
Title: Trigun
Type: Anime, Action
Format: DVD
Rating: PG
Stars: 10
Description: Vash the Stampede!
*****Movie*****
Title: Ishtar
Type: Comedy
Format: VHS
Rating: PG
Stars: 2
Description: Viewable boredom

예제 코드의 동작 흐름

  1. make_parser()로 파서 객체를 만들고, setFeature()로 네임스페이스 기능을 끕니다.
  2. setContentHandler()로 기본 핸들러 대신 MovieHandler를 등록합니다.
  3. parse()movies.xml을 순회하면서 movie 태그를 만나면 제목 속성을 출력하고, type, format, year 등의 자식 요소에서는 characters()로 값을 저장한 뒤 endElement()에서 출력합니다.

SAX API에 대한 더 자세한 내용은 Python 공식 문서의 표준 SAX API 레퍼런스를 참고하시기 바랍니다.