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)

Cette méthode est appelée pour traiter une balise ouvrante (p. ex. <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