Comprendre les principes fondamentaux du Web pour collecter des données
Le Web est complexe : de nombreuses technologies et concepts sont utilisés pour afficher une simple page web dans votre navigateur. Je n'ai pas la prétention de tout expliquer, mais je vais vous expliquer les éléments les plus importants à comprendre pour extraire des données du web.
Protocole de transfert hypertexte (HTTP)
Le protocole de transfert hypertexte (HTTP) utilise un modèle client/serveur.
Un client HTTP (un navigateur, votre programme Python, cURL, Requests...) :
- ouvre une connexion et envoie un message (En gros : "Je veux voir cette page : /produit") à un serveur HTTP (Nginx, Apache...).
- Le serveur réagit ensuite avec une réponse (le code HTML de la page par exemple) puis ferme la connexion.
HTTP est appelé un protocole “sans état” car chaque transaction (request/response) est indépendante.
Lorsque vous tapez l'adresse d'un site web dans votre navigateur, la requête HTTP ressemble à ceci :
Dans la première ligne de cette requête, on peut voir les éléments suivants :
- La méthode GET : Cela signifie que nous demandons des données à partir du chemin spécifique (la page en question) : /produit/. Il existe d'autres méthodes HTTP, dont vous pouvez consulter la liste complète ici.
- La version du protocole HTTP : Dans ce tutoriel, nous nous concentrerons sur le protocole “HTTP 1”.
- Plusieurs champs d'en-têtes : Connexion, User-Agent... Voici une liste exhaustive des en-têtes HTTP si vous souhaitez en savoir plus.
En bref, voici les champs d'en-tête les plus importants et ceux sur lesquels nous allons nous concentrer :
- Hôte : Il s'agit du nom de domaine du serveur. Si aucun numéro de port n'est indiqué, il est supposé être égal à 80.
- User-Agent : Contient des informations sur le client à l'origine de la requête, y compris le système d'exploitation. Dans ce cas, il s'agit de mon navigateur web (Chrome) sur macOS. Cet en-tête est important car il est soit utilisé pour des statistiques (combien d'utilisateurs visitent mon site web sur mobile vs desktop), soit pour empêcher les violations par des bots. Comme ces en-têtes sont envoyés par les clients, ils peuvent être modifiés ("Header Spoofing"). C'est exactement ce que nous allons faire avec nos scrapeurs - faire en sorte que nos scrapeurs ressemblent à un navigateur web ordinaire.
- Accept : Ce sont les types de contenu qui sont acceptés comme réponse. Il existe de nombreux types et sous-types de contenu différents : text/plain, text/html, image/jpeg, application/json ...
- Cookie : ce champ d'en-tête contient une liste de paires nom-valeur (name1=value1;name2=value2). Ces cookies de session sont utilisés pour stocker des données. Les cookies sont ce que les sites Web utilisent pour authentifier les utilisateurs et/ou stocker des données dans votre navigateur. Par exemple, lorsque vous remplissez un formulaire de connexion, le serveur vérifie si les informations d'identification que vous avez saisies sont correctes. Si c'est le cas, il vous redirigera et injectera un cookie de session dans votre navigateur. Votre navigateur enverra ensuite ce cookie à chaque demande ultérieure adressée à ce serveur.
Voici un exemple des cookies utilisés sur LinkedIn.
- Referrer : L'en-tête Referrer contient l’adresse de la page à partir de laquelle l'URL actuelle a été demandée. Cet en-tête est important car les sites Web l'utilisent pour modifier leur comportement en fonction de la provenance de l'utilisateur. Par exemple, de nombreux sites d'information ont un abonnement payant et ne vous laissent voir que 10 % d'un article, mais si l'utilisateur vient d'un agrégateur comme Reddit, ces mêmes sites vous laissent voir le contenu complet. Ils utilisent alors le site référent pour vérifier votre provenance et vous autoriser ou non de tout visualiser. Parfois, il faut savoir modifier cet en-tête pour simuler notre provenance (notre site référent) afin d’atteindre le contenu que nous voulons extraire.
Et la liste continue... vous pouvez trouver la liste complète des en-têtes ici.
Concernant la réponse d’un serveur, voici à quoi cela ressemble :
Sur la première ligne, nous avons un nouvel élément d'information, le code HTTP 200 OK. Cela signifie que la requête a réussi. En ce qui concerne les en-têtes de la demande, il existe de nombreux codes HTTP. Ils sont répartis en quatre classes communes :
- 2XX pour les requêtes réussies,
- 3XX pour les redirections,
- 4XX pour les mauvaises requêtes (la plus célèbre étant "404 Not Found")
- et 5XX pour les erreurs de serveur
Voici une liste non exhaustive des codes les plus courants :
- 200 : Il indique que l'état de votre site est bon et que le serveur renvoie l'URL du site que vous avez demandé.
- 301 : Il signifie que l'adresse de votre site a été définitivement déplacée vers une nouvelle adresse de site et que tous les domaines et sous-domaines doivent être redirigés vers un nouvel emplacement.
- 302 : Il indique que le serveur a trouvé une redirection temporaire du site Web. L'URL de ce site doit être utilisée à nouveau car elle est temporaire.
- 307 : similaire au 302.
- 400 : Indique une mauvaise requête. Il indique que le serveur n'est pas en mesure de comprendre l'URL du site Web que vous demandez.
- 401 : Accès non autorisé.
- 403 : Interdit : indique que le serveur n'affichera aucun contenu jusqu'à ce que ou à moins que vous soyez autorisé à accéder à ce contenu.
- 404 : Non trouvé. Ce message d'erreur nous frustre car nous n'obtenons pas le résultat que nous recherchons, ce qui signifie que le fichier que vous recherchez est introuvable sur un serveur web.
- 410 : similaire à 404.
- 500 : Erreur interne du serveur. Ce message d'erreur est une source de préoccupation pour le webmaster, le développeur web et les visiteurs du site web car cette erreur est liée au serveur et devrait être résolue très rapidement.
- 503 : Service non disponible, le code d'erreur du serveur indique qu'il n'est pas prêt à traiter la demande.
Une fois la réponse à votre requête HTTP reçue par votre navigateur web, ce dernier analysera le code HTML, récupérera tous les actifs potentiels (fichiers Javascript, CSS, images...) et affichera le résultat dans votre page.
Nous allons passer en revue les différentes façons d'effectuer des requêtes HTTP avec Python et d'extraire les données web.
1. Collecte de Données sur le Web en utilisant des requêtes HTTP et les Sockets
Comment utiliser l'ouverture de sockets ?
La façon la plus simple d'effectuer une requête HTTP en Python consiste à ouvrir un socket et à envoyer manuellement la requête HTTP.
Maintenant que nous disposons de la réponse HTTP, nous allons extraire des données avec ce que nous appelons des expressions régulières (“regular expressions” en anglais communément appelées ReGex)
À quoi servent les Expressions régulières dans le Web Scraping ?
Une expression régulière (RE ou regex) est un modèle de recherche pour les chaînes de caractères.
Avec une expression régulière, vous pouvez rechercher un caractère ou un mot particulier dans un corps de texte plus important.
Par exemple, vous pouvez identifier tous les numéros de téléphone ou emails d'une page Web donnée.
Vous pouvez également valider certaines entrées (comme par exemple pour contrôler que le champ email n’accepte que des ...emails dans vos formulaires de contact)
Pourquoi est-il important de comprendre les expressions régulières pour le Web Scraping ?
Après tout, il existe de nombreux modules Python différents pour analyser le code HTML, avec des sélecteurs XPath ou CSS.
Dans un monde sémantique idéal, les données sont facilement lisibles par les machines et les informations sont intégrées dans des éléments HTML avec des attributs facilement compréhensibles et clairs.
Mais le monde réel est désordonné.
Vous trouverez souvent d'énormes quantités de texte à l'intérieur d'un élément p (paragraphe). Par exemple, si vous voulez extraire des données spécifiques à l'intérieur d'un grand texte (un prix, une date, un nom...), vous devrez utiliser des expressions régulières.
Remarque : voici un excellent site Web pour tester vos expressions régulières : https://regex101.com/.
Les expressions régulières peuvent être utiles lorsque vous disposez de ce type de données :
<p>Prix : 19.99€</p>
Nous pourrions sélectionner ce texte avec un sélecteur Xpath et ensuite utiliser ce type de regex pour extraire le prix :
Résultat : 19.99
Vous obtiendrez ainsi le prix directement. Les regex vous éviteront d’avoir besoin d’ajouter des étapes nécessaires pour “nettoyer” le texte.
Pour extraire les bonnes données d'une balise HTML, il est faisable d'utiliser une regex :
Résultat : Prix : 19.99€
Comme vous pouvez le constater, il est possible d'envoyer manuellement la requête HTTP avec un socket et d'analyser la réponse avec une expression régulière, mais c'est compliqué et il existe des API qui peuvent faciliter cette tâche.
2. Collecte de données sur le Web avec urllib3 et LXML
Urllib3 est un package de haut niveau qui vous permet de faire à peu près tout ce que vous voulez avec une requête HTTP.
Par exemple avec urllib3, il aurait été possible de faire tout ce que nous avons réalisé précédemment avec beaucoup moins de lignes de code.
Comme vous pouvez le constater, cette version est beaucoup plus rapide que la version socket. De plus, l'API est très simple à utiliser.
De plus, vous pouvez facilement faire beaucoup d'autres choses, comme ajouter des en-têtes HTTP, utiliser un proxy, écrire dans des formulaires, les envoyer, etc ...
Par exemple, si nous avions décidé de définir certains en-têtes et d'utiliser un proxy, il nous aurait suffit de faire cela :
Vous voyez quelquechose ? Il y a exactement le même nombre de lignes. Cependant, il y a certaines choses qu'urllib3 ne gère pas très facilement.
Par exemple, si nous voulions créer des en-têtes, il faudrait les ajouter manuellement (en simulant un en-tête tout simplement)
Ensuite, pour analyser la réponse, nous allons utiliser le package LXML et les expressions XPath.
XPath
XPath est une technologie qui utilise des expressions pour sélectionner des nœuds ou des ensembles de nœuds dans un document XML (ou un document HTML).
Comme pour le DOM (Document Object Model), Xpath est une norme du W3C depuis 1999.
Bien que XPath ne soit pas un langage de programmation en soi, il vous permet d'écrire des expressions qui peuvent accéder directement à un nœud spécifique, ou à un ensemble de nœuds spécifique, sans avoir à parcourir l'ensemble de l'arbre HTML (ou de l'arbre XML).
Pour extraire des données web d'un document HTML avec XPath, nous avons besoin de trois éléments :
- un document HTML
- quelques expressions XPath
- un moteur XPath qui exécutera ces expressions
Pour commencer, nous allons utiliser le code HTML que nous avons obtenu par le biais d’urllib3.
Imaginons que nous voulions extraire tous les liens de la page d'accueil de Google.
Nous allons donc utiliser une simple expression XPath : //a. Et nous utiliserons LXML pour l'exécuter.
LXML est une bibliothèque de traitement XML et HTML rapide et facile à utiliser qui prend en charge XPATH.
Installation :
Voici le code qui vient juste après ce que nous avions écrit précédemment :
Voici à quoi ressemble le résultat de ce code :
Gardez à l'esprit que cet exemple est vraiment très simple et ne vous montre pas à quel point XPath peut être puissant.
La documentation LXML est bien rédigée et constitue un très bon point de départ si vous souhaitez monter en compétences.
Les expressions XPath, comme les regex, sont puissantes et constituent l'un des moyens les plus rapides d'extraire des informations du code HTML.
Et comme les regex, XPath peut rapidement devenir désordonné, difficile à lire et à maintenir. À ce titre, il est important de documenter votre code pour savoir revenir facilement dessus plus tard.
3. Collecte de données sur le Web avec Requests & BeautifulSoup
Requests
Requests est le roi des packages Python.
Avec plus de 11 000 000 téléchargements, c'est le package Python le plus utilisé.
Installation :
Faire une requête avec Requests est facile :
Avec Requests, il est facile d'effectuer des requêtes POST, de gérer les cookies, les paramètres de requêtes, etc...
Authentification
Disons que nous voulons créer un outil pour soumettre automatiquement des messages dans des formulaires. (Comme par exemple contacter des centaines de freelances automatiquement sur un célèbre site français ….).
Nous aurions besoin de nous authentifier sur ces sites avant de réaliser ces actions et de mettre en place des timers aléatoires pour éviter la détection des robots.
Il est possible de le faire avec Requests et BeautifulSoup.
Voici le formulaire à quoi cela peut ressembler :
(j’ai volontairement mis uniquement des bouts de code car l’original fait près de 350 lignes)
Nous allons voir tout de suite ce qu’est BeautifulSoup.
BeautifulSoup
La prochaine chose dont nous aurons besoin est BeautifulSoup, qui est une bibliothèque Python qui nous aidera à analyser le code HTML renvoyé par le serveur, pour savoir si nous sommes connectés ou non.
Installation:
Ainsi, tout ce que nous avons à faire est de POSTER ces trois entrées avec nos informations d'identification au point de terminaison /login et de vérifier la présence d'un élément qui ne s'affiche qu'une fois connecté :
Afin d'en savoir plus sur BeautifulSoup, nous pourrions essayer d'extraire par exemple tous les noms des outils présents sur la page de www.seo.sales-hacking.com.
La première chose à faire est d'inspecter la page d'accueil pour comprendre la structure et les différentes classes CSS que nous devrons sélectionner :
Nous pouvons voir que tous les noms de menus sont contenus dans une <div class="caption">. La première chose à faire est donc de sélectionner toutes ces balises.
Je vais vous montrer qu’il est possible de récupérer en quelques lignes de codes :
- Le nom de chaque outil
- L’url de l’image de l’icône
- Le lien de la page de chaque outil
Je vous laisse essayer le code si vous le souhaitez pour voir que vous récupérer rapidement toutes les informations demandées.
Requests et BeautifulSoup sont d'excellentes bibliothèques pour extraire des données et automatiser différentes actions en postant des formulaires.
Si vous souhaitez réaliser des projets de Web scraping à grande échelle, vous pouvez toujours utiliser Requests, mais vous devrez gérer vous-même de nombreux éléments.
Lorsque vous devez collecter les données d’un grand nombre de pages Web, il y a beaucoup de choses que vous devez gérer :
- Paralléliser votre code pour le rendre plus rapide
- Traiter des erreurs
- Stocker les résultats
- Filtrer les résultats
- Réduire des requêtes pour ne pas surcharger les serveurs.
Heureusement pour nous, il existe des outils qui peuvent s'en charger à notre place.
Grequests
Bien que le paquet de requests soit facile à utiliser, il pourrait que vous le trouviez un peu lent lorsque vous envisager de collecter des données sur des centaines de pages.
Le paquet requests, tel quel, ne permet que des requêtes dites “synchrones”. Cela signifie que si vous avez 50 URL à scraper, vous devriez les faire une par une.
Ainsi, si vous prenez 10 secondes à collecter les données pour une page, il vous faudra 50 x 10 secondes pour récupérer 50 pages.
La façon la plus simple d'accélérer ce processus est de faire plusieurs appels en même temps. Cela signifie qu'au lieu d'envoyer chaque requête unitaire une après l’autre, vous pouvez les envoyer par lots.
Prenons un exemple :
En envoyant vos 50 précédentes requêtes par lots de 5, vous obtenez alors 10 lots x 5 requêtes. Cela a un gros impact sur le temps de collecte de données car vous le divisez par 5 tout simplement.
Ainsi, le processus de scraping se déroulera en :
- 10 lots (50/5) x 10s = 100secondes
- au lieu de 50 x 10s =500 secondes.
Dans de nombreux langages de programmation, ce type de traitement est mis en œuvre en utilisant le parallélisme basé sur les threads. Cette notion peut s'avérer délicate pour les débutants, heureusement, il existe un un package fait tout le travail à votre place : grequest .
Cette librairie nous permet d'envoyer plusieurs requêtes en même temps, de manière simple et efficace.
Voici comment le code pour réaliser notre exemple précédent, c’est-à-dire envoyer nos 50 URLs par lots de 5 :
Grequest est parfait pour les scripts mais n'est pas adapté au code de production ou au web scraping à grande échelle. Pour cela, une autre solution s’offre à vous : Scrapy 👇.
4. Collecte de données sur le Web avec Scrapy
Scrapy est un puissant framework Python de web scraping et de web crawling..
Scrapy offre de nombreuses fonctionnalités pour télécharger des pages Web de manière asynchrone, les traiter et les sauvegarder. Il gère le multithreading, le crawling (le processus consistant à aller de lien en lien pour trouver chaque URL d'un site Web), le crawling de sitemap, et bien plus encore.
Scrapy dispose d’une version avec une interface (IDE) mais également d'un mode plus classique appelé Scrapy Shell.
Avec le Scrapy Shell, vous pouvez tester rapidement votre code de scraping, comme les expressions XPath ou les sélecteurs CSS.
L'inconvénient de Scrapy est que la courbe d'apprentissage n’est pas toujours simple. Il y a beaucoup de choses à apprendre pour monter en compétences.
Nous allons écrire un Spider Scrapy qui extrait les 15 premières pages d’un blog de nouvelles et enregistrer le résultat dans un fichier JSON.
Vous pouvez facilement installer Scrapy avec pip :
Vous pouvez ensuite utiliser le CLI de Scrapy pour générer le code de base de notre projet :
Dans notre dossier (spider) nous allons créer un nouveau fichier Python avec le code suivant :
Il y a beaucoup de conventions dans le code pour Scrapy.
Nous définissons un tableau d'URL (l’ensemble des pages que nous allons parcourir).
La méthode Parse sera appelée sur chaque URL dans le tableau (start_urls.)
Nous devons ensuite ajuster un peu Scrapy pour que notre Spider se comporte correctement avec le site Web cible.
Vous devriez toujours l'activer. Elle permet de s'assurer que le site Web cible n'est pas ralenti par vos spiders. Pour ce faire, il analyse le temps de réponse et adapte le nombre de threads simultanés.
Dans le cadre où votre site cible, je vous laisserai pas accéder par le biais de votre bot, n’hésitez pas à ajouter des Headers pour simuler un navigateur web comme ceci :
Pour exécuter ce code voici comment faire(paramétrable avec différents formats de sortie) :
Et le tour est joué ! Vous aurez maintenant toutes vos informations dans un fichier JSON entièrement formaté.
Il y a beaucoup plus à dire sur cet outil mais cela pourrait faire l’objet d’un article totalement dédié.
5. Navigation Headless
Headless et Chromedriver
Scrapy est idéal pour les tâches de web scraping à grande échelle. Cependant, il n'est pas suffisant si vous avez besoin de scraper une page générée par des frameworks Javascript.
Il peut être difficile de scraper ce type d'application car il y a souvent beaucoup d'appels AJAX et de connexions websockets.
Si les performances sont un problème, essayez toujours de reproduire le code Javascript. Cela signifie inspecter manuellement tous les appels réseau avec l'inspecteur de votre navigateur et reproduire les appels AJAX contenant les données intéressantes.
Dans certains cas, il y a trop d'appels HTTP asynchrones pour obtenir les données souhaitées et il peut être plus facile de rendre la page dans un navigateur sans tête.
Un autre bon cas d'utilisation serait de prendre une capture d'écran d'une page, et c'est ce que nous allons faire maintenant.
Vous pouvez installer le paquet selenium avec pip :
Vous aurez également besoin du Chromedriver :
- PS 1 : Sur Mac vous devrez valider dans les paramètres de sécurité le package chromedriver, sinon vous serez rejeté lors de l’exécution Python.
- PS 2 : Utiliser Chromedriver implique que vous ayez Chrome installé sur votre machine.
Ensuite, il suffit d'importer le Webdriver depuis le package Selenium, de configurer Chromedriver avec headless=True et de définir une taille de fenêtre (sinon vous ne verrez pas grand chose) :
Vous devriez obtenir une belle capture d'écran de la page d'accueil :
Voici ce qu’il est possible de Vous pouvez faire beaucoup plus avec l'API Selenium et Chrome :
- Exécution de Javascript
- Remplir des formulaires
- Cliquer sur les éléments
- Extraction d'éléments avec des sélecteurs CSS / expressions XPath
Selenium et Chrome sont la combinaison ultime pour collecter des données sur le web. Vous pouvez automatiser tout ce que vous pouvez faire avec votre navigateur Chrome ordinaire.
Le gros inconvénient est que Chrome a besoin de beaucoup de mémoire et de puissance CPU. Avec un réglage fin, vous pouvez réduire l'empreinte mémoire à 300-400 Mo par instance de Chrome, mais vous avez toujours besoin d'un cœur de processeur par instance.
Dans le cadre où vous souhaiteriez faire fonctionner plusieurs instances de Chrome simultanément, vous pourriez rapidement avoir besoin de serveurs puissants ainsi qu’un monitoring des ressources utilisées.
RoboBrowser et Mechanize
RoboBrowser et Mechanize sont des librairies Python qui vous permettent de naviguer sur le web en utilisant requests et BeautifulSoup à travers un code rapide à mettre en place.
Il ne s'agit pas de navigateurs à proprement parler, mais plutôt d’instances simulant des navigateurs où il est possible de collecter des données et réaliser des actions à l’aide de scripts en python.
Si vous avez besoin d'interagir avec des services web qui n'ont pas d'API, RoboBrowser ou Mechanize peuvent vous aider.
Par exemple, si vous voulez vous connecter sur un site web, vous pouvez écrire un script qui remplira les champs d’un formulaire et "appuiera" sur le bouton de connexion :
Comme vous pouvez le constater, le code est écrit comme si vous effectuiez manuellement les actions dans un vrai navigateur.
RoboBrowser et Mechanize sont vraiment sympas car leur approche simple vous permet de les mettre rapidement en place.
Cependant, étant donné qu’ils n’utilisent pas de vrai navigateur (Un driver de chrome ou de Mozilla par exemple), il ne sont pas en mesure de gérer l'exécution de JavaScript comme les appels AJAX ou certaines applications.
Avis final sur les méthodes de Web Scraping en Python
Voici un tableau récapitulatif rapide de toutes les technologies dont nous avons parlé dans cet article. N'hésitez pas à commenter si vous connaissez des ressources qui, selon vous, ont leur place ici.
J'espère que vous avez apprécié cet article !
Il s’agit d’une introduction rapide aux librairies les plus utilisés pour le web scraping en python.
Si vous ne disposez pas de temps ou n'avez pas encore les compétences nécessaires pour mettre en place un projet avec Python, vous pouvez utiliser des outils de Web Scraping.
LIRE PLUS : Les meilleurs outils pour collecter des données sur le Web
BONUS : Collecte de données du Common Crawl avec Selectolax
Lorsque l'on travaille sur des problèmes de PNL, on a parfois besoin d'obtenir un grand corpus de texte. Internet est la plus grande source de texte, mais malheureusement, l'extraction de texte à partir de pages HTML arbitraires est une tâche difficile et pénible.
Supposons que nous devions extraire le texte complet de diverses pages Web et que nous voulions supprimer toutes les balises HTML. En général, la solution par défaut consiste à utiliser la méthode get_text du module BeautifulSoup qui utilise lxml en interne. Il s'agit d'une solution éprouvée, mais qui peut être très lente lorsqu'on travaille avec des centaines de milliers de documents HTML.
En remplaçant BeautifulSoup par Selectolax, vous pouvez obtenir un gain de vitesse de 5 à 30 fois presque gratuitement !
Voici un benchmark simple qui analyse 10 000 pages HTML provenant de Common Crawl :
Il est clair que ce n'est pas la meilleure façon de tester quelque chose, mais cela donne une idée que selectolax peut être parfois 30 fois plus rapide que lxml.
Le moteur lui-même est un analyseur HTML5 très puissant et rapide écrit en C par lexborisov.
Selectolax n'est pas limité à un seul cas d'utilisation et supporte les sélecteurs CSS ainsi que d'autres fonctions de traversée du HTML.