html.parser --- 간단한 HTML과 XHTML 구문 분석기

소스 코드: Lib/html/parser.py


이 모듈은 HTML(HyperText Mark-up Language)와 XHTML 형식의 텍스트 파일을 구문 분석하기 위한 기초로 사용되는 클래스 HTMLParser를 정의합니다.

class html.parser.HTMLParser(*, convert_charrefs=True)

잘못된 마크업을 구문 분석할 수 있는 구문 분석기 인스턴스를 만듭니다.

convert_charrefsTrue(기본값)이면, (script/style 요소에 있는 것을 제외한) 모든 문자 참조(character references)가 자동으로 해당 유니코드 문자로 변환됩니다.

HTMLParser 인스턴스는 HTML 데이터를 받아서 시작 태그, 종료 태그, 텍스트, 주석 및 기타 마크업 요소를 만날 때마다 처리기 메서드를 호출합니다. 사용자는 원하는 동작을 구현하기 위해 HTMLParser의 서브 클래스를 만들고 해당 메서드를 재정의해야 합니다.

이 구문 분석기는 종료 태그가 시작 태그와 일치하는지 검사하거나, 바깥(outer) 요소를 닫음으로써 묵시적으로 닫힌 요소에 대해 종료 태그 처리기를 호출하지 않습니다.

버전 3.4에서 변경: convert_charrefs 키워드 인자가 추가되었습니다.

버전 3.5에서 변경: 인자 convert_charrefs의 기본값은 이제 True입니다.

HTML 구문 분석기 응용 프로그램 예제

기본 예제로, 다음은 HTMLParser 클래스를 사용하여 시작 태그, 종료 태그 및 데이터를 만날 때마다 인쇄하는 간단한 HTML 구문 분석기입니다:

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Encountered a start tag:", tag)

    def handle_endtag(self, tag):
        print("Encountered an end tag :", tag)

    def handle_data(self, data):
        print("Encountered some data  :", data)

parser = MyHTMLParser()
parser.feed('<html><head><title>Test</title></head>'
            '<body><h1>Parse me!</h1></body></html>')

출력은 다음과 같습니다:

Encountered a start tag: html
Encountered a start tag: head
Encountered a start tag: title
Encountered some data  : Test
Encountered an end tag : title
Encountered an end tag : head
Encountered a start tag: body
Encountered a start tag: h1
Encountered some data  : Parse me!
Encountered an end tag : h1
Encountered an end tag : body
Encountered an end tag : html

HTMLParser 메서드

HTMLParser 인스턴스에는 다음과 같은 메서드가 있습니다:

HTMLParser.feed(data)

구문 분석기에 텍스트를 입력합니다. 완전한 요소로 구성되어있는 부분까지 처리됩니다; 불완전한 데이터는 더 많은 데이터가 공급되거나 close()가 호출될 때까지 버퍼링 됩니다. datastr이어야 합니다.

HTMLParser.close()

버퍼링 된 모든 데이터를 마치 파일 끝(end-of-file) 표시가 붙은 것처럼 처리합니다. 이 메서드는 파생 클래스에 의해 입력 끝에서의 추가 처리를 정의하기 위해 재정의될 수 있지만, 재정의된 버전에서는 항상 HTMLParser 베이스 클래스 메서드인 close()를 호출해야 합니다.

HTMLParser.reset()

인스턴스를 재설정합니다. 처리되지 않은 모든 데이터를 잃습니다. 이것은 인스턴스 생성 시에 묵시적으로 호출됩니다.

HTMLParser.getpos()

현재의 줄 번호와 오프셋(offset)을 반환합니다.

HTMLParser.get_starttag_text()

가장 최근에 열렸던 시작 태그의 텍스트를 반환합니다. 이것은 일반적으로 구조화된 처리에 필요하지 않지만, "배치된 대로(as deployed)" HTML을 다루거나 최소한의 변경(어트리뷰트 사이의 공백을 보존할 수 있음, 등등)으로 입력을 다시 생성하는 데 유용할 수 있습니다.

다음 메서드는 데이터나 마크업 요소를 만날 때 호출되며 서브 클래스에서 재정의하려는 용도입니다. 베이스 클래스 구현은 아무 일도 하지 않습니다 (handle_startendtag()는 예외입니다).:

HTMLParser.handle_starttag(tag, attrs)

이 메서드는 태그의 시작(예를 들어, <div id="main">)을 처리하기 위해 호출됩니다.

tag 인자는 소문자로 변환된 태그의 이름입니다. attrs 인자는 태그의 <> 화살괄호 안에 있는 어트리뷰트를 포함하는 (name, value) 쌍의 리스트입니다. name은 소문자로 변환되고, value의 따옴표는 제거되고, 문자와 엔티티 참조는 치환됩니다.

예를 들어, 태그 <A HREF="https://www.cwi.nl/">의 경우, 이 메서드는 handle_starttag('a', [('href', 'https://www.cwi.nl/')])로 호출됩니다.

html.entities의 모든 엔티티 참조가 어트리뷰트 값에서 치환됩니다.

HTMLParser.handle_endtag(tag)

이 메서드는 요소의 종료 태그(예를 들어, </div>)를 처리하기 위해 호출됩니다.

tag 인자는 소문자로 변환된 태그의 이름입니다.

HTMLParser.handle_startendtag(tag, attrs)

handle_starttag()와 비슷하지만, 구문 분석기가 XHTML 스타일의 빈 태그(<img ... />)를 만날 때 호출됩니다. 이 메서드는 이 특정의 어휘 정보(lexical information)가 필요한 서브 클래스에 의해 재정의될 수 있습니다; 기본 구현은 단순히 handle_starttag()handle_endtag()를 호출합니다.

HTMLParser.handle_data(data)

이 메서드는 임의의 데이터(예를 들어, 텍스트 노드와 <script>...</script><style>...</style>의 내용)를 처리하기 위해 호출됩니다.

HTMLParser.handle_entityref(name)

이 메서드는 &name; 형식(예를 들어, &gt;)의 이름있는 문자 참조를 처리하기 위해 호출됩니다. 여기서 name은 일반 엔티티 참조(예를 들어, 'gt')입니다. convert_charrefsTrue이면, 이 메서드는 호출되지 않습니다.

HTMLParser.handle_charref(name)

이 메서드는 &#NNN;&#xNNN; 형식의 10진수 및 16진수 문자 참조를 처리하기 위해 호출됩니다. 예를 들어, &gt;에 해당하는 10진수는 &#62;이고, 반면에 16진수는 &#x3E;입니다; 이때 메서드는 '62''x3E'를 받습니다. 이 메서드는 convert_charrefsTrue이면 호출되지 않습니다.

HTMLParser.handle_comment(data)

이 메서드는 주석을 만날 때 호출됩니다 (예를 들어, <!--comment-->).

예를 들어, 주석 <!-- comment -->는 이 메서드가 인자 ' comment '로 호출되도록 합니다.

Internet Explorer 조건부 주석(condcoms)의 내용도 이 메서드로 보내지므로, <!--[if IE 9]>IE9-specific content<![endif]-->의 경우, 이 메서드는 '[if IE 9]>IE9-specific content<![endif]'를 받습니다.

HTMLParser.handle_decl(decl)

이 메서드는 HTML doctype 선언(예를 들어, <!DOCTYPE html>)을 처리하기 위해 호출됩니다.

decl 매개 변수는 <!...> 마크업 내의 선언 전체 내용입니다 (예를 들어, 'DOCTYPE html').

HTMLParser.handle_pi(data)

처리 명령(processing instruction)을 만날 때 호출되는 메서드. data 매개 변수에는 전체 처리 명령이 포함됩니다. 예를 들어, 처리 명령 <?proc color='red'>의 경우, 이 메서드는 handle_pi("proc color='red'")로 호출됩니다. 파생 클래스에 의해 재정의되려는 목적입니다; 베이스 클래스 구현은 아무것도 수행하지 않습니다.

참고

HTMLParser 클래스는 처리 명령에 대해 SGML 구문 규칙을 사용합니다. 후행 '?'를 사용하는 XHTML 처리 명령은 '?'data에 포함되도록 합니다.

HTMLParser.unknown_decl(data)

이 메서드는 구문 분석기가 인식할 수 없는 선언을 읽었을 때 호출됩니다.

data 매개 변수는 <![...]> 마크업 안에 있는 선언의 전체 내용입니다. 파생 클래스가 재정의하는 것이 때때로 유용합니다. 베이스 클래스 구현은 아무것도 수행하지 않습니다.

예제

다음 클래스는 더 많은 예를 설명하는 데 사용할 구문 분석기를 구현합니다:

from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Start tag:", tag)
        for attr in attrs:
            print("     attr:", attr)

    def handle_endtag(self, tag):
        print("End tag  :", tag)

    def handle_data(self, data):
        print("Data     :", data)

    def handle_comment(self, data):
        print("Comment  :", data)

    def handle_entityref(self, name):
        c = chr(name2codepoint[name])
        print("Named ent:", c)

    def handle_charref(self, name):
        if name.startswith('x'):
            c = chr(int(name[1:], 16))
        else:
            c = chr(int(name))
        print("Num ent  :", c)

    def handle_decl(self, data):
        print("Decl     :", data)

parser = MyHTMLParser()

doctype 구문 분석하기:

>>> parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
...             '"http://www.w3.org/TR/html4/strict.dtd">')
Decl     : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"

몇 가지 어트리뷰트를 가진 요소와 제목을 구문 분석하기:

>>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
Start tag: img
     attr: ('src', 'python-logo.png')
     attr: ('alt', 'The Python logo')
>>>
>>> parser.feed('<h1>Python</h1>')
Start tag: h1
Data     : Python
End tag  : h1

scriptstyle 요소의 내용은 더 구문 분석하지 않고 있는 그대로 반환됩니다:

>>> parser.feed('<style type="text/css">#python { color: green }</style>')
Start tag: style
     attr: ('type', 'text/css')
Data     : #python { color: green }
End tag  : style

>>> parser.feed('<script type="text/javascript">'
...             'alert("<strong>hello!</strong>");</script>')
Start tag: script
     attr: ('type', 'text/javascript')
Data     : alert("<strong>hello!</strong>");
End tag  : script

주석 구문 분석하기:

>>> parser.feed('<!-- a comment -->'
...             '<!--[if IE 9]>IE-specific content<![endif]-->')
Comment  :  a comment
Comment  : [if IE 9]>IE-specific content<![endif]

이름있는 문자 참조와 숫자 문자 참조를 구문 분석하고 올바른 문자로 변환합니다 (참고: 이 3개의 참조는 모두 '>'와 동등합니다):

>>> parser.feed('&gt;&#62;&#x3E;')
Named ent: >
Num ent  : >
Num ent  : >

불완전한 청크를 feed()로 보내는 것이 작동합니다만, handle_data()가 두 번 이상 호출될 수 있습니다 (convert_charrefsTrue로 설정되지 않은 한):

>>> for chunk in ['<sp', 'an>buff', 'ered ', 'text</s', 'pan>']:
...     parser.feed(chunk)
...
Start tag: span
Data     : buff
Data     : ered
Data     : text
End tag  : span

잘못된 HTML(예를 들어, 따옴표 처리되지 않은 어트리뷰트)을 구문 분석하는 것도 동작합니다:

>>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
Start tag: p
Start tag: a
     attr: ('class', 'link')
     attr: ('href', '#main')
Data     : tag soup
End tag  : p
End tag  : a