Construire un crawler pour extraire tous les liens internes et externes du site web en utilisant des demandes, requests-html et une belle soupe en Python.
L’extraction de tous les liens d’une page Web est une tâche courante parmi les racleurs Web. Il est utile de construire des racleurs avancés qui explorent chaque page d’un certain site web pour extraire des données. Il peut également être utilisé pour le processus de diagnostic SEO ou même la phase de collecte d’informations pour les testeurs de pénétration.
Dans ce tutoriel, vous apprendrez comment construire un outil d’extraction de liens en Python à partir de Scratch en utilisant uniquement les requêtes et la bibliothèque BeautifulSoup.
Notez qu’il y a beaucoup d’extracteurs de liens, comme Link Extractor par Sitechecker. Le but de ce tutoriel est d’en construire un seul en utilisant le langage de programmation Python.
Installons les dépendances:
pip3 install requests bs4 colorama
Nous utiliserons les demandes pour faire des requêtes HTTP commodément, BeautifulSoup pour analyser HTML, et colorama pour changer la couleur du texte.
Ouvrez un nouveau fichier Python et suivez. Importons les modules dont nous avons besoin:
import requests
from urllib.parse import urlparse, urljoin
from bs4 import BeautifulSoup
import colorama
Nous allons utiliser le colorama juste pour utiliser différentes couleurs lors de l’impression pour distinguer entre les liens internes et externes:
#initialiser le module colorama
colorama.init()
GREEN = colorama.Fore.GREEN
GRAY = colorama.Fore.LIGHTBLACK_EX
RESET = colorama.Fore.RESET
YELLOW = colorama.Fore.YELLOW
Nous allons avoir besoin de deux variables globales, l’une pour tous les liens internes du site et l’autre pour tous les liens externes :
#initialiser l'ensemble des liens (liens uniques)
internal_urls = set()
external_urls = set()
- Les liens internes sont des URL qui renvoient à d’autres pages du même site.
- Les liens externes sont des URL qui renvoient à d’autres sites web.
Étant donné que tous les liens dans les étiquettes d’ancrage (a
tags) sont valides (j’ai expérimenté cela), certains sont des liens vers des parties du site, et certains sont javascripts, donc écrivons une fonction pour valider les URL:
def is_valid(url):
"""
Vérifie si « url » est une URL valide.
"""
parsed = urlparse(url)
return bool(parsed.netloc) and bool(parsed.scheme)
Cela garantira http l’existence d’un système approprié (protocole, par exemple https ou https) et d’un nom de domaine dans l’URL.
Maintenant, construisons une fonction pour retourner toutes les URL valides d’une page web:
def get_all_website_links(url):
"""
Renvoie toutes les URL trouvées sur `url` dans lesquelles elles appartiennent au même site Web
"""
# all URLs of `url`
urls = set()
#nom de domaine de l'URL sans le protocole
domain_name = urlparse(url).netloc
soup = BeautifulSoup(requests.get(url).content, "html.parser")
Tout d’abord, j’ai initialisé le urls
Je me suis servi des ensembles Python ici parce que nous ne voulons pas de liens redondants.
Deuxièmement, j’ai extrait le nom de domaine de l’URL. Nous allons en avoir besoin pour vérifier si le lien que nous avons attrapé est externe ou interne.
Troisièmement, j’ai téléchargé le contenu HTML de la page web et je l’ai emballé avec un soup
s, opposer à faciliter l’analyse HTML.
Récupérons toutes les balises HTML a (balises d’ancrage qui contiennent tous les liens de la page Web) :
for a_tag in soup.findAll("a"):
href = a_tag.attrs.get("href")
if href == "" or href is None:
#href balise vide
continue
Donc nous obtenons l’attribut href et vérifions s’il y a quelque chose là-bas. Sinon, nous continuons simplement vers le maillon suivant.
Comme tous les liens ne sont pas absolus, nous allons devoir joindre des URL relatives à leur nom de domaine (par exemple lorsque le href est « /search » et l’url est « google.com », le résultat sera « google.com/search »):
#rejoindre l'URL si elle est relative (pas de lien absolu)
href = urljoin(url, href)
Maintenant, nous devons supprimer les paramètres HTTP GET des URL car cela provoquera une redondance dans l’ensemble. Le code ci-dessous gère que:
parsed_href = urlparse(href)
#supprimez les paramètres URL GET, les fragments d'URL, etc.
href = parsed_href.scheme + "://" + parsed_href.netloc + parsed_href.path
Finissons la fonction:
if not is_valid(href):
#si aucune URL n'est valide
continue
if href in internal_urls:
#déjà dans l'ensemble
continue
if domain_name not in href:
#lien externe
if href not in external_urls:
print(f"{GRAY}[!] External link: {href}{RESET}")
external_urls.add(href)
continue
print(f"{GREEN}[*] Internal link: {href}{RESET}")
urls.add(href)
internal_urls.add(href)
return urls
Tout ce que nous avons fait ici, c’était vérifier :
- Si l’URL n’est pas valide, continuez jusqu’au lien suivant.
- Si l’URL est déjà dans le
internal_urls
, nous n’avons pas besoin de ça non plus. - Si l’URL est un lien externe, imprimez-le en couleur grise, ajoutez-le à notre global
external_urls
et continuer à la liaison suivante.
Enfin, après tous les contrôles, l’URL sera un lien interne. Nous l’imprimons et l’ajoutons à notre urls
et internal_urls
ensembles.
La fonction ci-dessus ne saisira que les liens d’une page spécifique; et si nous voulons extraire tous les liens pour l’ensemble du site web? Faisons ceci :
# number of urls visited so far will be stored here
total_urls_visited = 0
def crawl(url, max_urls=30):
"""
Analyse une page Web et extrait tous les liens.
Vous trouverez tous les liens dans les variables globales `external_urls` et `internal_urls`.
paramètres :
max_urls (int) : nombre maximum d'URL à explorer, la valeur par défaut est 30.
"""
global total_urls_visited
total_urls_visited += 1
print(f"{YELLOW}[*] Crawling: {url}{RESET}")
links = get_all_website_links(url)
for link in links:
if total_urls_visited > max_urls:
break
crawl(link, max_urls=max_urls)
Cette fonction explore le site web, ce qui signifie qu’il obtient tous les liens de la première page, puis appelle elle-même récursivement pour suivre tous les liens précédemment extraits. Cependant, cela peut causer certains problèmes; le programme sera bloqué sur de grands sites web (qui ont de nombreux liens), tels que google.com
. En tant que un résultat, j’ai ajouté un max_urls
paramètre à sortir lorsque nous atteignons un certain nombre d’URL vérifiées.
Bon, testons ceci, assurons-le d’utiliser ceci sur un site web que vous êtes autorisé. Sinon, je ne suis pas responsable de tout mal que vous causez.
if __name__ == "__main__":
crawl("https://www.thepythoncode.com")
print("[+] Total Internal links:", len(internal_urls))
print("[+] Total External links:", len(external_urls))
print("[+] Total URLs:", len(external_urls) + len(internal_urls))
print("[+] Total crawled URLs:", max_urls)
Je le teste sur notre site web. Cependant, je vous encourage vivement à ne pas le faire, cela provoquera beaucoup de demandes car cela foulera notre petit serveur web et bloquera votre adresse IP.
Après les finitions rampantes, il imprimera les liens complets extraits et crawlés:
[+] Total Internal links: 90
[+] Total External links: 137
[+] Total URLs: 227
[+] Total crawled URLs: 30
C’est génial, n’est-ce pas ? J’espère que ce tutoriel a été bénéfique pour vous et vous a inspiré à construire de tels outils en utilisant Python.
Certains sites web chargent la majeure partie de leur contenu en utilisant JavaScript. Par conséquent, nous devons utiliser la bibliothèque requests-html à la place, ce qui nous permet d’exécuter Javascript en utilisant Chromium ; j’ai déjà écrit un script pour cela en ajoutant seulement quelques lignes (comme requests-html est assez similaire aux demandes). Vérifiez-le ici.
Demander le même site web plusieurs fois sur une courte période peut amener le site à bloquer votre adresse IP. Dans ce cas, vous devez utiliser un serveur proxy à de telles fins.