8.3. Extraction de données de documents HTML

Pour extraire des données de documents HTML, on dérive une classe de SGMLParser et on définit des méthodes pour chaque balise ou entité que l’on souhaite traiter.

La première étape pour extraire des données d’un document HTML est d’obtenir le HTML. Si vous avez un fichier HTML, vous pouvez le lire à l’aide des fonctions de fichier, mais le plus intéressant est d’obtenir le HTML depuis des sites web.

Exemple 8.5. Présentation de urllib

>>> import urllib                                       1
>>> sock = urllib.urlopen("http://diveintopython.org/") 2
>>> htmlSource = sock.read()                            3
>>> sock.close()                                        4
>>> print htmlSource                                    5
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head>
      <meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1'>
   <title>Dive Into Python</title>
<link rel='stylesheet' href='diveintopython.css' type='text/css'>
<link rev='made' href='mailto:[email protected]'>
<meta name='keywords' content='Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free'>
<meta name='description' content='a free Python tutorial for experienced programmers'>
</head>
<body bgcolor='white' text='black' link='#0000FF' vlink='#840084' alink='#0000FF'>
<table cellpadding='0' cellspacing='0' border='0' width='100%'>
<tr><td class='header' width='1%' valign='top'>diveintopython.org</td>
<td width='99%' align='right'><hr size='1' noshade></td></tr>
<tr><td class='tagline' colspan='2'>Python&nbsp;for&nbsp;experienced&nbsp;programmers</td></tr>

[...coupé...]
1 Le module urllib fait partie de la bibliothèque standard de Python. Il comprend des fonctions permettant d’obtenir des information et des données à partir d’URLs (principalement des pages web).
2 L’usage le plus simple de urllib est de lire le texte complet d’une page web à l’aide de la fonction urlopen. L’ouverture d’une URL est semblable à l’ouverture d’un fichier. La valeur de retour de urlopen est un objet semblable à un objet-fichier et dont certaines méthodes sont les mêmes que celles d’un objet-fichier.
3 La chose la plus simple à faire avec l’objet retourné par urlopen est d’appeler read, qui lit l’ensemble du code HTML de la page web en une chaîne unique. L’objet permet également l’emploi de readlines, qui lit le code ligne par ligne et le stocke dans une liste.
4 Quand vous n’en avez plus besoin, assurez vous de fermer l’objet par un close, comme pour un objet fichier.
5 Nous avons maintenant l’ensemble du code HTML de la page d’accueil de http://diveintopython.org/ dans une chaîne et nous sommes prêts à l’analyser.

Exemple 8.6. Présentation de urllister.py

Si vous ne l’avez pas déjà fait, vous pouvez télécharger cet exemple ainsi que les autres exemples du livre.


from sgmllib import SGMLParser

class URLLister(SGMLParser):
    def reset(self):                              1
        SGMLParser.reset(self)
        self.urls = []

    def start_a(self, attrs):                     2
        href = [v for k, v in attrs if k=='href'] 3 4
        if href:
            self.urls.extend(href)
1 reset est appelé par la méthode __init__ de SGMLParser et peut également être appelé manuellement quand une instance de l’analyseur a été créée. Donc, si vous avez une quelconque initialisation à faire, faites la dans reset et pas dans __init__, de manière à ce que la réinitialisation se fasse correctement lorsque quelqu’un réutilise une instance de l’analyseur.
2 start_a est appelé par SGMLParser à chaque fois qu’il trouve une balise <a>. La balise peut contenir un attribut href et/ou d’autres attributs comme name ou title. Le paramètre attrs est une liste de tuples, [(attribut, valeur), (attribut, valeur), ...]. La balise peut aussi être un simple <a>, ce qui est une balise HTML valide (bien qu’inutile), dans ce cas attrs sera une liste vide.
3 Nous pouvons savoir si la balise <a> a un attribut href à l’aide d’une simple mutation de liste multi-variable.
4 Les comparaisons de chaînes comme k=='href' sont toujours sensibles à la casse, mais ça ne pose pas de problème ici car SGMLParser convertit les noms d’attributs en minuscules lors de la création de attrs.

Exemple 8.7. Utilisation de urllister.py

>>> import urllib, urllister
>>> usock = urllib.urlopen("http://diveintopython.org/")
>>> parser = urllister.URLLister()
>>> parser.feed(usock.read())         1
>>> usock.close()                     2
>>> parser.close()                    3
>>> for url in parser.urls: print url 4
toc/index.html
#download
#languages
toc/index.html
appendix/history.html
download/diveintopython-html-5.0.zip
download/diveintopython-pdf-5.0.zip
download/diveintopython-word-5.0.zip
download/diveintopython-text-5.0.zip
download/diveintopython-html-flat-5.0.zip
download/diveintopython-xml-5.0.zip
download/diveintopython-common-5.0.zip


... nous coupons la suite pour rester bref ...
1 Appelez la méthode feed, définie dans SGMLParser, pour charger le code HTML dans l’analyseur.[3] La méthode prend une chaîne en argument, ce qui est ce que usock.read() retourne.
2 Comme pour les fichiers, vous devez fermer vos objets URL par close dès que vous n’en avez plus besoin.
3 Vous devez également fermer l’objet analyseur par close, mais pour une raison différente. La méthode feed ne garantit pas qu’elle traite tout le code HTML que vous lui passez, elle peut la garder dans un tampon en attendant que vous lui en passiez plus. Quand il n’y en a plus, appelez close pour vider le tampon et forcer l’analyse de tout le code.
4 Une fois le parser fermé par close, l’analyse est complète et parser.urls contient une liste de toutes les URLs pour lesquelles il y a un lien dans le document HTML.

Footnotes

[3] Le terme technique pour un analyseur comme SGMLParser est consommateur: il consomme du HTML est le décompose. On peut penser que le nom de feed (nourrir) a été choisi pour cadrer avec ce modèle du «consommateur». Personnellement, ça me fait penser à une cage sombre dans un zoo, sans arbres ni plantes ni trace de vie d’aucune sorte, mais où vous pouvez deviner, si vous vous tenez tout à fait immobile, deux yeux en vrilles qui vous regardent en retour dans le coin du fond à gauche, mais vous arrivez à vous convaincre que c’est votre esprit qui vous joue des tours et la seule chose qui vous permet de dire que ce n’est pas une cage vide est une petite pancarte sur la rambarde sur laquelle est écrit «Ne pas nourrir l’analyseur.» Mais peut-être que j’ai trop d’imagination. De toute manière, c’est une image mentale intéressante.