"html.parser"— Un analyseur syntaxique simple pour HTML et XHTML
****************************************************************

**Code source :** Lib/html/parser.py

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

Ce module définit une classe "HTMLParser" qui sert de base pour
l'analyse syntaxique de fichiers texte formatés HTML (*HyperText Mark-
up Language*, le  « langage de balisage hypertexte ») et XHTML
(*EXtensible HyperText Markup Language*, le « langage extensible de
balisage hypertexte »).

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

   Crée une instance d'analyseur capable de traiter tout balisage,
   même invalide.

   Si *convert_charrefs* est "True" (valeur par défaut), toute
   référence de caractère (sauf ceux enchâssés dans des éléments
   "script"/"style") est automatiquement convertie en son caractère
   Unicode.

   Une instance de "HTMLParser" est alimentée par des données HTML.
   Elle fait appel à des méthodes offrant un traitement spécifique
   quand est rencontré un élément de balisage : balise ouvrante ou
   fermante, textes, commentaires… Pour implémenter le comportement
   désiré, l'utilisateur crée une sous-classe de "HTMLParser" en
   surchargeant ses méthodes.

   Cet analyseur ne vérifie ni que les balises fermantes correspondent
   aux balises ouvrantes, ni n'invoque le gestionnaire de balises
   fermantes pour les éléments implicitement fermés par un élément
   extérieur.

   Modifié dans la version 3.4: L'argument *convert_charrefs* a été
   ajouté.

   Modifié dans la version 3.5: La valeur par défaut de l'argument
   *convert_charrefs* est désormais "True".


Exemple d'application de l'analyseur HTML
=========================================

Comme exemple simple, un analyseur HTML minimal qui utilise la classe
"HTMLParser" pour afficher les balises ouvrantes, les balises
fermantes ainsi que les données quand elles apparaissent :

   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 sortie est alors :

   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éthodes de la classe "HTMLParser"
==================================

Les instances de "HTMLParser" disposent des méthodes suivantes :

HTMLParser.feed(data)

   Alimente l'analyseur avec du texte. Ce texte est traité dans la
   mesure où il constitue des éléments complets ; les données
   incomplètes sont mises dans un tampon jusqu'à ce que d'autres
   données soient fournies ou que la méthode "close()" soit appelée.
   L'argument *data* doit être de classe "str".

HTMLParser.close()

   Force le traitement de toutes les données du tampon comme si elles
   étaient suivies par un caractère *fin de fichier*. Cette méthode
   peut-être redéfinie par une classe dérivée pour ajouter des
   traitements supplémentaires à la fin de l'entrée, mais la version
   redéfinie devra impérativement appeler la méthode "close()" de la
   classe de base "HTMLParser".

HTMLParser.reset()

   Réinitialise l'instance. Toutes les données non traitées sont
   perdues. Cette méthode est appelée implicitement lors de
   l'instanciation.

HTMLParser.getpos()

   Renvoie le numéro de ligne et le numéro du caractère dans la ligne
   où le curseur est positionné.

HTMLParser.get_starttag_text()

   Return the text of the most recently opened start tag.  This should
   not normally be needed for structured processing, but may be useful
   in dealing with HTML "as deployed" or for re-generating input with
   minimal changes (whitespace between attributes can be preserved,
   etc.).

Les méthodes suivantes sont appelées lors de la rencontre de données
ou d'éléments de balisage ; elles sont destinées à être surchargées
par la sous-classe. L'implémentation de la classe de base ne fait rien
(sauf pour ce qui est de "handle_startendtag()") :

HTMLParser.handle_starttag(tag, attrs)

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

   L'argument *tag* contient le nom de la balise en minuscules.
   L'argument *attrs* contient une liste de *n-uplets* "(name, value)"
   regroupant les attributs présents entre les symboles "<" et ">" de
   la balise. Le paramètre *name* est converti en minuscule ; les
   guillemets sont supprimés du paramètre *value* et toute entité de
   référence ou de caractère est remplacée.

   Par exemple, pour la balise "<A HREF="https://www.cwi.nl/">", cette
   méthode est appelée par "handle_starttag('a', [('href',
   'https://www.cwi.nl/')])".

   Toute référence d'entité présente dans "html.entities" est
   remplacée dans la valeur des attributs.

HTMLParser.handle_endtag(tag)

   Cette méthode est appelée pour traiter les balises fermantes (p.
   ex. "</div>").

   L'argument *tag* est le nom de la balise en minuscules.

HTMLParser.handle_startendtag(tag, attrs)

   Traitée de façon similaire à "handle_starttag()", mais appelée
   quand l'analyseur rencontre une balise vide de type *XHTML* (p.
   ex. "<img ... />"). Cette méthode peut-être surchargée par les
   sous-classes demandant cette information lexicale ;
   l'implémentation par défaut appelle simplement "handle_starttag()"
   et "handle_endtag()".

HTMLParser.handle_data(data)

   Cette méthode est appelée pour traiter toute donnée arbitraire (p.
   ex. les nœuds textuels ou les contenus de "<script>...</script>" et
   "<style>...</style>").

HTMLParser.handle_entityref(name)

   Cette méthode est appelée pour traiter les références nommées de
   caractères de la forme "&name;" (p.  ex. "&gt;"), où *name* est une
   référence à une entité générique (p.  ex. "'gt'"). Cette méthode
   n'est jamais appelée si *convert_charrefs* vaut "True".

HTMLParser.handle_charref(name)

   Cette méthode est appelée pour traiter les références de caractères
   décimales et hexadécimales de la forme "&#NNN;" et "&#xNNN;". Par
   exemple, l'équivalent décimal de "&gt;" est "&#62;", son équivalent
   hexadécimal étant "&#x3E;" ; dans ce cas, la méthode reçoit "'62'"
   or "'x3E'". Cette méthode n'est jamais appelée si
   *convert_charrefs* est "True".

HTMLParser.handle_comment(data)

   Cette méthode est appelée quand un commentaire (p.  ex. "<!--
   commentaire-->") est rencontré.

   Par exemple, le commentaire "<!-- commentaire -->" provoque l'appel
   de cette méthode avec l'argument "' commentaire '".

   Le contenu des commentaires conditionnels d'Internet Explorer
   (*condcoms*) ne sont pas traités de manière particulière par
   l'analyseur.  Ils sont passés à cette méthode comme tous les autres
   commentaires. Ainsi, pour "<!--[if IE 9]>Contenu spécifique à
   IE9<![endif]-->", cette méthode sera appelée avec "'[if IE
   9]>Contenu spécifique à IE9<![endif]'".

HTMLParser.handle_decl(decl)

   Cette méthode est appelée pour traiter la déclaration *doctype* de
   HTML (p.  ex. "<!DOCTYPE html>").

   Le paramètre *decl* contient la totalité de la déclaration contenue
   dans le balisage "<!...>" (p.  ex. "'DOCTYPE html'").

HTMLParser.handle_pi(data)

   Méthode appelée quand une instruction de traitement est rencontrée.
   Le paramètre *data* contient la totalité de l'instruction de
   traitement. Par exemple, pour l'instruction de traitement "<?proc
   color='rouge'>", cette méthode est appelée par "handle_pi("proc
   color='rouge'")". Elle est destinée à être surchargée dans une
   classe dérivée ; la classe de base ne réalise aucun traitement.

   Note:

     La classe "HTMLParser" utilise les règles syntaxiques de **SGML**
     pour traiter les instructions de traitement. Une instruction de
     traitement *XHTML* utilisant une terminaison en "'?'" se traduit
     par l'inclusion de ce "'?'" dans *data*.

HTMLParser.unknown_decl(data)

   Cette méthode est appelée quand une déclaration non reconnue est
   lue par l'analyseur.

   Le paramètre *data* contient toute la déclaration enchâssée dans le
   balisage "<![...]>". Il est parfois utile de le surcharger dans une
   classe dérivée. L'implémentation de la classe de base ne réalise
   aucune opération.


Exemples
========

La classe suivante implémente un analyseur qui est utilisé dans les
exemples ci-dessous :

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

Traitement du *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"

Analyse d'un élément avec un titre et des attributs :

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

Le contenu des éléments "script" et "style" est renvoyé tel quel (sans
autre traitement) :

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

Traitement des commentaires :

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

L'analyse des caractères nommés et des références numériques de
caractères et leur conversion en leur caractère correct (note : ces
trois références sont équivalentes à "'>'") :

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

Il est possible de fournir des portions de code incomplètes à
"feed()", mais alors il est possible que "handle_data()" soit appelée
plusieurs fois, à moins que *convert_charrefs* soit "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

L'analyse de code *HTML* non valide (p. ex. des attributs sans
guillemets) fonctionne également :

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