Le crawl est une technique qui consiste à naviguer sur ou plusieurs site pour en récupérer les information C'est la technique qu'emploient les moteurs de recherche pour créer une base de données qui contient les références au site et les mots clés qui correspondent à la recherche. La base du crawl est de stocker les liens présents dans une page, de les télécharger au fur et à mesure et de stocker les informations intéressantes. Le crawl permet donc de reconstituer une base de données en collectant les informations html et en les formattant pour les insérer dans une base
Il existe autant de crawler que de besoins spécifiques parfois et même souvent une même extraction peut procéder de différentes manières et donc avoir différents algorithmes.
Pour la collecte de données, on développe des crawlers spécifiques:
Par opposition le crawler web que développent les moteurs de recherche et qu'on appelera ici Spider
L'algorithme d'un spider simplifié fonctionne de la manière suivante:
A partir d'une url de départ:
* télécharger la page
* parser le contenu
* extraire toutes les urls
Pour chaque url de la liste de départ:
* refaire le travail
* et ainsi de suite jusqu'à ce qu'il n'y ait plus d'url à traiter
En pseudo code python cela donnerait
In [ ]:
tocrawl = []
def crawl(url):
html = download(url)
page = parse(html)
urls = extract_links(page)
tocrawl.append(urls)
return tocrawl
starter_url = "www.example.com"
tocrawl = crawl(starter_url)
while len(tocrawl) != 0:
for url in tocrawl:
crawl(url)
Evidemment c'est un tout petit peu plus compliqué que ça... Quelques exemples...
Les données du spider permettent de constituer des réseaux et des graphes en fonctions des informations collectées. Les réseaux constitués les plus simple constistent en graphes de co-citations une url source etant reliée a plusieurs urls par le fait quelle les mentionnent sur la page on peut cartographier les sites qui se citent les uns les autres.
L'algorithme d'un crawler de site simplifié fonctionne de la manière suivante: Une fois le parcours sur le site et les informations identifiées, le robot aggrège les données cibles qu'il stocke dans une base de données ou un fichier final à plat. La parcours varie en fonction de l'information auquel on souhaite accéder et la manière dont on veut interroger les données.
L'objectif étant de récupérer l'ensemble des arguments pour chaque article de loi
Le crawl produit une base de données d'arguments reliés à un article de loi Dans le cas d'un crawl sur un site particulier, il est capital de définir avant le crawl les données à extraire et les différentes étapes pour appliquer l'extraction à plusieurs pages
Reprenons les étapes d'extraction pour la page d'un site. à travers plusieurs exercices
In [ ]:
#!/usr/bin/python
Définissons ensuite l'encodage pour prendre en compte les accents
In [ ]:
# coding: utf-8
In [1]:
import csv
On importe au debut du script tous les modules complémentaire dont on va avoir besoin et qui sont importer grace à l'instruction import
In [1]:
import requests
In [ ]:
from bs4 import BeautifulSoup
Pour BeautifulSoup, on spécifie le type parser que nous allons utiliser
In [ ]:
BeautifulSoup("html.parser")
On va ensuite écrire les fonctions dont on a besoin:
* une fonction pour télécharger les urls ``download``
* une fonction pour écrire dans un fichier ``write_csv``
* une fonction pour extraire les liens ``extract_links``
Pour les utiliser ensuite (les appeler) dans notre fichier à la fin comme on va voir par la suite
In [1]:
def download(url):
'''fonction qui télécharge une page a partir d'une url et retourne le html sous forme de texte'''
#on appelle le module requests qui permet de télécharger la page
#et on stocke le résultat du téléchargement
#dans une variable qu'on appelle ici reponse
reponse = requests.get(url)
#response est produite par le module requests
#elle a donc des valeurs spécifiques qui sont documentés sur la page du module
# requests
#tel que le code de status de la page cf[[Les codes d'erreur HTTP
# le texte ou encore un json
if response.status_code == 200:
return response.text
L'avantage d'écrire une fonction est qu'elle peut etre utilisée autant de fois
qu'on veut avec n'importe quelle variable c'est à dire pour cette fonction download
avec n'importe quelle url
In [ ]:
#ici la fonction pour écrire une ligne dans un fichier CSV
def write_csv(filename, line):
#on ouvre le fichier appelé filename comme un fichier csv
# a pour ajouter à la suite (append)
#r pour lire (read)
#w pour ecrire (write)
with open(filename, 'a') as csvfile:
#on met le fichier dans le writer de csv
#en specifiant la délimitation ici ";"
spamwriter = csv.writer(csvfile, delimiter=';')
#line doit etre une liste d'éléments représentée en python par []
# chaque element contenu dans line va s'inscrire dans une colonne
spamwriter.writerow(data)
return
In [ ]:
#ici la fonction pour extraire des liens a partir d'une url
def extract_links(url):
#on télécharge le contenu de la page
#avec une fonction qu'on a déjà écrite plus haut
html = download(page)
#on va stocker tous les liens contenu dans la page dans une liste
#avec le nom
links = []
soup = BeautifulSoup(html)
#la fonction find_all renvoie une **liste**
#de plusieurs elements BeautifulSoup
# qui sont manipulable en utilisant les fonctions de Beautifulsoup
#on a besoin ici de récupérer le lein contenu de la balise a href="{{lien}}"
#qu'on va stocker dans notre liste comme dans un tableau
#ici on veut récupérer toutes les balises de type a sans filtre particulier
tag_link_list = soup.find_all("a")
#name est donc une liste d'element parsés de tags
#on va donc derouler les élements contenu
for element in tag_link_list:
#l'url de la page est stocké
#dans le corps de la balise
#exemple <a href="http://lemonde.fr">Site du Monde </a>
#pour le récupérer on utilise la fonction de Beautifulsoup get
lien = element.get("href")
#on ajoute le lien à la liste
liens.append(lien)
return liens
In [ ]:
#ici la fonction qui permet d'extraire les informations interessantes
#pour un site particulier
def extract_data(url):
#on télécharge le contenu de la page
#avec une fonction qu'on a déjà écrite plus haut
html = download(page)
#on transforme le html en le parcourant (parsing) avec BeautifulSoup
soup = BeautifulSoup(html)
#on va stocker tous les futures informations
#dans une liste qu'on appelle data
data = []
#pour extraire les données on utilise les fonctions de BeautifulSoup
#find_all renvoie une liste d'elements
#find renvoie le premier élement
#imaginons que nous voulons extraire toutes les videos de cette page
#http://www.bbc.com/news/science_and_environment
#elles sont contenues dans la balise suivante
#<div id="comp-candy-asset-munger" class="distinct-component-group container-condor wide-only">
#on peut découper la partie qui nous interesse
#en utilisant find qui renvoie la première balise
# et l'id qui permet de trouver cette balise particuliere
colonne_videos = soup.find("div", {"id": "comp-candy-asset-munger"})
#on peut ensuite appliquer les méthodes de BeautifulSoup à l'interieur de cette partie
#recuperer toutes les images en utilisant find_all
img_tags = colonne_videos.find_all("img", {"class":"responsive-image__inner-for-label"})
#il s'agit encore d'une liste de tag
#on va récupérer la source de cette image stockée dans src
for img in img_tags:
image_sources = img.get("scr")
data.append([image_sources, )
return data
Toutes ces fonctions toutes sont universelles et s'applique à n'importe quelle page html
sauf la derniere extract_data()
qui extrait les données a partir de tag qui n'existent que pour ce site et cette page précise.
Vous pouvez télécharger la version propre et complete depuis ce fichier
Maintenant nous allons appeler les différentes fonctions pour faire marcher notre crawler
In [ ]:
#### Exemple d'un crawl simple à profondeur 1
Ici le code de base pour extraire tous les liens d'une page de départ:
on part d'une url de départ qu'on télécharge et dont on extrait les liens qu'on stocke dans une liste
In [ ]:
url_de_depart='http://www.bbc.com/news/science_and_environment'
liens_page0 = extract_links(url_de_depart)
liens1 = []
# on déroule les urls de la page
for page in liens_page0:
# pour chaque lien
for lien in extract_links(page):
#on ajoute dans la liste
liens.append(lien)
La liste de toutes les urls de cette page de départ est stockées dans liens1 pour savoir combien d'url on a récupéré on peut appeler une fonction standard dans python len(uneliste) qui a partir d'une liste en parametres donne le nombre d'élements présents dans la liste
In [ ]:
nb_liens1 = len(liens1)
print nb_liens
Maintenant imaginons que nous soyons un simple crawler qui collecte toutes les url sans rien stocker de plus que des liens mais à profondeur infinie
On a besoin d'une liste d'url à traiter et d'une liste d'url déjà traitée pour s'assurer que le robot ne tourne pas à l'infini et qu'il ne crawl pas plusieurs fois une même url pour la même raison il est utile aussi de vérifier qu'une url n'est pas en double dans les deux listes
In [ ]:
url_de_depart='http://www.bbc.com'
#liste d'url a traiter
to_do = []
#liste d'url traitées
done = []
starter = extract_links(url_de_depart)
# on déroule les urls de la page
for page in starter:
# pour chaque lien
for lien in extract_links(page):
#on l'ajoute à faire
to_do.append(lien)
#on ajoute la page dont les liens ont déjà ete extrait
done.append(page)
#while est une instruction spéciale en python de la manière que for c'est une boucle
#while permet d'executer des instructions tant que la condition est remplie
#ici tant que la liste de to_do n'est pas vide
while len(to_do) != 0:
#on enlève les éventuels doublons avec la fonction de python set() qui enlève les éléments en double
uniq_to_do = set(to_do)
for lien in uniq_to_do:
for new_link in extract_links(lien):
if new_link not in done:
to_do.append(new_link)
done.append(lien)
to_do.remove(lien)
#ici on aura donc au final une liste de url crawlées stockées dans la liste done
In [ ]: