Raclage de pages Web en python avec une belle soupe recherche et modification du DOM

Dans le dernier tutoriel, vous avez appris les bases de la bibliothèque Beautiful Soup. Outre la navigation dans l’arborescence DOM, vous pouvez également rechercher des éléments ayant une valeur donnée. classe ou identifiant. Vous pouvez également modifier l'arborescence DOM à l'aide de cette bibliothèque.. 

Dans ce didacticiel, vous apprendrez différentes méthodes qui vous aideront dans la recherche et les modifications. Nous allons supprimer la même page Wikipedia sur Python de notre dernier tutoriel..

Filtres pour la recherche dans l'arbre

Beautiful Soup a beaucoup de méthodes pour rechercher l'arbre DOM. Ces méthodes sont très similaires et prennent les mêmes types de filtres que les arguments. Par conséquent, il est logique de bien comprendre les différents filtres avant de se renseigner sur les méthodes. Je vais utiliser le même Trouver tout() méthode pour expliquer la différence entre différents filtres.

Le filtre le plus simple que vous pouvez passer à n'importe quelle méthode de recherche est une chaîne. Beautiful Soup cherchera ensuite dans le document pour trouver une étiquette qui correspond exactement à la chaîne.

pour titre dans soup.find_all ('h2'): print (heading.text) # Sommaire # Historique [modifier] # Caractéristiques et philosophie [modifier] # Syntaxe et sémantique [modifier] # Bibliothèques [modifier] # Environnements de développement [modifier] #… etc.

Vous pouvez également passer un objet d’expression régulière au Trouver tout() méthode. Cette fois, Beautiful Soup filtrera l’arbre en faisant correspondre toutes les balises à une expression régulière donnée..

import re pour en-tête dans soup.find_all (re.compile ("^ h [1-6]")): print (heading.name + "+ heading.text.strip ()) # h1 Python (langage de programmation) # h2 Contenu # h2 Historique [modifier] # h2 Caractéristiques et philosophie [modifier] # h2 Syntaxe et sémantique [modifier] # h3 Indentation [modifier] # h3 Déclarations et flux de contrôle [modifier] #… et ainsi de suite.

Le code recherchera toutes les balises commençant par "h" et suivies d'un chiffre compris entre 1 et 6. En d'autres termes, il recherchera toutes les balises de titre du document..

Au lieu d'utiliser regex, vous pouvez obtenir le même résultat en passant une liste de tous les tags que vous souhaitez que Beautiful Soup compare au document..

pour en-tête dans soup.find_all (["h1", "h2", "h3", "h4", "h5", "h6"]): print (entête.nom + "+ entête.text.strip ())

Vous pouvez aussi passer Vrai en tant que paramètre de la Trouver tout() méthode. Le code renverra alors toutes les balises du document. Le résultat ci-dessous signifie que la page Wikipedia contient actuellement 4 339 tags que nous analysons..

len (soup.find_all (True)) # 4339

Si vous ne parvenez toujours pas à trouver ce que vous recherchez avec l’un des filtres ci-dessus, vous pouvez définir votre propre fonction qui prend un élément comme seul argument. La fonction doit aussi retourner Vrai s'il y a une correspondance et Faux autrement. Selon vos besoins, vous pouvez rendre la fonction aussi compliquée que nécessaire. Voici un exemple très simple:

def big_lists (tag): renvoie len (tag.contents)> 20 et tag.name == 'ul' len (soup.find_all (big_lists)) # 13

La fonction ci-dessus parcourt la même page Python Wikipedia et recherche des listes non ordonnées ayant plus de 20 enfants..

Recherche dans l'arborescence DOM à l'aide de fonctions intégrées

L’une des méthodes les plus populaires de recherche dans le DOM est Trouver tout(). Il parcourra tous les descendants de la balise et renverra une liste de tous les descendants correspondant à vos critères de recherche. Cette méthode a la signature suivante:

find_all (nom, attrs, récursif, chaîne, limite, ** kwargs)

le prénom argument est le nom du tag que cette fonction doit rechercher en parcourant l’arbre. Vous êtes libre de fournir une chaîne, une liste, une expression régulière, une fonction ou la valeur Vrai comme un nom.

Vous pouvez également filtrer les éléments de l’arborescence DOM en fonction de différents attributs, tels que identifiant, href, etc. Vous pouvez également obtenir tous les éléments avec un attribut spécifique quelle que soit sa valeur en utilisant attribut = vrai. La recherche d'éléments avec une classe spécifique est différente de la recherche d'attributs normaux. Puisque classe est un mot clé réservé en Python, vous devrez utiliser le classe_ argument de mot-clé lors de la recherche d'éléments avec une classe spécifique.

import re len (soup.find_all (id = True)) # 425 len (soup.find_all (class_ = True)) # 1734 len (soup.find_all (class _ = "mw-headline")) # 20 len (soup.find_f (href = True)) # 1410 len (soup.find_all (href = re.compile ("python"))))) # 102

Vous pouvez voir que le document contient 1 734 balises avec un classe attribut et 425 balises avec un identifiant attribut. Si vous n’avez besoin que des premiers résultats, vous pouvez transmettre un nombre à la méthode en tant que valeur de limite. Si vous passez cette valeur, Beautiful Soup cessera de rechercher plus d'éléments une fois qu'il aura atteint un certain nombre. Voici un exemple:

soup.find_all (class _ = "mw-headline", limit = 4) # L'histoire # Caractéristiques et philosophie # Syntaxe et sémantique # Échancrure

Quand vous utilisez le Trouver tout() méthode, vous dites à Belle Soupe de passer par tous les descendants d’une balise donnée pour trouver ce que vous recherchez. Parfois, vous souhaitez rechercher un élément uniquement dans les enfants directs sur une balise. Ceci peut être réalisé en passant récursif = False au Trouver tout() méthode.

len (soup.html.find_all ("meta")) # 6 len (soup.html.find_all ("meta", récursive = Faux)) # 0 len (soup.head.find_all ("meta", récursive = False) ) # 6

Si vous souhaitez rechercher un seul résultat pour une requête de recherche particulière, vous pouvez utiliser le trouver() méthode pour le trouver au lieu de passer limite = 1 à Trouver tout(). La seule différence entre les résultats renvoyés par ces deux méthodes est que Trouver tout() retourne une liste avec un seul élément et trouver() renvoie simplement le résultat.

soup.find_all ("h2", limite = 1) # [

Contenu

] soup.find ("h2") #

Contenu

le trouver() et Trouver tout() Les méthodes recherchent tous les descendants d’une balise donnée pour rechercher un élément. Il existe dix autres méthodes très similaires que vous pouvez utiliser pour parcourir l’arborescence DOM dans différentes directions..

find_parents (nom, attrs, chaîne, limite, ** kwargs) find_parent (nom, attrs, chaîne, ** kwargs) find_next_siblings (nom, attrs, chaîne, limite, ** kwargs) find_next_sibling (nom, attrs, chaîne, ** kwargs) find_previous_siblings (name, attrs, string, limit, ** kwargs) find_previous_sibling (name, attrs, string, ** kwargs) find_all_next (name, attrs, string, limit, ** kwargs) find_next (name, attrs, string, ** kwargs) find_all_previous (nom, attrs, chaîne, limite, ** kwargs) find_previous (nom, attrs, chaîne, ** kwargs)

le find_parent () et find_parents () les méthodes parcourent l’arbre DOM pour trouver l’élément donné. le find_next_sibling () et find_next_siblings () les méthodes itéreront sur tous les frères et sœurs de l'élément qui viennent après l'élément en cours. De même, le find_previous_sibling () et find_previous_siblings () les méthodes vont parcourir tous les frères et sœurs de l'élément qui précède l'actuel.

le find_next () et find_all_next () Les méthodes itéreront sur toutes les balises et chaînes qui suivent l'élément en cours. De même, le find_previous () et find_all_previous () les méthodes itéreront sur toutes les balises et chaînes précédant l'élément en cours.

Vous pouvez également rechercher des éléments à l'aide de sélecteurs CSS à l'aide du bouton sélectionner() méthode. Voici quelques exemples:

len (soup.select ("p a")) # 411 len (soup.select ("p> a")) # 291 soup.select ("h2: nth-de-type (1)") # [

Contenu

] len (soup.select ("p> a: nth-de-type (2)")) # 46 len (soup.select ("p> a: n-de-type (10)")) # 6 len (soup.select ("[classe * = section]")) # 80 len (soup.select ("[classe $ = section]")) # 20

Modification de l'arbre

Vous pouvez non seulement effectuer une recherche dans l’arborescence DOM pour trouver un élément, mais également le modifier. Il est très facile de renommer une balise et de modifier ses attributs..

heading_tag = soup.select ("h2: nth de type (2)") [0] heading_tag.name = "h3" print (heading_tag) # 

Feat… heading_tag ['class'] = 'printChanted' (imprimer_tag) #

Dans la suite de notre dernier exemple, vous pouvez remplacer le contenu d’une balise par une chaîne donnée en utilisant le .chaîne attribut. Si vous ne voulez pas remplacer le contenu mais ajouter quelque chose de plus à la fin de la balise, vous pouvez utiliser le ajouter() méthode. 

De même, si vous souhaitez insérer quelque chose dans une balise à un emplacement spécifique, vous pouvez utiliser le insérer() méthode. Le premier paramètre de cette méthode est la position ou l'index auquel vous souhaitez insérer le contenu, et le second paramètre est le contenu lui-même. Vous pouvez supprimer tout le contenu d'une balise à l'aide de la touche clair() méthode. Cela vous laissera juste avec la balise elle-même et ses attributs.

heading_tag.string = "Caractéristiques et philosophie" print (heading_tag) # 

Caractéristiques et philosophie

heading_tag.append ("[[Ajout de cette partie].") print (heading_tag) #

Caractéristiques et philosophie [Annexe de cette partie].

print (heading_tag.contents) # ['Caractéristiques et philosophie', '[[Ajout de cette partie]]'] heading_tag.insert (1, 'Inséré cette partie') print (heading_tag) #

Caractéristiques et philosophie Inséré cette partie [Annexe cette partie].

heading_tag.clear () print (heading_tag) #

Au début de cette section, vous avez sélectionné un en-tête de niveau deux dans le document et l'avez remplacé par un en-tête de niveau trois. Si vous utilisez à nouveau le même sélecteur, le titre suivant du niveau deux suivant l’originale est maintenant affiché. Cela a du sens car l’en-tête initial n’est plus un en-tête de niveau deux. 

Le titre original peut maintenant être sélectionné en utilisant h3: nième de type (2). Si vous voulez complètement supprimer un élément ou une balise et tout le contenu qu’il contient de l’arbre, vous pouvez utiliser la commande décomposer() méthode.

soup.select ("h3: nième de type (2)") [0] #  soup.select ("h3: nième de type (3)") [0] # 

Échancrure… Soup.select ("h3: n-de-type (2)") [0] .decompose () soup.select ("h3: n-de-type (2)") [0] #

Échancrure

Une fois que vous avez décomposé ou supprimé le titre original, le titre de la troisième place prend sa place..

Si vous souhaitez supprimer une balise et son contenu de l'arborescence sans vouloir la détruire complètement, vous pouvez utiliser le extrait() méthode. Cette méthode renverra la balise extraite. Vous aurez maintenant deux arbres différents que vous pouvez analyser. La racine du nouvel arbre sera la balise que vous venez d'extraire.

heading_tree = soup.select ("h3: nième de type (2)") [0] .extract () len (heading_tree.contents) # 2

Vous pouvez également remplacer une balise à l'intérieur de l'arborescence par quelque chose d'autre de votre choix à l'aide de la touche remplacer par() méthode. Cette méthode renverra la balise ou la chaîne remplacée. Il peut être utile de placer le contenu remplacé ailleurs dans le document..

soup.h1 # 

Python (langage de programmation)

bold_tag ​​= soup.new_tag ("b") bold_tag.string = "Python" soup.h1.replace_with (bold_tag) print (soup.h1) # Aucune empreinte (soup.b) # Python

Dans le code ci-dessus, l’en-tête du document a été remplacé par un b étiquette. Le document n'a plus de h1 tag, et c'est pourquoi imprimer (soup.h1) maintenant imprime Aucun.

Dernières pensées

Après avoir lu les deux tutoriels de cette série, vous devriez pouvoir analyser différentes pages Web et extraire des données importantes du document. Vous devriez également pouvoir récupérer la page Web d'origine, la modifier pour l'adapter à vos besoins et enregistrer la version modifiée localement..

Si vous avez des questions concernant ce tutoriel, merci de me le faire savoir dans les commentaires..