"html.parser" --- Analisador simples de HTML e XHTML
****************************************************

**Código-fonte:** Lib/html/parser.py

======================================================================

Este módulo define uma classe "HTMLParser" que serve como base para
analisar arquivos de texto formatados em HTML (HyperText Markup
Language) e XHTML.

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

   Cria uma instância de analisador sintático capaz de analisar
   marcação inválida.

   Se *convert_charrefs* for verdadeiro (o padrão), todas as
   referências de caracteres (exceto as de elementos como "script" e
   "style") serão convertidas automaticamente para os caracteres
   Unicode correspondentes.

   Se *scripting* for falso (o padrão), o conteúdo do elemento
   "noscript" será analisado normalmente; se for verdadeiro, será
   retornado como está, sem ser analisado.

   Uma instância de "HTMLParser" recebe dados HTML e chama métodos de
   tratamento quando encontra tags de abertura, tags de fechamento,
   texto, comentários e outros elementos de marcação. O usuário deve
   criar uma subclasse de "HTMLParser" e substituir seus métodos para
   implementar o comportamento desejado.

   Este analisador sintático não verifica se as tags de fechamento
   correspondem às tags de abertura nem chama o manipulador de tags de
   fechamento para elementos que são fechados implicitamente pelo
   fechamento de um elemento externo.

   Alterado na versão 3.4: Adicionado o argumento nomeado
   *convert_charrefs*.

   Alterado na versão 3.5: O valor padrão para o argumento
   *convert_charrefs* agora é "True".

   Alterado na versão 3.14.1: Adicionado o parâmetro *scripting*.


Exemplo de aplicação para análise de HTML
=========================================

Como exemplo básico, abaixo está um analisador HTML simples que usa a
classe "HTMLParser" para exibir as tags de abertura, tags de
fechamento e dados à medida que são encontrados:

   from html.parser import HTMLParser

   class MyHTMLParser(HTMLParser):
       def handle_starttag(self, tag, attrs):
           print("Encontrada a tag de abertura  :", tag)

       def handle_endtag(self, tag):
           print("Encontrada a tag de fechamento:", tag)

       def handle_data(self, data):
           print("Encontrados alguns dados      :", data)

   parser = MyHTMLParser()
   parser.feed('<html><head><title>Teste</title></head>'
               '<body><h1>Me analise!</h1></body></html>')

A saída então será:

   Encontrada a tag de abertura  : html
   Encontrada a tag de abertura  : head
   Encontrada a tag de abertura  : title
   Encontrados alguns dados      : Teste
   Encontrada a tag de fechamento: title
   Encontrada a tag de fechamento: head
   Encontrada a tag de abertura  : body
   Encontrada a tag de abertura  : h1
   Encontrados alguns dados      : Me analise!
   Encontrada a tag de fechamento: h1
   Encontrada a tag de fechamento: body
   Encontrada a tag de fechamento: html


Métodos "HTMLParser"
====================

Instâncias de "HTMLParser" têm os seguintes métodos:

HTMLParser.feed(data)

   Fornece algum texto ao analisador. Ele será processado na medida em
   que consistir em elementos completos; dados incompletos serão
   armazenados em buffer até que mais dados sejam fornecidos ou que o
   método "close()" seja chamado. Os dados devem ser do tipo "str".

HTMLParser.close()

   Força o processamento de todos os dados em buffer como se fossem
   seguidos por uma marca de fim de arquivo. Este método pode ser
   redefinido por uma classe derivada para definir processamento
   adicional no final da entrada, mas a versão redefinida deve sempre
   chamar o método "close()" da classe base "HTMLParser".

HTMLParser.reset()

   Reinicializa a instância. Perde todos os dados não processados.
   Este método é chamado implicitamente no momento da instanciação.

HTMLParser.getpos()

   Retorna o número da linha atual e o deslocamento.

HTMLParser.get_starttag_text()

   Retorna o texto da tag de abertura aberta mais recentemente.
   Normalmente, isso não é necessário para processamento estruturado,
   mas pode ser útil ao lidar com HTML "como implantado" ou para
   regenerar a entrada com alterações mínimas (o espaço em branco
   entre os atributos pode ser preservado, etc.).

Os seguintes métodos são chamados quando elementos de dados ou de
marcação são encontrados e devem ser sobrescritos em uma subclasse. As
implementações da classe base não fazem nada (exceto por
"handle_startendtag()"):

HTMLParser.handle_starttag(tag, attrs)

   Este método é chamado para manipular a tag de abertura de um
   elemento (por exemplo, "<div id="main">").

   O argumento *tag* é o nome da tag convertido para minúsculas. O
   argumento *attrs* é uma lista de pares "(nome, valor)" contendo os
   atributos encontrados dentro dos colchetes "<>" da tag. O *nome*
   será traduzido para minúsculas, as aspas no *valor* foram removidas
   e as referências a caracteres e entidades foram substituídas.

   Por exemplo, para a tag "<A HREF="https://www.cwi.nl/">", este
   método seria chamado como "handle_starttag('a', [('href',
   'https://www.cwi.nl/')])".

   Todas as referências de entidade de "html.entities" são
   substituídas nos valores dos atributos.

HTMLParser.handle_endtag(tag)

   Este método é chamado para manipular a tag de fechamento de um
   elemento (por exemplo, "</div>").

   O argumento *tag* é o nome da etiqueta convertido para letras
   minúsculas.

HTMLParser.handle_startendtag(tag, attrs)

   Similar a "handle_starttag()", mas chamado quando o analisador
   sintático encontra uma tag vazia no estilo XHTML ("<img ... />").
   Este método pode ser substituído por subclasses que requerem essa
   informação lexical específica; a implementação padrão simplesmente
   chama "handle_starttag()" e "handle_endtag()".

HTMLParser.handle_data(data)

   Este método é chamado para processar dados arbitrários (por
   exemplo, nós de texto e o conteúdo de elementos como "script" e
   "style").

HTMLParser.handle_entityref(name)

   Este método é chamado para processar uma referência de caractere
   nomeada no formato "&name;" (por exemplo, "&gt;"), onde *name* é
   uma referência de entidade genérica (por exemplo, "'gt'"). Este
   método só é chamado se *convert_charrefs* for falso.

HTMLParser.handle_charref(name)

   Este método é chamado para processar referências de caracteres
   numéricos decimais e hexadecimais no formato "&#*NNN*;" e
   "&#x*NNN*;". Por exemplo, o equivalente decimal para "&gt;" é
   "&#62;", enquanto o hexadecimal é "&#x3E;"; neste caso, o método
   receberá "'62'" ou "'x3E'". Este método só é chamado se
   *convert_charrefs* for falso.

HTMLParser.handle_comment(data)

   Este método é chamado quando um comentário é encontrado (por
   exemplo, "<!--comentário-->").

   Por exemplo, o comentário "<!-- comentário -->" fará com que este
   método seja chamado com o argumento "' comentário '".

   O conteúdo dos comentários condicionais do Internet Explorer
   (condcoms) também será enviado para este método, portanto, para
   "<!--[if IE 9]>conteúdo específico do IE9<![endif]-->", este método
   receberá "'[if IE 9]>conteúdo específico do IE9<![endif]'".

HTMLParser.handle_decl(decl)

   Este método é chamado para lidar com uma declaração de doctype HTML
   (por exemplo, "<!DOCTYPE html>").

   O parâmetro *decl* será todo o conteúdo da declaração dentro da
   marcação "<!...>" (por exemplo, "'DOCTYPE html'").

HTMLParser.handle_pi(data)

   Método chamado quando uma instrução de processamento é encontrada.
   O parâmetro *data* conterá a instrução de processamento completa.
   Por exemplo, para a instrução de processamento "<?proc
   color='red'>", este método seria chamado como "handle_pi("proc
   color='red'")". Ele foi projetado para ser substituído por uma
   classe derivada; a implementação da classe base não faz nada.

   Nota:

     A classe "HTMLParser" usa as regras sintáticas SGML para
     processar instruções. Uma instrução de processamento XHTML que
     usa o caractere "'?'" no final fará com que o "'?'" seja incluído
     em *data*.

HTMLParser.unknown_decl(data)

   Este método é chamado quando uma declaração não reconhecida é lida
   pelo analisador sintático.

   O parâmetro *data* será todo o conteúdo da declaração dentro da
   marcação "<![...]>". Às vezes, é útil que ele seja sobrescrito por
   uma classe derivada. A implementação da classe base não faz nada.


Exemplos
========

A classe a seguir implementa um analisador sintático que será usado
para ilustrar mais exemplos:

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

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

       def handle_endtag(self, tag):
           print("Tag final  :", tag)

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

       def handle_comment(self, data):
           print("Comentário :", data)

       def handle_entityref(self, name):
           c = chr(name2codepoint[name])
           print("Ent nomeada:", 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()

Analisando um 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"

Analisando um elemento com alguns atributos e um título:

   >>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
   Tag inicial: img
        attr: ('src', 'python-logo.png')
        attr: ('alt', 'The Python logo')
   >>>
   >>> parser.feed('<h1>Python</h1>')
   Tag inicial: h1
   Dados      : Python
   Tag final  : h1

O conteúdo de elementos como "script" e "style" é retornado como está,
sem mais análises:

   >>> parser.feed('<style type="text/css">#python { color: green }</style>')
   Tag inicial: style
        attr: ('type', 'text/css')
   Dados      : #python { color: green }
   Tag final  : style

   >>> parser.feed('<script type="text/javascript">'
   ...             'alert("<strong>hello! &#9786;</strong>");</script>')
   Tag inicial: script
        attr: ('type', 'text/javascript')
   Dados      : alert("<strong>hello! &#9786;</strong>");
   Tag final  : script

Analisando comentários:

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

Analisando referências de caracteres nomeadas e numéricas e
convertendo-as para o caractere correto (nota: essas 3 referências são
todas equivalentes a "'>'"):

   >>> parser = MyHTMLParser()
   >>> parser.feed('&gt;&#62;&#x3E;')
   Data     : >>>

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

Alimentar o "feed()" com blocos incompletos funciona, mas
"handle_data()" pode ser chamado mais de uma vez se *convert_charrefs*
for falso:

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

Analisando HTML inválido (por exemplo, atributos sem aspas) também
funciona:

   >>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
   Tag inicial: p
   Tag inicial: a
        attr: ('class', 'link')
        attr: ('href', '#main')
   Dados      : tag soup
   Tag final  : p
   Tag final  : a
