3. SAX: The Simple API for XML

SAX ´Â xml-dev ¸ÞÀϸµ ¸®½ºÆ®¸¦ ÅëÇÑ ¸¹Àº »ç¶÷µéÀÇ Á¶¾ðÀ» ÅëÇØ, David Megginson ÀÌ °³¹ßÇÏ¿´´Ù. SAX ´Â XML ÀÇ ÆÄ½Ì¿¡ ´ëÇØ À̺¥Æ®-±¸µ¿(event-driven) ÀÎÅÍÆäÀ̽º¸¦ °®´Â´Ù. SAX ¸¦ »ç¿ëÇϱâ À§Çؼ­, ÀûÀýÇÑ ÀÎÅÍÆäÀ̽º°¡ ¼±¾ðµÈ ÆÄÀ̽ã Ŭ·¡½º ÀνºÅϽº¸¦ Çϳª »ý¼ºÇϰí, ÀÌ ¿ÀºêÁ§Æ®ÀÇ ÀûÀýÇÑ ¸Þ½îµå¸¦ È£ÃâÇÔÀ¸·Î¼­ ÆÄ½ÌÀ» ÇÏ°Ô µÈ´Ù.

ÀÌÀü ¹öÀüÀÇ ¹®¼­¿¡¼­´Â SAX1 À» ¼³¸íÇÏ¿´Áö¸¸, À̹ø ÇÏ¿ìÅõ¿¡¼­´Â SAX ¹öÀü 2 (SAX2) ¸¦ °¡Áö°í ¼³¸íÀ» ÇÑ´Ù.

SAX ´Â óÀ½ºÎÅÍ ³¡±îÁö ±¸¼ºµÈ XML ¹®¼­¸¦ Àоîµé¿©, ´Ù¸¥ ¹®¼­·Î º¯È¯ÇϱâÀ§ÇØ ¿¬»êÇϰųª, ±× ¹®¼­ÀÇ Á¤º¸µéÀ» Á¤¸®ÇÏ´Â (¿¹¸¦µé¾î ƯÁ¤ ¿ä¼ÒÀÇ Æò±Õ°ªµîÀ» °è»êÇÏ´Â ÀÛ¾÷) µî¿¡ ÀûÇÕÇÏ´Ù. ±×·¯³ª, °£´ÜÇÑ ¿ä¼ÒÀÇ ³»¿ëÀ̳ª ¼Ó¼ºÀ» ´Ù¸¥°ªÀ¸·Î ¹Ù²Ù´Â µîÀÇ °£´ÜÇÑ ¸ñÀû¿¡ »ç¿ëµÉ¼ö´Â À־, ÁßøµÈ ¾î¶°ÇÑ ¿ä¼ÒµéÀ» ±³È¯ÇÑ´Ù´øÁö ÇÏ´Â º¹ÀâÇÑ ¿¬»êµîÀÇ ¹®¼­±¸Á¶ º¯È¯¿¡´Â ÀûÇÕÇÏÁö ¾Ê´Ù. ¿¹¸¦ µé¾î, SAX ¸¦ »ç¿ëÇÏ¿© ¼Ó¼º°ªÀÌ 'greek'ÀÎ ¾î¶² ¿ä¼ÒÀÇ ³»¿ëÀ» Greek ¹®ÀÚ·Î º¯È¯ÇÏ´Â µîÀÇ ÀÛ¾÷¿¡´Â ÀûÇÕÇÏÁö¸¸, Àüü Ã¥ÀÇ Ã©Å͸¦ Àç¹èÄ¡ÇÑ´Ù´Â µîÀÇ ÀÛ¾÷Àº ÀûÇÕÇÏÁö ¾Ê´Ù.

SAX ÀÇ ÀåÁ¡Àº ¼Óµµ¿Í ´Ü¼øÇÔ¿¡ ÀÖ´Ù. ¸¸È­Ã¥µéÀ» ³ª¿­Çϱâ À§ÇÑ DTD °¡ Á¤ÀǵǾî ÀÖ´Ù°í °¡Á¤Çϰí, ´ç½ÅÀÌ ´ç½ÅÀÌ ¼ÒÀåÇϰí ÀÖ´Â ¸¸È­Ã¥Áß¿¡¼­ ÀúÀÚ(writer)°¡ Neil Gaiman ÀÎ ¸ðµç°ÍÀ» ãÀ¸·Á ÇÑ´Ù°í ÇÏÀÚ. ÀÌ·¯ÇÑ Æ¯Á¤ÇÑ ÀÏ¿¡ ´ëÇØ¼­, °Ë»ö¿¡ °ü·ÃÀÌ ¾ø´Â ¹Ì¼ú°¡(artist)³ª ÆíÁýÀÚ(editor), ä»ö°¡(colourist) µîÀÇ ¿ä¼Ò¿¡ ´ëÇÑ ¼³¸íÀ» È®ÀåÇÒ ÇÊ¿ä´Â ¾ø´Ù. ±×·¯¹Ç·Î ÀÌ·²¶§¿¡´Â ¿ä¼ÒÁß ÀúÀÚ(writer)¸¦ Á¦¿ÜÇÑ ³ª¸ÓÁö ¿ä¼Ò´Â ¹«½ÃÇϵµ·Ï Ŭ·¡½º ÀνºÅϽº¸¦ ÀÛ¼ºÇÏ¸é µÈ´Ù.

¶Ç´Ù¸¥ ÀÌÁ¡Àº, ƯÁ¤½ÃÁ¡¿¡ ÀÖ¾î ¸ðµç ¹®¼­ÀÇ ³»¿ëÀ» ¸Þ¸ð¸®¿¡ ¿Ã¸®Áö ¾Ê¾Æµµ µÈ´Ù´Â °ÍÀÌ´Ù. À̰ÍÀº ¸Å¿ì Å« ¹®¼­¸¦ ó¸®ÇÒ¶§ ¸Å¿ì À¯¿ëÇÒ°ÍÀÌ´Ù.

SAX ´Â 4°³ÀÇ ±âº»ÀûÀÎ ÀÎÅÍÆäÀ̽º¸¦ °¡Áö°í ÀÖ´Ù. SAX ¸¦ µû¸£´Â ÆÄ¼­´Â µ¥ÀÌÅ͸¦ ó¸®Çϱâ À§ÇØ ´Ù¾çÇÑ ¸Þ½îµå¸¦ È£ÃâÇϸç, ÀÌ·¯ÇÑ ÀÎÅÍÆäÀ̽º¸¦ Áö¿øÇÏ´Â ¾î¶°ÇÑ ¿ÀºêÁ§Æ®µµ ³Ñ°Ü¹ÞÀ» ¼ö ÀÖ´Ù. ±×·¯¹Ç·Î, ´ç½ÅÀÇ ÀÛ¾÷¿¡ ´ç½ÅÀÇ ¾îÇø®ÄÉÀ̼ǰú °ü·ÃµÈ ÀÎÅÍÆäÀ̽º¸¦ ÀÌ¿ëÇÏ¸é µÉ°ÍÀÌ´Ù.

SAX ÀÎÅÍÆäÀ̽º´Â ´ÙÀ½°ú °°´Ù.:

Interface Purpose
ContentHandler ÀÌ ÀÎÅÍÆäÀ̽º´Â SAX ÀÇ ÇÙ½ÉÀ¸·Î, ÀϹÝÀûÀÎ ¹®¼­ À̺¥Æ®¸¦ À§ÇÑ È£ÃâÀÌ´Ù. ÀÌ ¸Þ½îµå´Â ¹®¼­ÀÇ ½ÃÀÛ, ¿ä¼ÒÀÇ ½ÃÀÛ°ú ³¡, ¿ä¼Ò°¡ Æ÷ÇÔÇÏ´Â ³»¿ëÀÇ ¹®ÀÚ¸¦ ¸¸³¯¶§ È£ÃâÀ» ÇÑ´Ù.
DTDHandler ±âº»ÀûÀÎ ÆÄ½Ì¿¡ ÀÖ¾î ¿ä±¸µÇ´Â DTD À̺¥Æ®¸¦ Çڵ鸵Çϱâ À§ÇØ È£ÃâµÈ´Ù. Áï Ç¥±â¹ý(XML spec section 4.7)°ú ÆÄ½ÌµÇÁö ¾Ê´Â ¿£Æ¼Æ¼(entity) ¼±¾ð(XML spec section 4)À»¸¸³­¶§ È£ÃâÀ» ÇÑ´Ù.
EntityResolver ¿ÜºÎ ¿£Æ¼Æ¼¸¦ ÂüÁ¶Çϱâ À§ÇÏ¿© »ç¿ëµÈ´Ù.¸¸ÀÏ ¹®¼­¿¡ ¿ÜºÎ ¿£Æ¼Æ¼ ÂüÁ¶°¡ ¾ø´Ù¸é, ÀÌ ÀÎÅÍÆäÀ̽º¸¦ ½ÇÇàÇÒ Çʿ䰡 ¾øÀ»°ÍÀÌ´Ù.
ErrorHandler ¿¡·¯¸¦ ó¸®Çϱâ À§ÇØ È£ÃâÇÑ´Ù.ÆÄ¼­´Â ¸ðµç °æ°í¿Í ¿¡·¯¸¦ º¸°íÇϱâ À§ÇØ ÀÌ ÀÎÅÍÆäÀ̽º¸¦ ÅëÇØ ¸Þ½îµå¸¦ È£ÃâÇÑ´Ù.

ÆÄÀ̽ãÀº ÀÎÅÍÆäÀ̽º¿¡ ´ëÇÑ ³»¿ëÀ» Áö¿øÇÏÁö ¾ÊÀ¸¹Ç·Î À§ÀÇ ÀÎÅÍÆäÀ̽º´Â ÆÄÀ̽ã Ŭ·¡½º·Î ÀçÁ¤ÀÇ µÇ¾î¾ß ÇÑ´Ù. ±âº»ÀûÀ¸·Î ¸Þ½îµå´Â ¾Æ¹«·± Àϵµ ÇÏÁö ¾Ê´Â´Ù.(¸Þ½îµåÀÇ ³»¿ëÀº ÆÄÀ̽ãÀÇ pass ¸í·ÉÀ¸·Î µÇ¾îÀÖ´Ù.) ±×·¯¹Ç·Î ¾îÇø®ÄÉÀ̼ǿ¡¼­ »ç¿ëÇÏÁö ¾Ê´Â ¸Þ½îµå¿¡ ´ëÇØ¼­´Â ´Ü¼øÈ÷ ¹«½ÃÇØ ¹ö¸®¸é µÈ´Ù.

¿©±â SAX ¸¦ »ç¿ëÇÏ´Â ½´µµÄÚµåÀÇ ¿¹°¡ ÀÖ´Ù :

   
   # Çڵ鷯 Ŭ·¡½ºÀÇ Á¤ÀÇ
   from xml.sax import Contenthandler, ...
   class docHandler(ContentHandler):
       ...

   # Çڵ鷯 Ŭ·¡½ºÀÇ ÀνºÅϽº »ý¼º
   dh = docHandler()

   # XML parser »ý¼º
   parser = ...

   # ÆÄ¼­¿¡ Çڵ鷯 ÀνºÅϽº¸¦ ÁöÁ¤
   parser.setContentHandler(dh)

   # ÆÄÀÏ ÆÄ½Ì; Çڵ鷯 ¸Þ¼Òµå°¡ È£ÃâµÉ°ÍÀÌ´Ù.
   parser.parse(sys.stdin)

3.1. Starting Out

¸¸È­Ã¥µé¿¡ ´ëÇÑ Á¤º¸¸¦ ÀúÀåÇÏ´Â °£´ÜÇÑ XML Æ÷¸Ë¿¡ ´ëÇØ »ý°¢Çغ¸ÀÚ. Çѱǿ¡ ´ëÇÑ °£´ÜÇÑ ¹®¼­°¡ ¿©±â ÀÖ´Ù. :

   
   <collection>
     <comic title="Sandman" number='62'>
       <writer>Neil Gaiman</writer>
       <penciller pages='1-9,18-24'>Glyn Dillon</penciller>
       <penciller pages="10-17">Charles Vess</penciller>
     </comic>
   </collection>

XML ¹®¼­´Â ¹Ýµå½Ã ÇϳªÀÇ ÃÖ»óÀ§ ¿ä¼Ò¸¦ °¡Áö°í ÀÖ¾î¾ßÇÑ´Ù. ¿©±â¼­´Â "collection" ÀÌ ÃÖ»óÀ§ ¿ä¼ÒÀÌ´Ù. À̰ÍÀº ÇϳªÀÇ ÀÚ½Ä ¿ä¼ÒÀÎ comic À» °®´Â´Ù. comic ¿ä¼ÒÀÇ ¼Ó¼ºÀ¸·Î Ã¥ÀÇ Á¦¸ñ(title)°ú ¹øÈ£(number)°¡ ÁÖ¾îÁ³À¸¸ç, ÀúÀÚ(writer)¿Í ¹Ì¼ú°¡(artist)¿¡ ´ëÇØ Çϳª ¶Ç´Â ±×ÀÌ»óÀÇ ÀÚ½ÄÀ» °¡Áö°í ÀÖ´Ù. ÇϳªÀÇ °£Ç๰(issue)¿¡ ´ëÇØ ¿©·¯¸íÀÇ ¹Ì¼ú°¡(artist) ³ª ÀúÀÚ(writer)°¡ ÀÖÀ»¼ö ÀÖ´Ù.

°£´ÜÇÑ »óÅ·Π½ÃÀÛÀ» ÇØº¸ÀÚ : FindIssue ¶ó À̸§ Áö¾îÁø ¹®¼­Çڵ鷯(document handler)´Â ÁÖ¾îÁø °£Ç๰ÀÌ Ä÷¢¼Ç ³»¿¡ ÀÖ´ÂÁö ã´ÂÀÏÀ» ÇÒ°ÍÀÌ´Ù.

   
   from xml.sax import saxutils

   class FindIssue(saxutils.DefaultHandler):
       def __init__(self, title, number):
           self.search_title, self.search_number = title, number

DefaultHandler Ŭ·¡½º´Â ³×°³ÀÇ ÀÎÅÍÆäÀ̽º - ContentHandler, DTDHandler, EntityResolver, ±×¸®°í ErrorHandler ¿¡¼­ »ó¼ÓÀ» ¹Þ´Â´Ù. À̰ÍÀº ¸ðµç°ÍÀ» ÇϳªÀÇ Å¬·¡½º ³»¿¡¼­ »ç¿ëÇÏ±æ ¿øÇÒ¶§ »ç¿ëÇÏ´Â ¹æ¹ýÀÌ´Ù. ¸¸ÀÏ °¢°¢ÀÇ ¸ñÀû¿¡ µû¶ó Ŭ·¡½º¸¦ ³ª´©±æ ¿øÇϰųª, ÇϳªÀÇ ÀÎÅÍÆäÀ̽º¸¸À» ¼±¾ðÇÏ±æ ¿øÇÑ´Ù¸é, °¢ ÀÎÅÍÆäÀ̽º¿¡ µû¶ó °¢°¢ ¼­ºêŬ·¡½º¸¦ ¸¸µé¼ö ÀÖ´Ù. ÀÌÁß¿¡ ¾î´À ¹æ¹ýÀÌ ´õ ÁÁ´Ù°í ÇÒ¼ö´Â ¾øÀ¸¸ç, ¹«¾ùÀ» ÇÒ°ÍÀΰ¡¿¡ µû¶ó ÀûÀýÇÑ ¹æ¹ýÀ» ÅÃÇÏ¸é µÉ°ÍÀÌ´Ù.

Ŭ·¡½º°¡ °Ë»öÀ» Çϱâ À§Çؼ­, ÀνºÅϽº´Â ¹«¾ùÀ» °Ë»öÇÒ°ÍÀÎÁö¸¦ ¾Ë¾Æ¾ßÇÑ´Ù. ã±â¸¦ ¿øÇÏ´Â title °ú ¹ßÇ๰ ¹øÈ£´Â FindIssue »ý¼ºÀÚ¿¡°Ô °Ç³»Áö°í, ÀνºÅϽºÀÇ ÀϺηΠÀúÀåµÉ°ÍÀÌ´Ù.

ÀÚ ÀÌÁ¦ ½ÇÁ¦·Î µ¿ÀÛÀ» ÇÏ´Â ÇÔ¼ö¸¦ »ìÆìº¸µµ·Ï ÇÏÀÚ. ÀÌ °£´ÜÇÑ ÀÛ¾÷Àº ´ÜÁö ÁÖ¾îÁø ¿ä¼ÒÀÇ ¼Ó¼ºÀ» »ìÆìº¸´Â °ÍÀ̹ǷΠstartElement ¸Þ¼Òµå°¡ ÀûÇÕÇÒ °ÍÀÌ´Ù.

   
    def startElement(self, name, attrs):
        # comic ¿ä¼Ò°¡ ¾Æ´Ï¸é ¹«½ÃÇ϶ó.
        if name != 'comic': return

        # title °ú number ¼Ó¼º¿¡ ´ëÇØ °Ë»öÀ» ÇÑ´Ù.
        title = attrs.get('title', None)
        number = attrs.get('number', None)
        if title == self.search_title and number == self.search_number:
            print title, '#'+str(number), 'found'
startElement() ¸Þ½îµå¿¡´Â ÁÖ¾îÁø ¿ä¼ÒÀÇ À̸§°ú ±× ¿ä¼ÒÀÇ ¼Ó¼ºÀ» °®´Â ÀνºÅϽº°¡ ÀüÇØÁø´Ù. ¸¶Áö¸· AttributeList ÀÎÅÍÆäÀ̽º´Â ÆÄÀ̽㠻çÀüÇüÀÌ´Ù. ±×·¯¹Ç·Î ÇÔ¼ö´Â ¸ÕÀú comic ¿ä¼ÒÀÎÁö È®ÀÎÇϰí, ƯÁ¤ title °ú number ¼Ó¼ºÀ» °Ë»öÇÏ°Ô µÈ´Ù. ºñ±³¿¡ ¼º°øÇÏ¸é ¸Þ¼¼Áö°¡ Ãâ·ÂµÈ´Ù.

startElement() ´Â ¹®¼­ÀÇ ¸ðµç ¿ä¼Ò¸¦ ¸¸³¯¶§¸¶´Ù È£ÃâµÈ´Ù. startElement() ÇÔ¼öÀÇ Ã³À½¿¡ 'Starting element:' ¸¦ Ãâ·ÂÇϵµ·Ï Äڵ带 Áý¾î³Ö´Â´Ù¸é, ´ÙÀ½°ú °°Àº Ãâ·ÂÀ» º¸°Ô µÉ°ÍÀÌ´Ù.

   
   Starting element: collection
   Starting element: comic
   Starting element: writer
   Starting element: penciller
   Starting element: penciller

½ÇÁ¦ Ŭ·¡½º¸¦ »ç¿ëÇϱâ À§Çؼ­, parser ¿Í FindIssue ÀνºÅϽº¸¦ »ý¼ºÇϰí À̵éÀ» ¿¬°áÇϰí, ÀԷ°ªÀ» ÆÄ½ÌÇϵµ·Ï ÆÄ¼­¸¦ È£ÃâÇÏ´Â ÃÖ»óÀ§ Äڵ尡 ÇÊ¿äÇÏ´Ù.

   
   from xml.sax import make_parser
   from xml.sax.handler import feature_namespaces

   if __name__ == '__main__':
       # ÆÄ¼­ÀÇ »ý¼º
       parser = make_parser()
       # ÆÄ¼­¿¡°Ô XML À̸§¿µ¿ªÀº ¹«½ÃÇϵµ·Ï ÀüÇÑ´Ù.
       parser.setFeature(feature_namespaces, 0)

       # handler ÀÇ »ý¼º
       dh = FindIssue('Sandman', '62')

       # ÆÄ¼­¿¡ ¿ì¸®°¡ ¸¸µç Çڵ鷯¸¦ »ç¿ëÇϵµ·Ï ÀüÇÑ´Ù.
       parser.setContentHandler(dh)

       # ÀÔ·ÂÀ» ÆÄ½ÌÇÑ´Ù.
       parser.parse(file)

make_parser Ŭ·¡½º´Â ÆÄ¼­¸¦ ¸¸µå´Â ÀÛ¾÷À» ÀÚµ¿À¸·Î ÇØÁØ´Ù. ÆÄÀ̽㿡´Â ÀÌ¹Ì ¿©·¯°¡Áö XML ÆÄ¼­°¡ Á¸ÀçÇϸç, ¾ÕÀ¸·Îµµ ¸¹Àº ÆÄ¼­°¡ Ãß°¡µÉ ¿¹Á¤ÀÌ´Ù. ÆÄÀ̽ã 1.5 ¹öÀü¿¡ ÀÖ´Â xmllib.py ´Â Ưº°È÷ ºü¸£Áö´Â ¾ÊÁö¸¸, ÇöÀçµµ »ç¿ë°¡´ÉÇÏ´Ù. xmllib.py ÀÇ ¼Óµµ¸¦ °³¼±ÇÑ ¹öÀüÀÌ xml.parsers ¿¡ Æ÷ÇԵǾîÀÖ´Ù. xml.parsers.expat ¸ðµâÀº Áö±Ý±îÁö´Â °¡Àå ºü¸£¸ç, °¡´ÉÇÏ´Ù¸é À̰ÍÀÌ ¼±ÅõȴÙ. make_parser ´Â ¾î¶² ÆÄ¼­°¡ »ç¿ë°¡´ÉÇÑÁö¸¦ °áÁ¤Çϰí, °¡Àå ºü¸¥ ÆÄ¼­¸¦ ¼±ÅÃÇÑ´Ù. ±×·¯¹Ç·Î, °¢ ÆÄ¼­µéÀÌ ¾î¶»°Ô ´Ù¸¥Áö¿¡ ´ëÇØ¼­´Â »ç¿ëÀÚ°¡ ¾Ë°í ÀÖ¾î¾ß ÇÒ Çʿ䰡 ¾ø´Ù. (make_parser ¿¡ ´ç½ÅÀÌ »ç¿ëÇϱ⸦ ¿øÇÏ´Â ÆÄ¼­ ¸®½ºÆ®¸¦ ¾Ë·ÁÁÙ¼öµµ ÀÖ´Ù.)

SAX2 ¿¡¼­ºÎÅÍ, XML À̸§¿µ¿ª(namespace)ÀÌ Áö¿øµÈ´Ù. ¸¸ÀÏ À̸§¿µ¿ª ÇÁ·Î¼¼½ÌÀÌ ¿¢Æ¼ºê µÇ¾îÀÖ´Ù¸é, startElement ´ë½Å startElementNS ÀÌ È£ÃâµÈ´Ù. content Çڵ鷯°¡ À̸§¿µ¿ª¿¡ ´ëÇÑ ¸Þ¼Òµå¸¦ Á¤ÀÇÇÏÁö ¾Ê¾Ò´Ù¸é, À̸§¿µ¿ª 󸮸¦ ÇÏÁö ¾Ê°Ú´Ù°í ¿ä±¸ÇÏ´Â °ÍÀÌ´Ù. ÀÌ ¼³Á¤ÀÇ ±âº»Àº ÆÄ¼­¿¡¼­ ÆÄ¼­·Î ¼öÁ¤À» °¡ÇÑ´Ù. ±×·¯¹Ç·Î ¾ÈÀüÇÑ °ªÀ¸·Î ¼³Á¤À» Çϴ°ÍÀÌ ÁÁ´Ù.

ÆÄ¼­ ÀνºÅϽº¸¦ »ý¼ºÇÑ ÈÄ, setContentHandler ¸¦ È£ÃâÇÏ¿© ÆÄ¼­¿¡°Ô »ç¿ëÇÒ Çڵ鷯¸¦ ³Ñ°ÜÁØ´Ù.

¿¹Á¦ XML ¹®¼­¸¦ À§ÀÇ Äڵ忡 ´ëÀÔÇϸé, Ãâ·ÂÀ¸·Î 'Sandman #62 found' °¡ ³ª¿Ã°ÍÀÌ´Ù.

3.2. Error Handling

ÀÌÁ¦ À§ÀÇ Äڵ忡 ¾Æ·¡ÀÇ ÆÄÀÏÀ» ÀÔ·ÂÀ¸·Î ³Ö¾îº¸ÀÚ:

   
   <collection>
     &foo;
     <comic title="Sandman" number='62'>
   </collection>

&foo; ¿£Æ¼Æ¼´Â ¾Ë·ÁÁöÁö ¾ÊÀº°ÍÀ̸ç, comic ¿ä¼Ò´Â ´ÝÈ÷Áö ¾Ê¾Ò´Ù. (¸¸ÀÏ ºó ³»¿ëÀ» °®´Â´Ù ÇÏ´õ¶óµµ ">" ·Î ´Ý±â Àü¿¡ "/" ¸¦ »ç¿ëÇÏ¿©¾ß ÇÑ´Ù.) ÀÌ·¯ÇÑ °á°ú·Î, SAXParseException ÀÌ ¹ß»ýÇÑ´Ù.

   
   xml.sax._exceptions.SAXParseException: undefined entity at None:2:2

ErrorHandler ÀÎÅÍÆäÀ̽ºÀÇ ±âº»ÄÚµå´Â ¾î¶°ÇÑ ¿¡·¯¿¡ ´ëÇØ¼­µµ ÀÚµ¿ÀûÀ¸·Î ¿¹¿Ü¸¦ ¹ß»ý½ÃŲ´Ù; ¸¸ÀÏ ÀÌ·¯ÇÑ ¿¡·¯Ã³¸®¸¦ ¿øÇÑ´Ù¸é, ¿¡·¯ Çڵ鷯¸¦ ¼öÁ¤ÇÒ Çʿ䰡 ¾øÀ»°ÍÀÌ´Ù. ¶ÇÇÑ, ÀÚ±â ÀڽŸ¸ÀÇ ErrorHandler ÀÎÅÍÆäÀ̽º³ª, error(), fatalError() ¿¡ ´ëÇÑ ÃÖ¼ÒÇÑÀÇ ¿À¹ö¶óÀ̵带 ¸¸µé¼öµµ ÀÖ´Ù. °¢ ¸Þ¼Òµå´Â ÃÖ¼Ò·Î ÇÑ ÁÙ Àϼöµµ ÀÖ´Ù. warning, error, fatalError ¿¡ ´ëÇÑ ErrorHandler ÀÎÅÍÆäÀ̽º ¸Þ½îµå´Â ¸ðµÎ ÇϳªÀÇ ÀÎÀÚ°ª - exception ÀνºÅϽº¸¦ °®´Â´Ù. exception Àº Ç×»ó SAXException ÀÇ ¼­ºêŬ·¡½ºÀ̸ç, str() ¸¦ ÀÌ¿ëÇÏ¿© ÀÐÀ»¼öÀÖ´Â ¹®Á¦Á¡À» ¼³¸íÇÏ´Â ¿¡·¯ ¸Þ¼¼Áö·Î ¸¸µé¼öÀÖ´Ù.

´ÙÀ½Àº, ¿©·¯°¡Áö ¿¡·¯¹ß»ý »óȲÀ» Àç¼³Á¤Çϱâ À§ÇØ, ¼¼°¡ÁöÁß ÇϳªÀÇ ¸Þ¼Òµå¸¦ ¿¹¿Ü¸¦ Ãâ·ÂÇϵµ·Ï Á¤ÀÇÇÏ¿´´Ù:

   
    def error(self, exception):
        import sys
        sys.stderr.write("\%s\n" \% exception)

ÀÌ Á¤ÀÇ¿¡ ÀÇÇϸé, Ä¡¸íÀûÀÌÁö ¾ÊÀº error ´Â ¿¡·¯ ¸Þ¼¼Áö¸¦ Ãâ·ÂÇÏ°Ô µÇÁö¸¸, Ä¡¸íÀûÀÎ ¿¡·¯(fatal error) ´Â Æ®·¹À̽º¹é(traceback)À» »ý¼ºÇÏ´Â ÀÏÀ» °è¼ÓÇÒ °ÍÀÌ´Ù.

3.3. Searching Element Content

ƯÁ¤ ÀúÀÚ(author) °¡ ¾´ ¸ðµç ¹ßÇ๰(issue) ¸¦ Ãâ·ÂÇÏ´Â, Á¶±Ý´õ º¹ÀâÇÑ ÀÛ¾÷À» ÇØº¸ÀÚ. Let's tackle a slightly more complicated task, printing out all issues written by a certain author. À̰ÍÀº ÀúÀÚÀÇ À̸§ÀÌ <writer>Peter Milligan</writer> °ú °°ÀÌ writer ¿ä¼ÒÀÇ ³»ºÎ¿¡ Á¸ÀçÇϹǷÎ, ¿ä¼ÒÀÇ ³»¿ëÀ» °Ë»öÇØ¾ßÇÑ´Ù.

ÀÌ·¯ÇÑ °Ë»öÀº ´ÙÀ½°ú °°Àº ¾Ë°í¸®ÁòÀ» µû¶ó 󸮵ȴÙ:

  1. startElement ¸Þ½îµå´Â Á¶±Ý´õ º¹ÀâÇÏ°Ô µÈ´Ù. comic ¿ä¼Ò¿¡ ´ëÇØ, °Ë»ö±âÁØ¿¡ ÀûÇÕÇÑ comic À» ³ªÁß¿¡ ã±âÀ§ÇØ, Çڵ鷯´Â title °ú number ¸¦ ÀúÀåÇÏ¿©¾ßÇÑ´Ù. writer ¿ä¼Ò¿¡ ´ëÇØ¼­´Â, inWriterContent Ç÷¡±×¸¦ ÂüÀ¸·Î ¼³Á¤Çϰí, writerName ¼Ó¼ºÀ» ºó ¹®ÀÚ¿­·Î ¼³Á¤ÇÑ´Ù.

  2. XML ű׹ÛÀÇ ¹®ÀÚµé(Characters)À» ó¸®ÇÏ¿©¾ß ÇÑ´Ù. inWriterContent °¡ ÂüÀ̸é, ÀÌ ¹®ÀÚµéÀ» writeName ¹®ÀÚ¿­¿¡ Ãß°¡ÇÑ´Ù.

  3. writer ¿ä¼Ò°¡ ³¡³ª¸é, writerName ¼Ó¼º¾ÈÀÇ ¸ðµç ¿ä¼ÒÀÇ ³»¿ëÀ» ¼öÁýÇѰÍÀÌ µÈ´Ù. ±×·¯¹Ç·Î, ã°íÀÚÇÏ´Â name °ú µ¿ÀÏÇѰÍÀÎÁö °Ë»çÇϰí, ¸¸ÀÏ ¸Â´Â´Ù¸é, ÀÌ comic ¿¡ ´ëÇÑ Á¤º¸¸¦ Ãâ·ÂÇÑ´Ù. ´Ù½Ã inWriterContent ¸¦ °ÅÁþÀ¸·Î ¼³Á¤ÇÑ´Ù.

¿©±â¿¡ ù¹øÂ° ºÎºÐÀÇ Äڵ尡 ÀÖ´Ù.

   
   from xml.sax import ContentHandler
   import string

   def normalize_whitespace(text):
       "¹®ÀÚ¿­¿¡¼­ Áߺ¹ÀûÀ¸·Î ³ªÅ¸³ª´Â °ø¹é¹®ÀÚ(whitespace)¸¦ Á¦°ÅÇÑ´Ù."
       return string.join(string.split(text), ' ')

   class FindWriter(ContentHandler):
       def __init__(self, search_name):
           # ã°íÀÚ ÇÏ´Â À̸§À» ÀúÀåÇÑ´Ù.
           self.search_name = normalize_whitespace(search_name)

           # Ç÷¡±×¸¦ °ÅÁþ(false)À¸·Î ÃʱâÈ­ÇÑ´Ù.
           self.inWriterContent = 0

       def startElement(self, name, attrs):
           # comic ¿ä¼ÒÀ̸é, title °ú issue ¸¦ ÀúÀåÇÑ´Ù.
           if name == 'comic':
               title = normalize_whitespace(attrs.get('title', ""))
               number = normalize_whitespace(attrs.get('number', ""))
               self.this_title = title
               self.this_number = number

           # writer ¿ä¼ÒÀÇ ½ÃÀÛÀ̸é, Ç÷¡±×¸¦ ¼³Á¤ÇÑ´Ù.
           elif name == 'writer':
               self.inWriterContent = 1
               self.writerName = ""

startElement() ¸Þ¼Òµå¿¡ ´ëÇØ¼­´Â ÀÌÀü¿¡ ¼³¸íÇÏ¿´´Ù. ÀÌÁ¦ ¿ä¼ÒÀÇ ³»¿ëÀ» ¾î¶»°Ô ó¸®ÇÏ´ÂÁö º¸ÀÚ.

normalize_whitespace() ÇÔ¼ö´Â ¸Å¿ì Áß¿äÇϹǷÎ, Äڵ峻¿¡ À̰ÍÀ» »ç¿ëÇÏ¿©¾ß ÇÑ´Ù. XML Àº °ø¹é¹®ÀÚ(whitespace) ¿¡ ¸Å¿ì ¹Î°¨ÇÏ´Ù; ´ç½ÅÀº ´ç½ÅÀÌ ¿øÇÏ´Â °÷¿¡ ¿©ºÐÀÇ °ø¹é°ú °³Ç๮ÀÚµéÀ» Æ÷ÇÔ½Ãų ¼ö ÀÖ´Ù. À̰ÍÀº ¼Ó¼º°ªÀ̳ª ¿ä¼ÒÀÇ ³»¿ëÀ» ºñ±³Çϱâ Àü¿¡, °ø¹é¹®ÀÚ¸¦ Á¤»óÀûÀ¸·Î ¸¸µé¾î¾ß ÇÔÀ» ÀǹÌÇÑ´Ù; ¸¸ÀÏ µÎ°³ÀÇ ¿ä¼ÒÀÇ ³»¿ëÀÌ ´Ù¸¥ ¼öÀÇ °ø¹é¹®ÀÚ¸¦ °®´Â´Ù¸é, ºñ±³¿¬»êÀº À߸øµÈ °á°ú¸¦ ¸¸µé¾î³¾ °ÍÀÌ´Ù.

   
    def characters(self, ch):
        if self.inWriterContent:
            self.writerName = self.writerName + ch

characters() ¸Þ½îµå´Â XML ű׹ÛÀÇ ¹®ÀÚµéÀ» ¸¸³¯¶§ È£ÃâµÈ´Ù. ch ´Â ¹®ÀÚµéÀÇ ½ºÆ®¸µÀÌÁö¸¸, ¹Ýµå½Ã ¹ÙÀÌÆ® ½ºÆ®¸µÀΰÍÀº ¾Æ´Ï´Ù; ÆÄ¼­´Â ¹®¼­ÀÇ ÀϺκÐÀÎ ¹öÆÛ ¿ÀºêÁ§Æ®¸¦ Á¦°øÇϰųª, À¯´ÏÄÚµå ¿ÀºêÁ§Æ®¸¦ °Ç³»ÁÖ°Ô µÈ´Ù(ÆÄÀ̽ã 2.0 ÀÇ expat ÆÄ¼­). parsers may also provide a buffer object that is a slice of the full document, or they may pass Unicode objects (as the expat parser does in Python 2.0).

¸ðµç ¹®ÀÚµéÀÌ ÇѹøÀÇ ÇÔ¼ö È£Ãâ·Î ó¸®µÈ´Ù°í °¡Á¤ÇÏÁö ¸»±â ¹Ù¶õ´Ù. À§ÀÇ ¿¹¸¦ µé¾îº¸¸é, "Peter Milligan" ¶ó´Â ¹®ÀÚ¿­¿¡ ´ëÇØ ÇѹøÀÇ characters() È£ÃâÀÌ Àְųª, ¶Ç´Â °¢ ¹®ÀÚ¿¡ ´ëÇØ Çѹø¾¿ characters() ÀÇ È£ÃâÀÌ ÀÖÀ» ¼ö ÀÖ´Ù. ´õ¿í Çö½ÇÀûÀ¸·Î, ¸¸ÀÏ ³»¿ëÀÌ "Wagner &amp; Seagle" °ú °°ÀÌ ¿£Æ¼Æ¼ ÂüÁ¶¸¦ °¡Áö°í ÀÖ´Ù¸é, ÆÄ¼­´Â "Wagner ", ¿£Æ¼Æ¼ ÂüÁ¶¸¦ »ó¡ÇÏ´Â "&", ±×¸®°í " Seagle" ¿¡ ´ëÇØ °¢ ¼¼¹øÀÇ ¸Þ¼Òµå È£ÃâÀ» ÇÏ°Ô µÈ´Ù.

FindWriter ÀÇ µÎ¹øÂ° °úÁ¤¿¡¼­, characters() ´Â ´ÜÁö inWriterContent ¸¦ °Ë»çÇϰí, ÀÌ °ªÀÌ ÂüÀ̸é, ¹®ÀÚ¸¦ ¹®ÀÚ¿­¿¡ Ãß°¡ÇÏ´Â ÀÛ¾÷À» ÇÑ´Ù.

¸¶Áö¸·À¸·Î, writer ¿ä¼Ò°¡ ³¡³¯¶§, Àüü À̸§ÀÌ ¼öÁýµÉ°ÍÀ̰í, À̰ÍÀ» ¿ì¸®°¡ ã°íÀÚÇÏ´Â À̸§°ú ºñ±³ÇÏ°Ô µÈ´Ù.

   
    def endElement(self, name):
        if name == 'writer':
            self.inWriterContent = 0
            self.writerName = normalize_whitespace(self.writerName)
            if self.search_name == self.writerName:
                print 'Found:', self.this_title, self.this_number

´Ù¸¥ °ø¹é¹®ÀÚ¼ö¿¡ ÀÇÇÑ È¥µ¿À» ¸·±âÀ§ÇØ, normalize_whitespace() ÇÔ¼ö°¡ È£ÃâµÇ¾ú´Ù. À̰ÍÀº ÀÌ DTD ³»¿¡¼­´Â, ÀÌ ¿ä¼Ò°¡ °¡Áö°í ÀÖ´Â °ø¹é¹®ÀÚ°¡ Áß¿äÇÏÁö ¾Ê±â ¶§¹®ÀÌ´Ù.

¸¶Ä§ ű×(End tags)´Â ¼Ó¼ºÀ» °¡Áú¼ö ¾øÀ¸¹Ç·Î, ¿©±â¿¡´Â attrs ÀÎÀÚ°¡ ¾ø´Ù. "<arc name="Season of Mists"/>" ¿Í °°ÀÌ ¼Ó¼ºÀ» °®´Â ºó ű״Â, startElement() ¸¦ È£ÃâÇÑ ÈÄ ¹Ù·Î endElement() ¸¦ È£ÃâÇÑ´Ù.

XXX ¿ÜºÎ ¿£Æ¼Æ¼ 󸮴 ¾î¶»°Ô ÇÒ°ÍÀΰ¡? ±×µé¿¡ ´ëÇÑ Ã³¸®°¡ Ưº°È÷ ÇÊ¿äÇѰ¡?

3.4. °ü·Ã ȨÆäÀÌÁö

http://www.megginson.com/SAX/

SAX ȨÆäÀÌÁö´Ù. ¿©±â¿¡´Â °¡Àå ÃÖ½ÅÀÇ ¸í¼¼¼­¿Í ´Ù¾çÇÑ ¾ð¾î¿Í Ç÷§Æû»óÀÇ SAX µµ±¸ ¸®½ºÆ®°¡ ÀÖ´Ù. ÇöÀç´Â ÀÚ¹Ù Áß½ÉÀûÀ¸·Î µÇ¾îÀÖ´Ù.