Comment gratter des pages Web avec une belle soupe et Python 3

introduction

De nombreux projets d’analyse de données, de données volumineuses et d’apprentissage automatique nécessitent le raclage de sites Web pour rassembler les données avec lesquelles vous travaillerez. Le langage de programmation Python est largement utilisé dans la communauté des technologies de l'information et dispose donc d'un écosystème de modules et d'outils que vous pouvez utiliser dans vos propres projets. Dans ce tutoriel, nous allons nous concentrer sur le module Beautiful Soup.

Beautiful Soup, une allusion à la chansonMock Turtle’s trouvée dans le chapitre 10 deAlice’s Adventures in Wonderland de Lewis Carroll, est une bibliothèque Python qui permet une rotation rapide des projets de web scraping. Actuellement disponible en tant que Beautiful Soup 4 et compatible avec Python 2.7 et Python 3, Beautiful Soup crée un arbre d'analyse à partir de documents HTML et XML analysés (y compris des documents avec des balises non fermées ou destag soup et d'autres balisages malformés).

Dans ce didacticiel, nous allons collecter et analyser une page Web afin de récupérer des données textuelles et d’écrire les informations que nous avons rassemblées dans un fichier CSV.

Conditions préalables

Avant de travailler sur ce didacticiel, vous devez avoir un environnement de programmation Pythonlocal ouserver-based configuré sur votre machine.

Vous devriez avoir les modules Requests et Beautiful Soupinstalled, que vous pouvez réaliser en suivant notre tutoriel «https://www.digitalocean.com/community/tutorials/how-to-work-with-web-data- using-requests-and-beautiful-soup-with-python-3 [Comment travailler avec des données Web à l'aide de requêtes et de Beautiful Soup avec Python 3]. » Il serait également utile de se familiariser avec ces modules.

De plus, étant donné que nous allons utiliser des données extraites du Web, vous devriez être à l'aise avec la structure et le balisage HTML.

Comprendre les données

Dans ce didacticiel, nous allons travailler avec les données du site Web officiel desNational Gallery of Art aux États-Unis. La National Gallery est un musée d'art situé dans le National Mall à Washington, DC. Il contient plus de 120 000 œuvres datant de la Renaissance à nos jours et réalisées par plus de 13 000 artistes.

Nous souhaitons effectuer une recherche dans l’index des artistes qui, au moment de la mise à jour de ce tutoriel, est disponible via lesInternet ArchiveWayback Machine à l’URL suivante:

[.Remarque]##

Note: La longue URL ci-dessus est due au fait que ce site Web a été archivé par Internet Archive.

Les archives Internet sont une bibliothèque numérique à but non lucratif qui offre un accès gratuit aux sites Internet et autres médias numériques. Cette organisation prend des instantanés de sites Web pour préserver l'historique des sites, et nous pouvons actuellement accéder à une ancienne version du site de la National Gallery qui était disponible lorsque ce didacticiel a été écrit pour la première fois. Les archives Internet sont un bon outil à garder à l'esprit lors de tout type de grattage de données historiques, y compris la comparaison entre les itérations du même site et les données disponibles.

Sous l’entête d’Internet Archive, vous verrez une page qui ressemble à ceci:

Index of Artists Landing Page

Comme nous allons faire ce projet pour en savoir plus sur le nettoyage Web avec Beautiful Soup, nous n’avons pas besoin de extraire trop de données du site. Limitons donc la portée des données sur les artistes que nous cherchons à supprimer. Choisissons donc une lettre - dans notre exemple nous allons choisir la lettreZ - et nous verrons une page qui ressemble à ceci:

Artist names beginning with Z list

Dans la page ci-dessus, nous voyons que le premier artiste répertorié au moment de la rédaction estZabaglia, Niccola, ce qui est une bonne chose à noter lorsque nous commençons à extraire des données. Nous allons commencer par travailler avec cette première page, avec l'URL suivante pour la lettreZ:

Il est important de noter pour plus tard le nombre total de pages de la lettre que vous choisissez de lister, que vous pouvez découvrir en cliquant sur la dernière page des artistes. Dans ce cas, il y a 4 pages au total, et le dernier artiste répertorié au moment de la rédaction estZykmund, Václav. La dernière page des artistes deZ a l'URL suivante:

However, vous pouvez également accéder à la page ci-dessus en utilisant la même chaîne numérique Internet Archive de la première page:

Il est important de noter que nous allons parcourir ces pages plus tard dans ce tutoriel.

Pour commencer à vous familiariser avec la configuration de cette page Web, vous pouvez jeter un œil à sesDOM, qui vous aideront à comprendre comment le HTML est structuré. Pour inspecter le DOM, vous pouvez ouvrir lesDeveloper Tools de votre navigateur.

Importer les bibliothèques

Pour commencer notre projet de codage, activons notre environnement de programmation Python 3. Assurez-vous que vous êtes dans le répertoire où se trouve votre environnement et exécutez la commande suivante:

. my_env/bin/activate

Avec notre environnement de programmation activé, nous allons créer un nouveau fichier, avec nano par exemple. Vous pouvez nommer votre fichier comme vous le souhaitez, nous l'appelleronsnga_z_artists.py dans ce tutoriel.

nano nga_z_artists.py

Dans ce fichier, nous pouvons commencer à importer les bibliothèques que nous utiliserons -Requests et Beautiful Soup.

La bibliothèque Requests vous permet d'utiliser le protocole HTTP au sein de vos programmes Python de manière lisible par l'homme, et le module Beautiful Soup est conçu pour effectuer rapidement le grattage Web.

Nous importerons les requêtes et la belle soupe avec lesimport statement. Pour Beautiful Soup, nous l’importerons depuisbs4, le package dans lequel se trouve Beautiful Soup 4.

nga_z_artists.py

# Import libraries
import requests
from bs4 import BeautifulSoup

Avec les modules Requests et Beautiful Soup importés, nous pouvons commencer par collecter une page, puis l’analyser.

Collecte et analyse d'une page Web

L'étape suivante consiste à collecter l'URL de la première page Web contenant des demandes. Nous attribuerons l'URL de la première page auxvariablepage en utilisant lesmethod requests.get().

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


# Collect first page of artists’ list
page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

Note: Comme l'URL est longue, le code ci-dessus et tout au long de ce didacticiel ne passera pasPEP 8 E501 qui marque les lignes de plus de 79 caractères. Vous souhaiterez peut-être attribuer l'URL à une variable pour rendre le code plus lisible dans les versions finales. Le code de ce didacticiel est à des fins de démonstration et vous permettra d'échanger des URL plus courtes dans le cadre de vos propres projets.

Nous allons maintenant créer un objetBeautifulSoup ou un arbre d’analyse. Cet objet prend comme argument le documentpage.text des requêtes (le contenu de la réponse du serveur), puis l’analyse à partir deshtml.parser intégrés à Python.

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

# Create a BeautifulSoup object
soup = BeautifulSoup(page.text, 'html.parser')

Avec notre page collectée, analysée et configurée comme objetBeautifulSoup, nous pouvons passer à la collecte des données que nous souhaitons.

Tirer du texte d'une page Web

Pour ce projet, nous collecterons les noms d’artistes et les liens pertinents disponibles sur le site Web. Vous voudrez peut-être collecter différentes données, telles que la nationalité et les dates des artistes. Quelles que soient les données que vous souhaitez collecter, vous devez savoir comment elles sont décrites par le DOM de la page Web.

Pour ce faire, dans votre navigateur Web, faites un clic droit - ouCTRL + clic sur macOS - sur le nom du premier artiste,Zabaglia, Niccola. Dans le menu contextuel qui apparaît, vous devriez voir un élément de menu similaire àInspect Element (Firefox) ouInspect (Chrome).

Context Menu — Inspect Element

Une fois que vous avez cliqué sur l'élément de menuInspect approprié, les outils pour les développeurs Web devraient apparaître dans votre navigateur. Nous souhaitons rechercher la classe et les balises associées aux noms d’artistes figurant dans cette liste.

Web Page Inspector

Nous verrons d'abord que la table des noms est comprise entre les balises<div>class="BodyText". Il est important de noter que nous ne cherchons que du texte dans cette section de la page Web. Nous remarquons également que le nomZabaglia, Niccola est dans une balise de lien, puisque le nom fait référence à une page Web qui décrit l'artiste. Nous voudrons donc référencer la balise<a> pour les liens. Le nom de chaque artiste est une référence à un lien.

Pour ce faire, nous utiliserons les méthodesfind() etfind_all() de Beautiful Soup afin d'extraire le texte des noms d'artistes desBodyText<div>.

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


# Collect and parse first page
page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')
soup = BeautifulSoup(page.text, 'html.parser')

# Pull all text from the BodyText div
artist_name_list = soup.find(class_='BodyText')

# Pull text from all instances of  tag within BodyText div
artist_name_list_items = artist_name_list.find_all('a')

Ensuite, en bas de notre fichier programme, nous voudrons créer unfor loop afin d'itérer sur tous les noms d'artistes que nous venons de mettre dans la variableartist_name_list_items.

Nous allons imprimer ces noms avec la méthodeprettify() afin de transformer l'arborescence d'analyse de Beautiful Soup en une chaîne Unicode bien formatée.

nga_z_artists.py

...
artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

# Create for loop to print out all artists' names
for artist_name in artist_name_list_items:
    print(artist_name.prettify())

Lançons le programme tel que nous l’avons fait jusqu’à présent:

python nga_z_artists.py

Une fois que nous le faisons, nous recevrons le résultat suivant:

Ce que nous voyons dans la sortie à ce stade, c'est le texte intégral et les balises liées à tous les noms d'artistes dans les balises<a> trouvées dans la balise<div class="BodyText"> sur la première page, ainsi que quelques texte du lien en bas. Puisque nous ne voulons pas de ces informations supplémentaires, travaillons à les supprimer dans la section suivante.

Suppression des données superflues

Jusqu'à présent, nous avons pu collecter toutes les données de texte de lien dans une section<div> de notre page Web. Cependant, nous ne souhaitons pas que les liens du bas ne mentionnent pas les noms d’artistes. Nous allons donc travailler pour supprimer cette partie.

Pour supprimer les liens du bas de la page, faisons à nouveau un clic droit etInspect le DOM. Nous verrons que les liens en bas de la section<div class="BodyText"> sont contenus dans un tableau HTML:<table class="AlphaNav">:

Links in AlphaNav HTML Table

On peut donc utiliser Beautiful Soup pour trouver la classeAlphaNav et utiliser la méthodedecompose() pour supprimer une balise de l'arborescence d'analyse et la détruire avec son contenu.

Nous utiliserons la variablelast_links pour référencer ces liens inférieurs et les ajouter au fichier programme:

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

soup = BeautifulSoup(page.text, 'html.parser')

# Remove bottom links
last_links = soup.find(class_='AlphaNav')
last_links.decompose()

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

for artist_name in artist_name_list_items:
    print(artist_name.prettify())

Maintenant, lorsque nous exécutons le programme avec la commandepython nga_z_artist.py, nous recevrons le résultat suivant:

À ce stade, nous voyons que la sortie n’inclut plus les liens au bas de la page Web et affiche désormais uniquement les liens associés aux noms d’artistes.

Jusqu’à présent, nous avons ciblé les liens avec les noms d’artistes en particulier, mais nous disposons des données de balises supplémentaires que nous ne souhaitons pas vraiment. Supprimons cela dans la section suivante.

Tirer le contenu d'une étiquette

Afin d’accéder uniquement aux noms réels des artistes, nous souhaitons cibler le contenu des balises<a> plutôt que d’imprimer la balise de lien entière.

Nous pouvons le faire avec les.contents de Beautiful Soup, qui renverront les enfants de la balise sous forme delist data type Python.

Révisons la bouclefor pour qu'au lieu d'imprimer le lien entier et sa balise, nous imprimons la liste des enfants (c'est-à-dire les noms complets des artistes):

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

soup = BeautifulSoup(page.text, 'html.parser')

last_links = soup.find(class_='AlphaNav')
last_links.decompose()

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

# Use .contents to pull out the  tag’s children
for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    print(names)

Nous pouvons exécuter le programme avec la commandepython pour afficher la sortie suivante:

OutputZabaglia, Niccola
Zaccone, Fabian
Zadkine, Ossip
...
Zanini-Viola, Giuseppe
Zanotti, Giampietro
Zao Wou-Ki

Nous avons reçu une liste de tous les noms d’artistes disponibles sur la première page de la lettreZ.

Cependant, que se passe-t-il si nous voulons également capturer les URL associées à ces artistes? Nous pouvons extraire les URL trouvées dans les balises<a> d'une page en utilisant la méthodeget('href') de Beautiful Soup.

D'après la sortie des liens ci-dessus, nous savons que l'URL entière n'est pas capturée, nous allons doncconcatenate la chaîne de lien avec l'avant de la chaîne d'URL (dans ce cashttps://web.archive.org/).

Nous allons également ajouter ces lignes à la bouclefor:

nga_z_artists.py

...
for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    links = 'https://web.archive.org' + artist_name.get('href')
    print(names)
    print(links)

Lorsque nous exécuterons le programme ci-dessus, nous recevronsboth les noms des artistes et les URL vers les liens qui nous en disent plus sur les artistes:

OutputZabaglia, Niccola
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630
Zaccone, Fabian
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202
...
Zanotti, Giampietro
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11631
Zao Wou-Ki
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427

Bien que nous obtenions maintenant des informations sur le site Web, celui-ci n’est en train d’imprimer que dans la fenêtre de notre terminal. Saisissons plutôt ces données afin de pouvoir les utiliser ailleurs en les écrivant dans un fichier.

Écrire les données dans un fichier CSV

La collecte de données qui ne vit que dans une fenêtre de terminal n’est pas très utile. Les fichiers CSV (Comma-Separated Values) nous permettent de stocker des données tabulaires en texte brut. C'est un format courant pour les feuilles de calcul et les bases de données. Avant de commencer cette section, vous devez vous familiariser avechow to handle plain text files in Python.

Tout d'abord, nous devons importer le module intégrécsv de Python avec les autres modules en haut du fichier de programmation Python:

import csv

Ensuite, nous allons créer et ouvrir un fichier appeléz-artist-names.csv pour nous verswrite to (nous utiliserons la variablef pour fichier ici) en utilisant le mode'w'. Nous écrirons également les en-têtes de ligne du haut:Name etLink que nous passerons à la méthodewriterow() sous forme de liste:

f = csv.writer(open('z-artist-names.csv', 'w'))
f.writerow(['Name', 'Link'])

Enfin, dans notre bouclefor, nous écrirons chaque ligne avec lesnames des artistes et leurslinks associés:

f.writerow([names, links])

Vous pouvez voir les lignes pour chacune de ces tâches dans le fichier ci-dessous:

nga_z_artists.py

import requests
import csv
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/anZ1.htm')

soup = BeautifulSoup(page.text, 'html.parser')

last_links = soup.find(class_='AlphaNav')
last_links.decompose()

# Create a file to write to, add headers row
f = csv.writer(open('z-artist-names.csv', 'w'))
f.writerow(['Name', 'Link'])

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    links = 'https://web.archive.org' + artist_name.get('href')


    # Add each artist’s name and associated link to a row
    f.writerow([names, links])

Lorsque vous exécutez le programme maintenant avec la commandepython, aucune sortie ne sera renvoyée dans la fenêtre de votre terminal. Au lieu de cela, un fichier sera créé dans le répertoire dans lequel vous travaillez appeléz-artist-names.csv.

Selon ce que vous utilisez pour l'ouvrir, cela peut ressembler à ceci:

z-artist-names.csv

Name,Link
"Zabaglia, Niccola",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11630
"Zaccone, Fabian",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=34202
"Zadkine, Ossip",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3475w
...

Ou, cela peut ressembler davantage à un tableur:

CSV Spreadsheet

Dans les deux cas, vous pouvez maintenant utiliser ce fichier pour utiliser les données de manière plus significative, car les informations que vous avez collectées sont maintenant stockées dans la mémoire de votre ordinateur.

Nous avons créé un programme qui extraira les données de la première page de la liste des artistes dont les noms commencent par la lettreZ. Cependant, 4 pages au total de ces artistes sont disponibles sur le site.

Afin de collecter toutes ces pages, nous pouvons effectuer plus d'itérations avec les bouclesfor. Ceci révisera la plupart du code que nous avons écrit jusqu'à présent, mais utilisera des concepts similaires.

Pour commencer, nous voudrons initialiser une liste pour contenir les pages:

pages = []

Nous remplirons cette liste initialisée avec la bouclefor suivante:

for i in range(1, 5):
    url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
    pages.append(url)

Earlier in this tutorial, nous avons noté que nous devrions prêter attention au nombre total de pages contenant des noms d'artistes commençant par la lettreZ (ou toute autre lettre que nous utilisons). Puisqu'il y a 4 pages pour la lettreZ, nous avons construit la bouclefor ci-dessus avec une plage de1 à5 afin qu'elle itère à travers chacune des 4 pages .

Pour ce site Web spécifique, les URL commencent par la chaînehttps://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ, puis sont suivies d'un numéro pour la page (qui sera l'entieri de la bouclefor que nousconvert to a string) et terminez par.htm. Nous allons concaténer ces chaînes ensemble, puis ajouter le résultat à la listepages.

En plus de cette boucle, nous aurons une deuxième boucle qui parcourra chacune des pages ci-dessus. Le code de cette bouclefor ressemblera au code que nous avons créé jusqu'à présent, car il effectue la tâche que nous avons effectuée pour la première page de la lettreZ Artists pour chacun des 4 pages au total . Notez que parce que nous avons mis le programme d'origine dans la deuxième bouclefor, nous avons maintenant la boucle d'origine en tant quenested for loop qu'elle contient.

Les deux bouclesfor ressembleront à ceci:

pages = []

for i in range(1, 5):
    url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
    pages.append(url)

for item in pages:
    page = requests.get(item)
    soup = BeautifulSoup(page.text, 'html.parser')

    last_links = soup.find(class_='AlphaNav')
    last_links.decompose()

    artist_name_list = soup.find(class_='BodyText')
    artist_name_list_items = artist_name_list.find_all('a')

    for artist_name in artist_name_list_items:
        names = artist_name.contents[0]
        links = 'https://web.archive.org' + artist_name.get('href')

        f.writerow([names, links])

Dans le code ci-dessus, vous devriez voir que la première bouclefor itère sur les pages et la deuxième bouclefor récupère les données de chacune de ces pages, puis ajoute les noms et les liens des artistes. par ligne à travers chaque ligne de chaque page.

Ces deux bouclesfor viennent en dessous des instructionsimport, de la création et de l'écriture du fichier CSV (avec la ligne d'écriture des en-têtes du fichier), et de l'initialisation de la variablepages (affectée à une liste).

Dans le contexte plus général du fichier de programmation, le code complet ressemble à ceci:

nga_z_artists.py

import requests
import csv
from bs4 import BeautifulSoup


f = csv.writer(open('z-artist-names.csv', 'w'))
f.writerow(['Name', 'Link'])

pages = []

for i in range(1, 5):
    url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
    pages.append(url)


for item in pages:
    page = requests.get(item)
    soup = BeautifulSoup(page.text, 'html.parser')

    last_links = soup.find(class_='AlphaNav')
    last_links.decompose()

    artist_name_list = soup.find(class_='BodyText')
    artist_name_list_items = artist_name_list.find_all('a')

    for artist_name in artist_name_list_items:
        names = artist_name.contents[0]
        links = 'https://web.archive.org' + artist_name.get('href')

        f.writerow([names, links])

Étant donné que ce programme effectue un peu de travail, la création du fichier CSV prendra un certain temps. Une fois que cela est fait, la sortie sera complète, montrant les noms des artistes et leurs liens associés deZabaglia, Niccola àZykmund, Václav.

Être prévenant

Lorsque vous grattez des pages Web, il est important de rester attentif aux serveurs à partir desquels vous récupérez des informations.

Vérifiez si un site comporte des conditions d'utilisation ou d'utilisation relatives au raclage Web. Vérifiez également si un site dispose d'une API qui vous permet de récupérer des données avant de les récupérer vous-même.

Veillez à ne pas frapper les serveurs en permanence pour collecter des données. Une fois que vous avez collecté ce dont vous avez besoin sur un site, exécutez des scripts qui analysent les données localement plutôt que de surcharger les serveurs de quelqu'un d'autre.

De plus, c’est une bonne idée de gratter avec un en-tête avec votre nom et votre adresse électronique afin qu’un site Web puisse vous identifier et assurer un suivi s’ils ont des questions. Voici un exemple d’en-tête que vous pouvez utiliser avec la bibliothèque Python Requests:

import requests

headers = {
    'User-Agent': 'Your Name, example.com',
    'From': '[email protected]'
}

url = 'https://example.com'

page = requests.get(url, headers = headers)

L'utilisation d'en-têtes contenant des informations identifiables garantit que les personnes qui consultent les journaux d'un serveur peuvent vous contacter.

Conclusion

Ce didacticiel a utilisé Python et Beautiful Soup pour collecter des données d’un site Web. Nous avons stocké le texte que nous avons rassemblé dans un fichier CSV.

Vous pouvez continuer à travailler sur ce projet en collectant plus de données et en rendant votre fichier CSV plus robuste. Par exemple, vous pouvez inclure les nationalités et les années de chaque artiste. Vous pouvez également utiliser ce que vous avez appris pour extraire des données d'autres sites Web.

Pour continuer à apprendre à extraire des informations du Web, lisez notre tutoriel «https://www.digitalocean.com/community/tutorials/how-to-crawl-a-web-page-with-scrapy-and-python-3 [ Comment explorer une page Web avec Scrapy et Python 3]. ”