Nous avons vu précédemment comment parser du XML , il est également possible de parser du HTML et l'outil qui fait le mieux le job selon moi c'est le librairy BeautifulSoup
Installer la bibliothèque BeautifulSoup
Qui dit lib python dit pip
pip install beautifulsoup4
Récupérer le contenu d'une balise spécifiée
BeautifulSoup vous propose par exemple de récupérer toutes les balises p d'une page HTML
# coding: utf-8 from bs4 import BeautifulSoup html_doc = """ <html> <head> <title>Titre de votre site</title> </head> <body> <p>Texte à lire 1</p> <p>Texte à lire 2</p> </body> </html> """ soup = BeautifulSoup(html_doc) for p in soup.find_all('p'): print p
Ce qui retournera:
<p>Texte à lire 1</p> <p>Texte à lire 2</p>
Changer le contenu de balises
Trouver les éléments qui nous intéresse c'est une chose, mais pouvoir les modifier c'est encore mieux!
# coding: utf-8 from bs4 import BeautifulSoup html_doc = """ <html> <head> <title>Titre de votre site</title> </head> <body> <p>Texte à lire 1</p> <p>Texte à lire 2</p> </body> </html> """ soup = BeautifulSoup(html_doc) for p in soup.find_all('p'): p.string = "Nouveau texte" print soup
Résultat:
<html> <head> <title>Titre de votre site</title> </head> <body> <p>Nouveau texte</p> <p>Nouveau texte</p> </body> </html>
Remplacer des balises
Vous pouvez remplacer les balises avec la méthode replace_with :
# coding: utf-8 from bs4 import BeautifulSoup html_doc = """ <html> <head> <title>Titre de votre site</title> </head> <body> <p>Texte à lire 1</p> <p>Texte à lire 2</p> </body> </html> """ soup = BeautifulSoup(html_doc) for p in soup.find_all('p'): n = BeautifulSoup('<pre>%s</pre>' % p.string) p.replace_with(n.body.contents[0]) print soup
Réponse du script:
<html> <head> <title>Titre de votre site</title> </head> <body> <pre>Texte à lire 1</pre> <pre>Texte à lire 2</pre> </body> </html>
Lire les attributs
Il est possible de lire les attributs des éléments avec la méthode get :
# coding: utf-8 from bs4 import BeautifulSoup html_doc = """ <html> <head> <title>Titre de votre site</title> </head> <body> <p class="c1 c2">Texte à lire 1</p> <p class="c3">Texte à lire 2</p> </body> </html> """ soup = BeautifulSoup(html_doc) for p in soup.find_all('p'): print p.get("class")
Résultat:
>>> ['c1', 'c2'] >>> ['c3']
Les méthodes de la classe BeautifulSoup
clear ( decompose=False )
Extrait tous les enfants
decode_contents ( indent_level=None, eventual_encoding='utf-8', formatter='minimal' )
Créer un rendu en chaine unicode
decompose ( )
Detruit récursivement les contenus de l'arbre
encode ( encoding='utf-8', indent_level=None, formatter='minimal', errors='xmlcharrefreplace' )
encode
encode_contents ( indent_level=None, encoding='utf-8', formatter='minimal' )
Créer des rendus du tag en bytestring
find ( name=None, attrs={}, recursive=True, text=None, **kwargs )
Retourne seulement le premier enfant de la balise correspondant pour le critère donné
find_all ( name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs )
Retourne une liste d'objet balise correspondant à la demande.
find ( name=None, attrs={}, recursive=True, text=None, **kwargs )
Retourne seulement le premier enfant de la balise cherchée
findChildren ( name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs )
Retourne une liste d'objet balise correspondant à la demande
get ( key, default=None )
Retourne la valeur de l'attribut "key" de la balise ou retourne la valeur default
get_text ( self, separator=u'', strip=False, types=( <class 'bs4.element.NavigableString'>, <class 'bs4.element.CData'> ) )
Retourne toutes les chaines de caractères des enfants concaténé utilisant le séparateur indiqué
has_attr ( key )
True si l'attribut demandé est présent
has_key ( key )
Vérifie la présence de la clé
index ( element )
Retourne l'index de l'élément
prettify ( self, encoding=None, formatter='minimal' )
Améliore la lecture du code
recursiveChildGenerator ( )
append ( self, tag )
Ajoute la balise donnée à l'objet en cours
extract ( )
Extrait l'élément de l'arbre
find_next_siblings ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Renvoi les objects frères de l'objet en cours
find_parents ( self, name=None, attrs={}, limit=None, **kwargs )
Renvoi les parents
find_all_previous ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne tous les items qui match avec le critère donné avant l'objet en cours
find_previous_siblings ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne les objets frères de l'objet en cours qui sont avant celui-ci
find_all_next ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne les objets qui correpondent à la recherche mais qui se situent après l'objet en cours
find_all_previous ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne les objets qui correspondent à la recherche mais qui se situent avant l'objet en cours
find_next ( self, name=None, attrs={}, text=None, **kwargs )
Retourne le premier objet frère après l'objet en cours
find_next_sibling ( self, name=None, attrs={}, text=None, **kwargs )
Retourne l'objet frère le plus proche après lui
find_next_siblings ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne les objets frères suivants
find_parent ( self, name=None, attrs={}, **kwargs )
Retourne le parent le plus proche
find_parents ( self, name=None, attrs={}, limit=None, **kwargs )
Retourne les parents
find_previous ( self, name=None, attrs={}, text=None, **kwargs )
Retourne le premier item avant l'objet en cours
find_previous_sibling ( self, name=None, attrs={}, text=None, **kwargs )
Retourne l'item frère le plus proche précédent l'objet en cours
find_previous_siblings ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne les items frères les plus proches précédents l'objet en cours
find_all_next ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne tous les items qui suivent l'objet en cours
find_all_previous ( self, name=None, attrs={}, text=None, limit=None, **kwargs )
Retourne tous les items qui précédent l'objet en cours
Exemple d'utilisation
J'avais besoin d'un parseur HTML pour mettre en forme et colorer le code que je présente sur ce site; je partage ce petit script:
# coding: utf-8 import sys import glob from bs4 import BeautifulSoup from pygments import highlight from pygments.lexers import PythonLexer from pygments.formatters import HtmlFormatter def pygments_file(pathname): # On ouvre le fichier with open(pathname, "r" ) as f: html_doc = f.read() soup = BeautifulSoup(html_doc) # On boucle sur les pre trouvés for pre in soup.find_all('pre'): try: if "code" in pre.get("class"): texte = highlight(pre.get_text(), PythonLexer(), \ HtmlFormatter(nowrap=True)) n = BeautifulSoup('%s' % texte) pre.replace_with(n.body.contents[0]) except: print("Erreur avec {}".format(pre,)) if soup.body: with open(pathname, "w") as f: f.write(soup.body.encode_contents()) p = "/home/olivier/*.html" if sys.argv[1]: p = str(sys.argv[1]) pathnames = glob.glob(p) for pathname in pathnames: pygments_file(pathname)
Vous pouvez aussi bien lui renseigner un dossier qu'un seul fichier.