Cours d'informatique pour le lycée

Projet 5 : grenouille hurlante

Présentation

Dans ce projet, nous allons créer un crawler de site comme le logiciel Screaming Frog. Le but d'un tel logiciel est de parcourir toutes les pages d'un site afin d'en analyser le contenu et de détecter d'éventuelles anomalies. Le logiciel Screaming Frog est très riche et complet, nous allons donc faire une version très simplifiée d'un tel logiciel.

Les pages d'un site web constituent les nœuds d'un graphe et les liens ses arrêtes. Notre programme va donc effectuer un parcours en largeur du site web et relever des informations sur chaque page.

Nous allons voir les différents éléments nécessaires pour réaliser le projet.

Éléments de code

Modules nécessaires

Nous allons avoir besoin de trois modules dans ce projet :

import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, urljoin

Le module requests permet de faire une requête http et de récupérer le contenu de la réponse.

Le module BeautifulSoup permet, à partir une page récupérée avec requests, de créer une « soupe » permettant d'accéder facilement aux éléments de la page. Ce module fait ce qu'on appelle un parsing de la page.

Enfin le module urllib.parse permet de manipuler facilement les urls.

Décomposer et recomposer une url

Voici un code permettant de comprendre comment décomposer et recomposer une url :

from urllib.parse import urlparse, urljoin

depart = "https://kxs.fr/"
print("Url de depart : " + depart)

# On détermine le domaine
domaine = urlparse(depart).hostname
# On détermine le protocole
protocole = urlparse(depart).scheme
# On détermine le chemin
chemin = urlparse(depart).path
# On écrit tout ça
print("Domaine : " + domaine)
print("Protocle : " + protocole)
print("Chemin : " + chemin)

# Recomposition d'une url
url = protocole + "://" + domaine + chemin
print("Url recomposée : " + url)

Testez ce code et essayez éventuellement avec d'autres url pour bien comprendre ce qu'il se passe.

Nous allons avoir besoin de décomposer une url car nous ne stockerons que les chemins des urls et il nous faudre déterminer si les liens sont internes (vers le même domaine) ou externes (vers un autre domaine).

Explorer une url

Voici un code permettant d'extraire des informations d'une url :

import requests
from bs4 import BeautifulSoup

proxyDict = {
              "http"  : "http://demander_au_prof:demander_au_prof@172.16.0.253:3128",
              "https" : "https://demander_au_prof:demander_au_prof@172.16.0.253:3128"
            }

url = "https://kxs.fr/"
requete = requests.get(url, proxies=proxyDict)
# On récupère le contenu de la page
page = requete.content
# On crée la soupe
soup = BeautifulSoup(page, "lxml")
# On affiche le titre
print(soup.title.string)
# On affiche le statut
print(requete.status_code)

# On récupère les liens
liens = soup.find_all('a')

# On affiche les liens et leur cible
for lien in liens:
    print(lien, end=" : ")
    cible = lien.get('href')
    print(cible)

Testez ce code en changeant éventuellement l'url pour voir ce qu'il se passe.

Reformer une url à partir d'un lien

Bien souvent, les cibles des liens (href) sur les page ne contiennent pas des url (commencant par https) mais des chemins. On peut voir ça sur l'exemple précédent :

href : cours/
url induite : https://kxs.fr/cours/

Il n'est pas vraiment simple de reconstituer l'url à partir de la cible. C'est pour cela que nous allons utiliser le module urllib.parse pour faire ce travail. On utilisera ainsi la méthode urljoin en lui fournissant la page actuelle et la cible du lien :

from urllib.parse import urlparse, urljoin
# Url de la page
url = "https://kxs.fr/"
# Cible du lien
cible = "cours/"
# Url pointée par le lien
url_lien = urljoin(url, cible)

print("Url :", url)
print("Cible :", cible)
print("Url du lien :", url_lien)

L'exmple étant simple, cela peut sembler être une simple concaténation. Ça n'est pas aussi simple car les cibles des liens peuvent être absolus :

from urllib.parse import urlparse, urljoin
# Url de la page
url = "https://kxs.fr/cours/unepage"
# Cible du lien
cible = "/cours/uneautrepage"
# Url pointée par le lien
url_lien = urljoin(url, cible)

print("Url :", url)
print("Cible :", cible)
print("Url du lien :", url_lien)

Cahier des charges

Dans un premier temps, nous allons chercher à simplement afficher la liste des pages du site avec la liste de leurs liens comme ci-dessous. Pour chaque page on affiche le chemin de la page, son titre et son statut :

cheminpage1 titre1 statut1
 - url lien 1
 - url lien 2
cheminpage2 titre2 statut2
 - url lien 1
 - url lien 2
…

Vous aurez besoin d'un file pour stocker les chemins à explorer. Je vous suggère de faire au moins deux fonctions :

exploreChemin(chemin)
Cette fonction explore l'url associée au chemin. Elle doit donc afficher les informations de la page et ses liens. Il faut également ajouter les chemins des liens dans la file. Pour cela on utilisera une autre fonction ajouteLien(url)

ajouteLien(url)
À partir d'une url, cette fonction détermine si le lien est interne. Si c'est le cas, elle ajoute son chemin à la file.

Attention, il faut prendre garde à plusieurs dangers plus ou moins fatals dans ce programme :

Lorsque vous avez réussi à coder les fonctionalités de base, vous pouvez vous attaquer aux améliorations suivantes :

Pour les plus rapides, avec des recherches à faire, en bonus :

Tableau du barème

Voilà le barème complet sur 20 pour ce projet.

Tâche Barème
Liste des liens de la page /cours/ 1 points
Liste de presque toutes les pages du site 2 points
Avec les liens de chaque page 2 points
Avec le titre et le statut 1 point
Éviter d'explorer les pages externes 1 point
Affichage du nombre total de pages 1 points
Nombre de liens internes et externe pour chaque page 1 point
Liste de tous les liens externes 1 points
Liste de tous les liens morts 1 points
Export dans un fichier texte 1 points
Export de la liste des pages avec leurs infos dans un fichier csv 1 points
Temps aléatoire 1 points
Exclure les fichiers images, pdf et sql 1 points
Profondeur de chaque page 1 points
Code propre 1.5 points
Code optimisé 1 point
Commentaires 1.5 points
Export SQL 0.5 point bonus
liens nofollow 0.5 points bonus
Total 20