# 20.2。 `html.parser` - 简单的HTML和XHTML解析器

源代码： Lib / html / parser.py

此模块定义了一个类`HTMLParser`，作为解析以HTML（超文本标记语言）和XHTML格式化的文本文件的基础。

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

  创建一个解析器实例能够解析无效标记。

  如果convert_charrefs是`True`（默认值），所有字符引用（`script` / `style`会自动转换为相应的Unicode字符。

  当遇到开始标签，结束标签，文本，注释和其他标记元素时，`HTMLParser`实例提供HTML数据并调用处理程序方法。用户应该子类化`HTMLParser`并覆盖其方法以实现所需的行为。

  此解析器不检查结束标记是否匹配开始标记，或者通过关闭外部元素来隐式关闭的元素调用结束标记处理程序。

  在版本3.4中已更改： convert_charrefs添加了关键字参数。

  在版本3.5中更改：参数convert_charrefs的默认值现在为`True`。

## 20.2.1. 示例HTML解析器应用程序

作为一个基本示例，下面是一个简单的HTML解析器，它使用`HTMLParser`类打印出遇到的开始标签，结束标签和数据：

    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

## 20.2.2. `HTMLParser`方法

`HTMLParser`实例具有以下方法：

`HTMLParser.``feed`(data)

  将一些文本提供给解析器。若包含的元素完整，则它将被解析器处理；不完整的数据将被缓冲，直到送入更多数据或调用`close()`。数据必须为`str`。

`HTMLParser.``close`()

  强制处理所有缓冲的数据，就好像它后面是文件结束标记。此方法可以由派生类重新定义，以在输入结束时定义附加处理，但重新定义的版本应始终调用`HTMLParser`基类方法`close()`。

`HTMLParser.``reset`()

  重置实例。丢失所有未处理的数据。这在实例化时隐式调用。

`HTMLParser.``getpos`()

  返回当前行号和偏移量。

`HTMLParser.``get_starttag_text`()

  返回最近打开的开始标记的文本。这通常不需要用于结构化处理，但是可能在处理“部署的”或用于以最小改变重新生成输入（可以保留属性之间的空格等）时有用。）。

当遇到数据或标记元素并且它们意图在子类中被覆盖时，调用以下方法。基类实现什么也不做（除了`handle_startendtag()`）：

`HTMLParser.``handle_starttag`(tag, attrs)

  调用此方法来处理标记的开始（例如，`＆lt； div id =“main”＆gt；`）。

  tag参数是转换为小写的标签的名称。The attrs argument is a list of `(name, value)` pairs containing the attributes found inside the tag’s `<>` brackets. name将被转换为小写，value中的引号已被移除，字符和实体引用已被替换。

  对于实例，对于标签`＆lt； A HREF =“https://www.cwi.nl/”＆gt；`称为`handle_starttag（'a'， [（'href'， 'https://www.cwi.nl/'）]）` 。

  来自`html.entities`的所有实体引用都将在属性值中替换。

`HTMLParser.``handle_endtag`(tag)

  调用此方法来处理元素的结束标记（例如，`</div>`）。

  标签参数是转换为小写的标签的名称。

`HTMLParser.``handle_startendtag`(tag, attrs)

  Similar to `handle_starttag()`, but called when the parser encounters an XHTML-style empty tag (`<img ... />`). 这个方法可以被需要这个特定词汇信息的子类覆盖；默认实现只需调用`handle_starttag()`和`handle_endtag()`。

`HTMLParser.``handle_data`(data)

  调用此方法来处理任意数据（例如，文本节点和`<script>...</script>`和`<style>...</style>`）的内容。

`HTMLParser.``handle_entityref`(name)

  调用此方法来处理形式为`&name;`的命名字符引用（例如，`&gt;`），其中name是一般实体引用`'gt'`）。如果convert_charrefs为`True`，则不会调用此方法。

`HTMLParser.``handle_charref`(name)

  调用此方法以处理形式为`&#NNN;`和`&#xNNN;`的十进制和十六进制数字字符引用。例如，`&gt;`的十进制等于`&#62;`，而十六进制是`&#x3E;`；在这种情况下，该方法将接收`'62'`或`'x3E'`。如果convert_charrefs为`True`，则不会调用此方法。

`HTMLParser.``handle_comment`(data)

  当遇到注释时调用此方法。`<!--comment-->`）。

  例如，注释`注释 - >`将导致调用此方法与参数`' 注释 '`。

  The content of Internet Explorer conditional comments (condcoms) will also be sent to this method, so, for `<!--[if IE 9]>IE9-specific content<![endif]-->`, this method will receive `'[if IE 9]>IE9-specific content<![endif]'`.

`HTMLParser.``handle_decl`(decl)

  调用此方法来处理HTML doctype声明。`＆lt；！DOCTYPE html＆gt；`）。

  decl t>参数将是`<!...>`标记内声明的全部内容。`'DOCTYPE html'`）。

`HTMLParser.``handle_pi`(data)

  遇到处理指令时调用的方法。数据参数将包含整个处理指令。For example, for the processing instruction `<?proc color='red'>`, this method would be called as `handle_pi("proc color='red'")`. 它的目的是被一个派生类覆盖；基类实现什么也不做。

  注意

  `HTMLParser`类使用SGML语法规则来处理指令。使用尾随`'?'`的XHTML处理指令将导致`'?'`以包括在数据中。

`HTMLParser.``unknown_decl`(data)

  当解析器读取无法识别的声明时，将调用此方法。

  data参数将是`<![...]>`标记内声明的全部内容。有时有用的是被派生类覆盖。基类实现什么也不做。

## 20.2.3. 实例

下面的类实现了一个解析器，将用来说明更多的例子：

    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

`script`和`style`元素的内容按原样返回，无需进一步解析：

    >>> 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]

解析命名和数字字符引用并将它们转换为正确的char（注意：这3个引用都等效于`'>'`）：

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

向`feed()`提供不完整的块可以工作，但`handle_data()`可能会被调用多次（除非convert_charrefs设置为`True`）：

    >>> 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