# `html.parser` — Analizador simple de HTML y XHTML

Código fuente: Lib/html/parser.py

Este módulo define una clase `HTMLParser` que sirve como base para analizar archivos de texto formateados en HTML (HyperText Mark-up Language) y XHTML.

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

  Cree una instancia de analizador capaz de analizar marcado no válido.

  Si convert_charrefs es `True` (el valor predeterminado), todas las referencias de caracteres (excepto las de los elementos `script`/`style`) se convierten automáticamente en los caracteres Unicode correspondientes.

  Una instancia de `HTMLParser` se alimenta de datos HTML y llama a métodos de manejo cuando se encuentran etiquetas de inicio, etiquetas finales, texto, comentarios y otros elementos de marcado. El usuario debe subclasificar `HTMLParser` y anular sus métodos para implementar el comportamiento deseado.

  Este analizador no verifica que las etiquetas finales coincidan con las etiquetas iniciales ni llame al manejador de etiquetas finales para los elementos que se cierran implícitamente al cerrar un elemento externo.

  Distinto en la versión 3.4: argumento de palabra clave convert_charrefs agregado.

  Distinto en la versión 3.5: El valor predeterminado para el argumento convert_charrefs ahora es `True`.

## Aplicación ejemplo de un analizador sintáctico (parser) de HTML

Como ejemplo básico, a continuación hay un analizador HTML simple que usa la clase `HTMLParser` para imprimir etiquetas de inicio, etiquetas finales y datos a medida que se encuentran:

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

La salida será entonces:

    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

## Métodos `HTMLParser`

instancias de `HTMLParser` tienen los siguientes métodos:

HTMLParser.feed(data)

  Alimente un poco de texto al analizador. Se procesa en la medida en que consta de elementos completos; los datos incompletos se almacenan en el búfer hasta que se introducen más datos o se llama a `close()`. data debe ser `str`.

HTMLParser.close()

  Fuerce el procesamiento de todos los datos almacenados como si fueran seguidos por una marca de fin de archivo. Este método puede ser redefinido por una clase derivada para definir un procesamiento adicional al final de la entrada, pero la versión redefinida siempre debe llamar a `HTMLParser` método de clase base `close()`.

HTMLParser.reset()

  Restablecer la instancia. Pierde todos los datos no procesados. Esto se llama implícitamente en el momento de la instanciación.

HTMLParser.getpos()

  Retorna el número de línea actual y el desplazamiento.

HTMLParser.get_starttag_text()

  Retorna el texto de la etiqueta de inicio abierta más recientemente. Normalmente, esto no debería ser necesario para el procesamiento estructurado, pero puede ser útil para tratar con HTML «como implementado» o para volver a generar entradas con cambios mínimos (se puede preservar el espacio en blanco entre los atributos, etc.).

Los siguientes métodos se invocan cuando se encuentran datos o elementos de marcado y deben anularse en una subclase. Las implementaciones de la clase base no hacen nada (excepto `handle_startendtag()`):

HTMLParser.handle_starttag(tag, attrs)

  This method is called to handle the start tag of an element (e.g. `<div id="main">`).

  El argumento tag es el nombre de la etiqueta convertida a minúsculas. El argumento attrs es una lista de pares `(nombre, valor)` que contienen los atributos encontrados dentro de los corchetes `<>` de la etiqueta. El name se traducirá a minúsculas, se eliminarán las comillas en el value y se reemplazarán las referencias de caracteres y entidades.

  Por ejemplo, para la etiqueta `<A HREF="https://www.cwi.nl/">`, este método se llamaría como `handle_starttag('a', [('href', 'https : //www.cwi.nl/ ')])`.

  Todas las referencias de entidad de `html.entities` se reemplazan en los valores de los atributos.

HTMLParser.handle_endtag(tag)

  Este método se llama para manejar la etiqueta final de un elemento (por ejemplo, `</div>`)

  El argumento tag es el nombre de la etiqueta convertida a minúsculas.

HTMLParser.handle_startendtag(tag, attrs)

  Similar a `handle_starttag()`, pero llamado cuando el analizador encuentra una etiqueta vacía de estilo XHTML (`<img .../>`). Este método puede ser anulado por subclases que requieren esta información léxica particular; la implementación predeterminada simplemente llama `handle_starttag()` y `handle_endtag()`.

HTMLParser.handle_data(data)

  Este método se llama para procesar datos arbitrarios (por ejemplo, nodos de texto y el contenido de `<script>...</script>` y `<style>...</style>`).

HTMLParser.handle_entityref(name)

  Este método se llama para procesar una referencia de caracteres con nombre del formulario `&name;` (por ejemplo, `&gt;`), donde name es una referencia de entidad general (por ejemplo, `'gt'`). Este método nunca se llama si convert_charrefs es `True`.

HTMLParser.handle_charref(name)

  This method is called to process decimal and hexadecimal numeric character references of the form `&#NNN;` and `&#xNNN;`. For example, the decimal equivalent for `&gt;` is `&#62;`, whereas the hexadecimal is `&#x3E;`; in this case the method will receive `'62'` or `'x3E'`. This method is never called if convert_charrefs is `True`.

HTMLParser.handle_comment(data)

  Este método se llama cuando se encuentra un comentario (por ejemplo, `<!--comment-->`).

  Por ejemplo, el comentario `<! - comment ->` hará que se llame a este método con el argumento `'comment'`.

  El contenido de los comentarios condicionales de Internet Explorer (condcoms) también se enviará a este método, por lo tanto, para `<!--[if IE 9]>IE9-specific content<![endif]-->`, este método recibirá `'[if IE 9]>IE9-specific content<![endif]'`.

HTMLParser.handle_decl(decl)

  Este método se llama para manejar una declaración de tipo de documento HTML (por ejemplo, `<!DOCTYPE html>`).

  El parámetro decl será todo el contenido de la declaración dentro del `<!...>` markup (por ejemplo, `'DOCTYPE html'`).

HTMLParser.handle_pi(data)

  Método llamado cuando se encuentra una instrucción de procesamiento. El parámetro data contendrá toda la instrucción de procesamiento. Por ejemplo, para la instrucción de procesamiento `<?proc color='red'>`, este método se llamaría como `handle_pi("proc color='red'")`. Está destinado a ser anulado por una clase derivada; La implementación de la clase base no hace nada.

  Nota

  La clase `HTMLParser` utiliza las reglas sintácticas SGML para procesar instrucciones. Una instrucción de procesamiento XHTML que use el `'?'` final hará que se incluya el `'?'` en data.

HTMLParser.unknown_decl(data)

  Se llama a este método cuando el analizador lee una declaración no reconocida.

  El parámetro data será el contenido completo de la declaración dentro del marcado `<! [...]>`. A veces es útil ser reemplazado por una clase derivada. La implementación de la clase base no hace nada.

## Ejemplos

La siguiente clase implementa un analizador que se utilizará para ilustrar más ejemplos:

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

Analizando un 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"

Analizando un elemento con algunos atributos y un título:

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

El contenido de los elementos `script` y `style` se retorna tal cual, sin más análisis

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

Analizando comentarios:

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

Analizar referencias de caracteres con nombre y numéricos y convertirlos al carácter correcto (nota: estas 3 referencias son todas equivalentes a `'>'`):

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

La alimentación de fragmentos incompletos a `feed()` funciona, pero `handle_data()` podría llamarse más de una vez (a menos que convert_charrefs esté configurado como `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

Analizar HTML no válido (por ejemplo, atributos sin comillas) también funciona:

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