Retour au blog
Les techniques de web scraping
Suciu DanLast updated on Apr 30, 202634 min read

Comment construire un crawler web en Python : Du début à la fin

Comment construire un crawler web en Python : Du début à la fin
En bref : un robot d'exploration Web en Python automatise la tâche fastidieuse qui consiste à suivre les liens d'un site Web pour découvrir et collecter du contenu. Ce guide vous explique comment en créer un de A à Z avec requests et BeautifulSoup, puis passer à Scrapy pour l'exploration simultanée, les pipelines d'éléments et l'exportation de données structurées. Vous apprendrez également à explorer de manière responsable, à alterner les proxys pour éviter les blocages et à gérer les pages rendues par JavaScript.

Un robot d'indexation Python est un programme qui parcourt automatiquement les sites web en suivant les hyperliens, en découvrant de nouvelles pages et en collectant leur contenu au fur et à mesure. Si le web scraping consiste à extraire des données spécifiques d'une seule page, le web crawling consiste à parcourir un site entier (voire plusieurs sites) pour trouver ces pages en premier lieu.

Python est sans doute le langage le plus populaire pour cette tâche. Entre sa syntaxe lisible, ses bibliothèques HTTP éprouvées et un framework littéralement nommé d’après les araignées web, cet écosystème rend l’exploration accessible sans sacrifier la puissance. Que vous ayez besoin de cartographier toutes les pages de produits d’un site e-commerce, de créer un index de backlinks pour l’analyse SEO ou d’alimenter des pipelines d’apprentissage automatique en données structurées, un robot d’exploration bien conçu est le moteur qui anime l’ensemble du processus.

Ce tutoriel couvre le cycle de vie complet de la création d'un robot d'indexation web en Python : récupérer votre première page avec requests, l'analyse et l'extraction de liens avec BeautifulSoup, puis la mise à l'échelle avec les robots, les sélecteurs et les pipelines d'éléments de Scrapy. Au fil de ce tutoriel, vous apprendrez à gérer les cas particuliers tels que les URL relatives et les API JSON, à respecter le fichier robots.txt, à limiter vos requêtes et à éviter d'être bloqué par les systèmes anti-bots. Chaque section comprend du code exécutable que vous pouvez copier, adapter et étendre pour vos propres projets. À la fin, vous disposerez d'un chemin clair menant d'un prototype de 20 lignes à un pipeline de crawling prêt pour la production.

Qu'est-ce qu'un robot d'indexation Python et pourquoi en créer un ?

À la base, un robot d'exploration Web Python est un script automatisé qui part d'une ou plusieurs URL de départ, récupère le contenu de la page, extrait tous les liens qu'il trouve, puis répète le cycle pour chaque nouvelle URL. Considérez-le comme un visiteur méthodique qui lit le répertoire de chaque étage d'un immeuble avant de décider dans quelles pièces entrer ensuite.

La distinction entre l'exploration (crawling) et l'extraction (scraping) prête constamment à confusion. L'exploration est la phase de découverte : trouver des pages en parcourant le graphe de liens. L'extraction est la phase de récupération : extraire des champs structurés (titres, prix, dates) des pages que vous avez déjà localisées. En pratique, la plupart des projets ont besoin des deux, mais ce sont des préoccupations distinctes avec des exigences différentes en matière d'outils. Comprendre cette distinction vous aide à choisir les bons outils et à structurer correctement votre projet.

Alors, pourquoi en créer un en Python ? Voici quelques raisons concrètes :

  • Audit SEO et cartographie des backlinks : explorez votre propre site pour trouver les liens rompus, les pages orphelines ou les balises méta manquantes. Vous pouvez également parcourir les blogs, les sites partenaires et les médias d’information pour découvrir qui renvoie vers vous ou vos concurrents.
  • Collecte de données pour le ML et l'analyse : rassemblez des données d'entraînement à partir de centaines de pages et acheminez-les directement vers des DataFrames pandas, des magasins de caractéristiques ou des pipelines d'entraînement LLM. La sortie structurée d'un crawler bien conçu alimente directement l'analyse en aval.
  • Surveillance des prix et des stocks : parcourez chaque nuit les pages des catégories de produits pour suivre les variations de prix et les niveaux de stock sur des milliers de références.
  • Recherche et archivage : les chercheurs universitaires explorent des forums, des bases de données gouvernementales et des ensembles de données publics qui ne proposent pas d'API de téléchargement en masse.
  • Agrégation de contenu : les organes de presse et les cabinets d'études de marché explorent les sites sectoriels pour créer des flux de contenu sélectionnés et des tableaux de bord de veille concurrentielle.

L'écosystème Python (requests, BeautifulSoup, Scrapy et bien d'autres) vous permet de prototyper un crawler fonctionnel en moins de 30 lignes de code, puis d'étendre cette même logique à des millions de pages sans changer de langage. C'est précisément ce parcours, du prototype à la production, que couvre ce guide.

Comment fonctionnent les robots d'indexation en coulisses

Tout robot d'indexation Python, qu'il s'agisse d'un script de dix lignes ou d'un système distribué, suit la même boucle fondamentale :

  1. Commencez par des URL de départ. Vous fournissez une ou plusieurs adresses de départ. Celles-ci sont placées dans une file d'attente (souvent appelée « frontière »).
  2. Récupérez la page. Le robot d'indexation envoie une requête HTTP GET pour l'URL suivante dans la file d'attente et reçoit la réponse HTML.
  3. Analysez le code HTML. Un analyseur (BeautifulSoup, lxml, sélecteurs Scrapy) lit le document et expose sa structure sous la forme d’un arbre parcourable.
  4. Extraction des liens. L'analyseur extrait tous les <a href="..."> de la page, ainsi que toutes les autres URL détectables.
  5. Filtrer et dédupliquer. Tous les liens ne méritent pas d'être suivis. Le robot d'indexation compare chaque URL à un ensemble d'URL déjà vues, applique des filtres de domaine ou de chemin d'accès, et élimine les doublons. Cette étape inclut également la normalisation des URL : suppression des fragments, tri des paramètres de requête et conversion des chemins d'accès en minuscules afin que example.com/Page et example.com/page ne soient pas traités comme des URL différentes.
  6. Mettre les nouvelles URL en file d'attente. Les liens restants rejoignent la file d'attente « frontier ».
  7. Répéter jusqu’à ce que la file d’attente soit vide ou qu’une condition d’arrêt soit remplie (profondeur maximale, nombre maximal de pages, limite de temps).

Cette boucle est d'une simplicité trompeuse, mais le véritable travail d'ingénierie réside dans les détails. Comment gérez-vous les pages qui renvoient une redirection 301 vers une URL que vous avez déjà visitée ? Que se passe-t-il lorsque le serveur est lent et que 500 URL attendent dans la file d'attente ? Comment évitez-vous d'explorer le même contenu sous différents modèles d'URL (identifiants de session, paramètres de suivi, widgets de calendrier) ?

Une implémentation naïve récupère les URL une par une, ce qui convient pour quelques dizaines de pages. Dès que vous devez explorer des milliers de pages, vous avez besoin de concurrence (plusieurs requêtes en cours), de files d’attente persistantes et d’une logique de réessai en cas d’échecs temporaires. Les robots d'indexation simples qui ne disposent pas de mécanismes de réessai et récupèrent les pages de manière séquentielle sont véritablement inadaptés à un travail à l'échelle de la production. C'est exactement le vide que Scrapy a été conçu pour combler : il vous offre un moteur asynchrone, un planificateur intégré avec déduplication et des hooks de middleware pour chaque étape de la boucle.

Comprendre cette boucle n'est pas seulement théorique. Lorsque votre crawler se comporte de manière anormale (il manque des pages, revisite la même URL ou ralentit jusqu'à s'arrêter), le bug est presque toujours lié à l'une de ces sept étapes. Le diagnostic des problèmes devient beaucoup plus rapide lorsque vous pouvez identifier précisément l'étape qui pose problème.

Choisir le bon outil de crawling Python

Avant d'écrire la moindre ligne de code, il est judicieux de choisir la bibliothèque adaptée à l'échelle et à la complexité de votre projet. Voici une matrice de décision pratique pour créer un robot d'indexation en Python :

Critères

requests + BeautifulSoup

Scrapy

Service API géré

Temps de configuration

Minutes

15 à 30 min (mise en place du projet)

Minutes (clé API)

Concurrence

Manuelle (threads/asyncio)

Moteur asynchrone intégré

Géré pour vous

Déduplication

Vous le créez

Filtre de planificateur intégré

Géré pour vous

Rendu JS

Non pris en charge

Nécessite un plugin (par exemple, scrapy-playwright)

Souvent inclus

Exportation de données

Manuelle (écriture dans un fichier)

Options CLI pour JSON/CSV

Varie selon le fournisseur

Gestion anti-bot

À configurer soi-même (en-têtes, proxys)

Crochets middleware

Rotation de proxy intégrée, résolution de CAPTCHA

Idéal pour

Petits crawls ponctuels

Explorations récurrentes de moyenne à grande envergure

Sites dotés de défenses anti-bot robustes ou de rendu JS

requests + BeautifulSoup est la combinaison incontournable lorsque vous avez besoin d'un prototype rapide ou d'un crawler qui explore une poignée de pages. Vous contrôlez chaque détail, ce qui est idéal pour l'apprentissage mais catastrophique pour la mise à l'échelle. Les crawlers basiques construits de cette manière revisitent souvent les mêmes pages ou se retrouvent bloqués à suivre des liens répétés sans logique de déduplication rigoureuse.

Scrapy est un framework complet spécialement conçu pour le crawling web à grande échelle. Il gère la concurrence, les tentatives de reprise, la déduplication et les pipelines de données dès son installation. En contrepartie, la courbe d'apprentissage est plus raide et la structure du projet est imposée. Mais une fois que vous avez assimilé le modèle spider/pipeline, la création de nouveaux crawlers devient remarquablement rapide.

Les services API gérés sont pertinents lorsque la partie la plus difficile de votre exploration n'est pas la logique d'analyse, mais l'infrastructure : rotation des proxys, résolution des CAPTCHA, rendu JavaScript. Au lieu de gérer cette pile vous-même, vous envoyez une requête et recevez du HTML (ou du JSON) en retour.

Choisissez l'option la plus simple qui répond à vos besoins. Vous pourrez toujours passer à un niveau supérieur plus tard, et ce guide vous montrera comment progresser à travers chaque niveau.

Configuration de votre environnement Python

Un environnement propre évite les conflits de dépendances et garantit la reproductibilité de votre projet. Voici la configuration minimale pour votre projet de robot d'indexation Python :

# Create and activate a virtual environment
python3 -m venv crawler-env
source crawler-env/bin/activate   # macOS / Linux
crawler-env\\Scripts\\activate      # Windows

# Install core libraries
pip install requests beautifulsoup4 lxml scrapy

Votre dossier de projet devrait ressembler à ceci :

my-crawler/
├── crawler-env/
├── simple_crawler.py      # requests + BS4 version
├── scrapy_project/        # generated by scrapy startproject
│   ├── scrapy_project/
│   │   ├── spiders/
│   │   ├── items.py
│   │   ├── pipelines.py
│   │   └── settings.py
│   └── scrapy.cfg
└── requirements.txt

Fixez vos dépendances avec pip freeze > requirements.txt afin que toute personne clonant le dépôt obtienne les mêmes versions. Si vous prévoyez d'utiliser Scrapy avec un navigateur sans interface graphique pour les pages rendues en JavaScript, ajoutez également scrapy-playwright à la liste d'installation.

lxml est inclus comme backend d'analyse pour BeautifulSoup. Il est nettement plus rapide que le backend intégré à Python html.parser et gère plus efficacement le HTML mal formé, ce qui est important lorsque vous explorez des pages dont le balisage n'a manifestement pas été écrit par des humains.

Une fois ces paquets installés, vous êtes prêt à écrire du code. La section suivante vous explique comment créer un robot d'indexation complet et fonctionnel à partir de zéro.

Création d'un robot d'indexation Python de base avec Requests et BeautifulSoup

Il est temps d'écrire le code proprement dit. L'objectif de ce premier robot d'indexation est simple : partir d'une URL de départ, récupérer la page, trouver tous les liens qu'elle contient, puis visiter chacun de ces liens tout en restant sur le même domaine. Il est volontairement minimaliste afin que vous puissiez comprendre son fonctionnement avant d'ajouter de la complexité.

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

def crawl(seed_url, max_pages=20, delay=1):
    visited = set()
    queue = [seed_url]
    allowed_domain = urlparse(seed_url).netloc

    while queue and len(visited) < max_pages:
        url = queue.pop(0)
        if url in visited:
            continue

        try:
            response = requests.get(
                url,
                headers={"User-Agent": "MyCrawler/1.0 (contact@example.com)"},
                timeout=10,
            )
            response.raise_for_status()
        except requests.RequestException as e:
            print(f"Failed to fetch {url}: {e}")
            continue

        visited.add(url)
        print(f"Crawled: {url} ({response.status_code})")

        soup = BeautifulSoup(response.text, "lxml")

        for anchor in soup.find_all("a", href=True):
            link = urljoin(url, anchor["href"])
            parsed = urlparse(link)
            # Strip fragments and stay on the same domain
            clean_link = parsed._replace(fragment="").geturl()
            if parsed.netloc == allowed_domain and clean_link not in visited:
                queue.append(clean_link)

        time.sleep(delay)  # Be polite

    print(f"Done. Visited {len(visited)} pages.")
    return visited

if __name__ == "__main__":
    crawl("https://example.com")

Passons en revue les choix clés de ce robot d'indexation Python :

  • visited set : il s'agit de votre mécanisme de déduplication. Avant de récupérer une URL, vous vérifiez si elle figure déjà dans le set. Sans cela, le crawler tournerait en boucle indéfiniment sur les sites comportant des liens de navigation circulaires. Même les petits sites peuvent avoir des menus de navigation qui créent des boucles.
  • urljoin: Convertit les chemins relatifs /about en URL absolues telles que example.com/about. C'est essentiel car la plupart des sites utilisent des liens relatifs dans leur navigation et leurs liens internes.
  • Filtrage de domaine : le urlparse vérification maintient le robot d'indexation sur un seul domaine. Sans cela, un seul lien externe pourrait envoyer votre robot d'indexation en spirale à travers tout Internet. C'est ce qui distingue un crawl ciblé d'un crawl incontrôlé.
  • Suppression des fragments : L' _replace(fragment="") appel supprime #section les ancres des URL. Celles-ci renvoient à différentes positions sur la même page, et non à des pages différentes ; les traiter comme des URL distinctes entraînerait donc des récupérations redondantes.
  • max_pages limite : un filet de sécurité particulièrement important pendant le développement. Vous ne voulez pas envoyer accidentellement des milliers de requêtes pendant le débogage de votre analyseur.
  • time.sleep(delay): Une mesure de courtoisie élémentaire. Même une seconde d'intervalle entre les requêtes fait une différence significative pour la charge du serveur cible.
  • User-Agent personnalisé : identifier votre robot d'indexation à l'aide d'un en-tête descriptif (incluant vos coordonnées) est à la fois éthique et pratique. Les sites sont bien moins susceptibles de bloquer un robot d'indexation qui s'identifie honnêtement.
  • Gestion des erreurs : le try/except bloc intercepte les délais d'expiration de connexion, les échecs DNS et les erreurs HTTP (via raise_for_status). Un robot d'indexation en production en a besoin ; un processus qui plante signifie une perte de progression et des données potentiellement incomplètes.

Ce robot est synchrone, ce qui signifie qu’il récupère une page à la fois. Pour 20 pages, cela convient parfaitement. Pour 2 000, ce serait d’une lenteur pénible. Nous aborderons cette limitation lorsque nous passerons à Scrapy plus loin dans ce guide.

Extraction et filtrage des liens

La logique d'extraction des liens du crawler de base fonctionne, mais les pages réelles sont désordonnées. Les balises d'ancrage pointent vers des PDF, des adresses mailto, des appels JavaScript vides, des liens vers des fragments uniquement et des variantes de chaînes de requête de la même page. Un filtre plus intelligent vous évite de gaspiller des requêtes sur des URL indésirables et permet de cibler votre exploration.

from urllib.parse import urljoin, urlparse

IGNORED_EXTENSIONS = {".pdf", ".jpg", ".png", ".gif", ".zip", ".exe", ".mp4"}

def extract_links(soup, base_url, allowed_domain):
    links = set()
    for anchor in soup.find_all("a", href=True):
        raw = anchor["href"]

        # Skip non-HTTP schemes
        if raw.startswith(("mailto:", "javascript:", "tel:", "#")):
            continue

        full_url = urljoin(base_url, raw)
        parsed = urlparse(full_url)

        # Strip fragments for deduplication
        clean = parsed._replace(fragment="").geturl()

        # Domain filter
        if parsed.netloc != allowed_domain:
            continue

        # Extension filter
        if any(clean.lower().endswith(ext) for ext in IGNORED_EXTENSIONS):
            continue

        links.add(clean)
    return links

Cette fonction gère les cas limites les plus courants lorsque vous explorez un site web avec Python : elle résout les URL relatives, supprime les identifiants de fragment (la #section partie qui ne modifie pas la page réelle), ignore les schémas non-HTTP et ignore les extensions de fichiers binaires. Le résultat est un ensemble propre d'URL du même domaine, prêt à être ajouté à la file d'attente de crawl.

Pour une détection encore plus efficace des URL en double, envisagez de normaliser les paramètres de requête en les triant par ordre alphabétique. Deux URL qui ne diffèrent que par l'ordre des paramètres (?a=1&b=2 vs. ?b=2&a=1) renvoient généralement le même contenu, et les traiter comme distinctes gaspille de la bande passante. Vous pouvez également appliquer la gestion des URL canoniques en recherchant les balises <link rel="canonical"> balises dans le code HTML, qui vous indiquent l'URL préférée pour un élément de contenu.

Une autre technique utile est la détection de modèles d'URL. Si vous remarquez que le robot d'exploration génère des milliers d'URL correspondant à un modèle tel que /calendar?date=2024-01-01, /calendar?date=2024-01-02, etc., vous pouvez ajouter une liste de refus d'expressions régulières pour court-circuiter ces chemins avant même qu'ils n'entrent dans la file d'attente.

Gestion des URL relatives et des cas limites

Les URL relatives sont la source de bugs la plus courante dans un robot d'indexation Python débutant. Une page à l'adresse example.com/blog/ peut contenir des liens tels que ../about, ./post-1, ou même //cdn.example.com/image.png. La fonction urllib.parse.urljoin gère correctement tous ces cas, c'est pourquoi il est apparu dans tous les exemples de code jusqu'à présent.

Au-delà des chemins relatifs, méfiez-vous de ces cas particuliers :

  • Chaînes de redirection : une redirection 301 ou 302 signifie que l'URL finale diffère de celle que vous avez demandée. Utilisez response.url (et non l'URL de la requête d'origine) lorsque vous ajoutez un élément à votre ensemble de pages visitées, sinon vous explorerez deux fois la même page sous des adresses différentes.
  • 404 « souples » : certains sites renvoient un statut 200 mais affichent un corps générique « page introuvable ». Si vous extrayez des données, vérifiez la présence d'un marqueur de contenu (comme le titre d'un produit) avant de considérer la page comme valide.
  • Caractères encodés en URL : %20 par rapport à un espace littéral, %2F vs. /. Normalisez-les avant la déduplication pour éviter de traiter les variantes encodées et non encodées comme des URL distinctes.
  • Modèles d'URL infinis : les widgets de calendrier, les identifiants de session dans le chemin d'accès ou les combinaisons de filtres peuvent générer un nombre illimité d'URL d'apparence unique qui servent toutes un contenu similaire. Définissez une profondeur de crawl maximale ou utilisez la détection de modèles d'URL pour briser la boucle.

Gérer ces cas dès le départ permet d'économiser des heures de débogage par la suite. Un robot d'exploration qui comptabilise silencieusement deux fois les pages ou passe à côté de contenu à cause d'une barre oblique finale est plus difficile à corriger qu'un robot qui plante bruyamment face à une URL mal formée.

Exploration et analyse des API JSON

Tous les sites web ne fournissent pas leurs données au format HTML. De nombreuses applications web modernes chargent du contenu à partir d'API internes qui renvoient du JSON au lieu d'intégrer les données directement dans le balisage de la page. Si vous inspectez les requêtes réseau dans les outils de développement de votre navigateur (onglet Réseau, filtré par XHR/Fetch), vous trouverez souvent des points de terminaison qui vous fournissent des données structurées sans qu'aucun analyseur syntaxique HTML ne soit nécessaire.

Voici un modèle pour explorer une API JSON paginée :

import requests
import json
import time

def crawl_json_api(base_url, max_pages=10, delay=1):
    all_items = []
    page = 1

    while page <= max_pages:
        response = requests.get(
            base_url,
            params={"page": page, "per_page": 50},
            headers={"Accept": "application/json"},
            timeout=10,
        )
        response.raise_for_status()
        data = response.json()

        items = data.get("results", [])
        if not items:
            break  # No more data

        all_items.extend(items)
        print(f"Page {page}: fetched {len(items)} items")

        # Check for explicit pagination metadata
        if not data.get("has_next", True):
            break

        page += 1
        time.sleep(delay)

    return all_items

# Example usage
items = crawl_json_api("https://api.example.com/products")
print(f"Total items collected: {len(items)}")

L'approche est presque identique à l'exploration HTML, à deux différences près. Premièrement, vous ignorez complètement l'étape d'analyse HTML car response.json() vous fournit un dictionnaire Python natif. Deuxièmement, la pagination est généralement explicite : l'API renvoie soit une next URL, un has_next indicateur, soit vous incrémentez un paramètre de page jusqu’à ce que le tableau de résultats revienne vide.

Lorsque l'API nécessite une authentification (une clé API ou un jeton de session), transmettez-la via les en-têtes plutôt que par des paramètres de requête afin d'éviter de divulguer les identifiants dans les journaux du serveur. Et vérifiez toujours la présence d'en-têtes de limitation de débit (X-RateLimit-Remaining, Retry-After). Respecter ces en-têtes est à la fois courtois et pratique, car le serveur vous bloquera si vous les ignorez.

Cette approche s'associe bien avec des outils comme pandas. Une fois que vous disposez d'une liste de dictionnaires issus de votre exploration JSON, les charger dans un DataFrame pour analyse ou exportation ne nécessite qu'un seul pd.DataFrame(items) appel. Vous pouvez ensuite utiliser pandas pour nettoyer, filtrer et analyser les données collectées ou y entraîner directement des modèles d'apprentissage automatique.

Évoluer avec Scrapy

Une fois que vos besoins en matière de crawling dépassent le cadre d’une boucle synchrone requests , Scrapy est la prochaine étape logique. Il s'agit d'un framework Python complet conçu spécifiquement pour le crawling Web à grande échelle, qui gère les problèmes d'infrastructure complexes (concurrence, tentatives de reconnexion, déduplication et régulation du débit) afin que vous puissiez vous concentrer sur la logique d'analyse.

L'architecture de Scrapy comporte cinq composants principaux qui fonctionnent ensemble :

  • Moteur : le coordinateur central. Il transmet les requêtes au téléchargeur et les réponses aux araignées, orchestrant ainsi l'ensemble du cycle de crawling.
  • Planificateur : gère la file d'attente des requêtes et déduplique automatiquement les URL à l'aide de l'empreinte digitale. Vous n'avez jamais à créer de visited ensemble manuellement.
  • Downloader : envoie des requêtes HTTP de manière asynchrone à l'aide de la boucle d'événements de Twisted, ce qui signifie que des centaines de requêtes peuvent être en cours simultanément sans surcharge liée aux threads.
  • Spiders : votre code. Chaque classe de spider définit les URL de départ et la manière d'analyser chaque réponse. C'est là que réside votre logique spécifique au domaine.
  • Pipelines d'éléments : étapes de post-traitement qui nettoient, valident et stockent les données extraites par vos araignées. Vous pouvez enchaîner plusieurs pipelines pour répondre à différents besoins.

Ce qui rend cette architecture puissante, c'est que chaque composant est modulable. Besoin d'en-têtes personnalisés pour chaque requête ? Écrivez un middleware de téléchargement. Vous souhaitez écarter les éléments dont certains champs sont manquants ? Ajoutez un pipeline de validation. Besoin de transférer les résultats vers une base de données plutôt que vers un fichier JSON ? Remplacez l'exportateur par défaut par un pipeline personnalisé.

Scrapy peut traiter les pages beaucoup plus rapidement qu'une boucle de requêtes à thread unique. Le framework gère les requêtes simultanées sur plusieurs domaines tout en respectant les limites de débit que vous définissez. Il respecte également le fichier robots.txt par défaut (via le ROBOTSTXT_OBEY paramètre), ce que de nombreux crawlers développés manuellement oublient de mettre en œuvre.

Le revers de la médaille, c'est la complexité. Scrapy a une structure de projet bien définie, une courbe d'apprentissage et son propre vocabulaire (spiders, items, pipelines, middlewares). Mais une fois que vous avez assimilé le modèle, vous pouvez créer des crawlers de niveau production remarquablement rapidement. La suite de ce guide vous montre comment.

Créer un projet Scrapy et votre premier spider

Mettons en place un véritable projet Scrapy. Ouvrez un terminal dans votre environnement virtuel et exécutez :

scrapy startproject bookstore
cd bookstore
scrapy genspider books books.toscrape.com

Cela génère l'arborescence complète des répertoires : settings.py, items.py, pipelines.py, ainsi qu'un spiders/ dossier contenant votre nouvelle books.py spider. La genspider commande pré-remplit le spider avec les allowed_domains et une liste de démarrage start_urls . Ouvrez ce fichier de spider et remplacez le code standard par un analyseur fonctionnel :

import scrapy

class BooksSpider(scrapy.Spider):
    name = "books"
    allowed_domains = ["books.toscrape.com"]
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for book in response.css("article.product_pod"):
            yield {
                "title": book.css("h3 a::attr(title)").get(),
                "price": book.css(".price_color::text").get(),
                "availability": book.css(
                    ".instock.availability::text"
                ).getall()[-1].strip(),
            }

        # Follow the "next" pagination link
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

Exécutez le spider avec :

scrapy crawl books -o books.json

Cette seule commande démarre le moteur, récupère toutes les pages de listes paginées, extrait les données des livres et écrit les résultats dans un fichier JSON. Pas de boucle manuelle, pas de liste de pages visitées, pas de code standard pour l'écriture de fichiers. C'est là toute la puissance d'un robot d'indexation Python construit sur un framework adapté.

Quelques points à noter concernant ce spider :

  • allowed_domains limite le crawler au site cible. Tous les liens hors domaine découverts lors de l'analyse sont ignorés, ce qui empêche votre spider de se promener sur des sites externes.
  • response.css() utilise des sélecteurs CSS sur le corps de la réponse. Scrapy analyse le code HTML une seule fois et met en cache l'arborescence analysée, ce qui rend peu coûteux l'appel de plusieurs sélecteurs sur la même réponse.
  • yield au lieu de return: les robots d'indexation Scrapy sont des générateurs. Vous produisez des éléments (dictionnaires ou objets Item) et des requêtes. Le moteur décide quand et comment les planifier, en gérant la concurrence à votre place.
  • response.follow() gère les URL relatives en interne (aucun urljoin besoin) et déduplique automatiquement les URL déjà vues grâce au filtre d'empreinte digitale du planificateur.

Voici un spider de crawling web complet et fonctionnel en environ 20 lignes de code. Tout le reste (gestion HTTP, planification, téléchargements simultanés, exportation) est géré par le framework Scrapy. À partir de là, vous pouvez étendre le spider avec un suivi de liens plus approfondi, des callbacks de parsing supplémentaires et un traitement en pipeline.

Suivi des liens sur plusieurs pages

La pagination est l'un des modèles les plus courants lors de la création d'un robot d'indexation web en Python. La plupart des sites de listes répartissent les résultats sur plusieurs pages, et votre robot doit suivre ces liens « Suivant » jusqu'à ce qu'il n'y en ait plus. Le robot Scrapy ci-dessus illustre déjà l'approche de base, mais examinons une version plus robuste qui gère des structures de liens plus profondes à l'aide de l'utilitaire LinkExtractor.

import scrapy
from scrapy.linkextractors import LinkExtractor

class DeepCrawlSpider(scrapy.Spider):
    name = "deepcrawl"
    start_urls = ["https://example.com/catalog"]
    allowed_domains = ["example.com"]

    link_extractor = LinkExtractor(
        allow=r"/catalog/",
        deny=[r"/login", r"/cart", r"/account"],
    )

    def parse(self, response):
        # Extract data from the current page
        for product in response.css(".product-card"):
            yield {
                "name": product.css("h2::text").get(),
                "url": response.urljoin(product.css("a::attr(href)").get()),
                "price": product.css(".price::text").get(),
            }

        # Follow all matching links found on the page
        for link in self.link_extractor.extract_links(response):
            yield scrapy.Request(link.url, callback=self.parse)

LinkExtractor est l'utilitaire de Scrapy permettant d'extraire des liens d'une page à partir de modèles d'expressions régulières. Le allow ne conserve que les URL correspondant à /catalog/, tandis que deny filtre les pages de connexion, de panier et de compte qui gaspilleraient des requêtes. Cette méthode est bien plus facile à maintenir que de coder manuellement les vérifications d'URL dans votre méthode parse, surtout lorsque le nombre de modèles d'exclusion augmente.

Pour les sites qui utilisent des boutons « Charger plus » au lieu des liens de pagination traditionnels, vous trouverez généralement un point de terminaison API sous-jacent dans l'onglet Réseau. Construisez la requête suivante manuellement en incrémentant un paramètre de page ou de décalage, exactement comme le modèle de crawling d'API JSON abordé plus haut dans ce guide.

Une erreur courante consiste à oublier de définir une limite de profondeur. Scrapy DEPTH_LIMIT limite le nombre de sauts de lien que le crawler suivra à partir de l'URL de départ. Sans cela, un spider sur un site volumineux peut mettre en file d'attente des millions d'URL avant que vous ne vous en rendiez compte. Commencez par une limite prudente (3 à 5) pendant le développement, puis augmentez-la une fois que votre spider est stable et que vous avez confiance en votre logique de filtrage.

Une autre technique utile consiste à combiner CrawlSpider (une classe intégrée à Scrapy) avec des Rule des objets. Cette approche vous permet de définir des règles de suivi de liens de manière déclarative, en séparant la logique de navigation de la logique d'extraction de données. Elle facilite la compréhension des explorations complexes à plusieurs niveaux.

Sélecteurs XPath vs CSS pour l'extraction de données

Scrapy prend en charge à la fois les sélecteurs CSS et les expressions XPath pour l'analyse du HTML, et vous pouvez les combiner librement au sein d'un même spider. Savoir quand utiliser l'un ou l'autre vous fait gagner du temps et rend vos sélecteurs plus lisibles.

Fonctionnalité

Sélecteurs CSS

XPath

Syntaxe

Familier aux développeurs front-end

Langage de requête XML

Extraction de texte

::text pseudo-élément

text() fonction

Accès aux attributs

::attr(href)

@href

Parcours des parents

Non pris en charge

.. ou ancestor:: axe

Logique conditionnelle

Limité (:nth-child, :not)

Riche (contains(), starts-with(), opérateurs booléens)

Lisibilité

Généralement plus concis

Plus verbeux mais plus expressif

Utilisez les sélecteurs CSS lorsque vous ciblez des éléments par classe, ID ou hiérarchie simple. Ils sont plus courts, plus faciles à lire et suffisants pour la majorité des tâches d'extraction :

# CSS: get all product titles
titles = response.css("h3.product-title::text").getall()

# CSS: get the href of every link inside a nav element
nav_links = response.css("nav a::attr(href)").getall()

Utilisez XPath lorsque vous devez remonter dans l'arborescence DOM, faire correspondre un contenu textuel partiel ou appliquer une logique conditionnelle que le CSS ne peut pas exprimer :

# XPath: find links whose visible text contains "Next"
next_link = response.xpath('//a[contains(text(), "Next")]/@href').get()

# XPath: get the parent div of a specific span
parent = response.xpath('//span[@class="price"]/..').get()

# XPath: select items where the price is not empty
priced_items = response.xpath(
    '//div[@class="product"][.//span[@class="price" and text()]]'
).getall()

En pratique, la plupart des robots Scrapy utilisent des sélecteurs CSS pour environ 80 % de leur travail d'extraction et passent à XPath pour les cas marginaux restants où le CSS ne suffit pas. Scrapy convertit les sélecteurs CSS en XPath en interne avant de les exécuter, il n'y a donc pas de différence de performance entre les deux approches. Choisissez celle qui rend votre intention la plus claire pour chaque tâche d'extraction spécifique.

Un conseil pratique : lors du débogage des sélecteurs, utilisez scrapy shell "https://target-url.com" pour ouvrir une session interactive. Vous pouvez tester à la fois les expressions CSS et XPath sur une page en direct sans exécuter votre spider complet, ce qui accélère considérablement le développement.

Exportation des données explorées au format JSON et CSV

Les exportations de flux intégrées à Scrapy prennent en charge les formats de sortie les plus courants sans aucun code personnalisé. Vous avez déjà vu la commande d'exportation de base :

# Export to JSON
scrapy crawl books -o output.json

# Export to CSV
scrapy crawl books -o output.csv

# Export to JSON Lines (one JSON object per line, better for large datasets)
scrapy crawl books -o output.jsonl

Le -o ajoute le contenu au fichier s'il existe déjà, ce qui peut produire du JSON mal formé lors d'exécutions répétées. Utilisez -O (O majuscule, disponible dans Scrapy 2.3+) pour écraser le fichier à la place.

Pour un meilleur contrôle de votre pipeline d'exportation de données, configurez les exportations dans settings.py:

FEEDS = {
    "data/books.json": {
        "format": "json",
        "encoding": "utf-8",
        "indent": 2,
        "overwrite": True,
    },
    "data/books.csv": {
        "format": "csv",
        "fields": ["title", "price", "availability"],
    },
}

Le FEEDS dictionnaire vous permet de générer simultanément plusieurs formats, de contrôler l’ordre des champs dans le CSV et de définir l’encodage. Cela est particulièrement utile lorsque différents utilisateurs ont besoin de formats différents : votre équipe d’analyse souhaite un CSV avec des colonnes spécifiques, vos utilisateurs d’API veulent des JSON Lines pour l’ingestion en continu, et vos archives ont besoin d’un instantané JSON bien formaté.

Le format JSON Lines (.jsonl) mérite une attention particulière pour les crawls de grande envergure. Contrairement au JSON standard, qui regroupe tout dans un seul tableau, JSON Lines écrit un objet JSON complet par ligne. Cela signifie que vous pouvez traiter le fichier en continu ligne par ligne, ajouter de nouveaux enregistrements sans réanalyser l'ensemble du fichier et récupérer des résultats partiels si un crawl plante en cours d'exécution.

Si vous devez transférer des données vers une base de données, une file d'attente de messages ou un compartiment de stockage cloud, ignorez complètement l'exportateur de fichiers et écrivez un pipeline d'éléments personnalisé. Cette approche vous offre un contrôle total sur la validation, la transformation et la logique de stockage.

Nettoyage des données avec les pipelines d'éléments Scrapy

Les données brutes issues du crawling ne sont presque jamais propres. Les prix comportent des espaces à la fin, les titres contiennent des sauts de ligne parasites et certaines pages renvoient des enregistrements incomplets. Le système de pipeline d'éléments de Scrapy vous permet de traiter chaque élément entre l'extraction et l'exportation, garantissant ainsi que votre sortie est cohérente et valide.

Voici un pipeline qui gère les trois tâches de nettoyage les plus courantes :

from scrapy.exceptions import DropItem

class CleaningPipeline:
    def __init__(self):
        self.seen_titles = set()

    def process_item(self, item, spider):
        # 1. Strip whitespace from all string fields
        for field in item:
            if isinstance(item[field], str):
                item[field] = item[field].strip()

        # 2. Validate required fields
        if not item.get("title"):
            raise DropItem(f"Missing title: {item}")

        # 3. Drop duplicates based on title
        if item["title"] in self.seen_titles:
            raise DropItem(f"Duplicate: {item['title']}")
        self.seen_titles.add(item["title"])

        return item

Pour activer le pipeline, enregistrez-le dans settings.py:

ITEM_PIPELINES = {
    "bookstore.pipelines.CleaningPipeline": 300,
}

L'entier (300) correspond à la priorité. Les nombres les plus bas s'exécutent en premier, ce qui vous permet d'enchaîner plusieurs pipelines : un pipeline de nettoyage à 300, un pipeline de validation à 400 et un pipeline d'écriture en base de données à 500. Chaque pipeline reçoit l'élément, le traite, puis renvoie l'élément modifié (en le transmettant au pipeline suivant) ou lève une DropItem pour le supprimer entièrement.

Le déclenchement de DropItem supprime l'élément de la sortie et consigne un message. Cette méthode est plus propre que le filtrage a posteriori, car les éléments supprimés n'atteignent jamais l'exportateur ni la base de données. Vous pouvez surveiller le taux de suppression de votre exploration pour identifier rapidement les problèmes d'analyse.

Pour les projets qui extraient des données de dizaines de types de pages différents, envisagez de définir des éléments Scrapy formels (ou des chargeurs d'éléments basés sur des classes de données) plutôt que de simples dictionnaires. Les éléments imposent un schéma, fournissent des valeurs par défaut et fonctionnent avec les processeurs de chargeurs d'éléments de Scrapy pour des transformations au niveau des champs comme MapCompose(str.strip, str.lower). Cela est particulièrement utile dans le cadre de projets en équipe où plusieurs développeurs écrivent des robots basés sur le même modèle de données.

Exploration responsable : robots.txt, limites de débit et éthique

Un robot d'indexation Python qui ignore les règles d'un site finira par être bloqué et, dans certaines juridictions, cela pourrait entraîner des risques juridiques. Un crawling responsable n'est pas seulement une question de bonnes manières ; c'est une exigence pratique pour tout robot d'indexation qui doit fonctionner de manière fiable au fil du temps.

Respecter le fichier robots.txt

Le fichier robots.txt se trouve à la racine de chaque site web (par exemple, https://example.com/robots.txt) et indique aux robots d'indexation quels chemins sont interdits d'accès et à quelle fréquence ils doivent effectuer des requêtes. Voici comment l'analyser par programmation à l'aide de la bibliothèque standard de Python :

from urllib.robotparser import RobotFileParser

rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()

if rp.can_fetch("*", "https://example.com/private/data"):
    print("Allowed")
else:
    print("Blocked by robots.txt")

crawl_delay = rp.crawl_delay("*")
print(f"Recommended delay: {crawl_delay} seconds")

Scrapy vérifie automatiquement le fichier robots.txt lorsque ROBOTSTXT_OBEY = True (paramètre par défaut). Si vous utilisez requests et BeautifulSoup, vous devez implémenter cette vérification vous-même avant chaque récupération.

Configuration des délais d'exploration et de la limitation

Même si le fichier robots.txt ne spécifie pas de délai de crawl, bombarder un serveur de centaines de requêtes simultanées est le moyen le plus rapide de se faire bannir par adresse IP. Dans Scrapy, trois paramètres contrôlent votre rythme de crawl :

# settings.py
DOWNLOAD_DELAY = 1                      # seconds between requests
CONCURRENT_REQUESTS_PER_DOMAIN = 8      # max parallel requests to one domain
AUTOTHROTTLE_ENABLED = True             # dynamically adjusts delay based on load
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0   # target number of parallel requests

AUTOTHROTTLE est particulièrement utile car il s'adapte automatiquement en fonction des temps de réponse du serveur. Si le serveur répond rapidement, Scrapy accélère. Si les temps de réponse augmentent brusquement (ce qui indique une surcharge du serveur), il ralentit. Cela permet d'équilibrer le débit et la courtoisie sans que vous ayez à deviner le bon délai fixe.

Directives éthiques

Au-delà des paramètres techniques, respectez les principes suivants :

  • Identifiez votre robot d'exploration à l'aide d'une chaîne User-Agent descriptive comprenant vos coordonnées.
  • Ne crawlez pas les pages protégées par un système d'authentification ou un accès payant, sauf si vous disposez d'une autorisation explicite.
  • Mettez les réponses en cache localement pendant le développement afin de ne pas solliciter les serveurs en production à chaque exécution de test.
  • Respectez le protocole d'exclusion des robots (Robots Exclusion Protocol) comme référence, même s'il n'est pas juridiquement contraignant dans votre juridiction.

Lorsque vous passez à l'échelle supérieure, n'oubliez pas que les sites web ne sont pas conçus pour gérer des centaines de requêtes simultanées provenant de robots. La surcharge d'un serveur affecte les utilisateurs réels, et un crawling responsable vous garantit de pouvoir revenir sur le même site le lendemain sans que votre adresse IP ne figure sur une liste noire.

Éviter les blocages : agents utilisateurs, proxys et stratégies anti-bot

Même les robots d'exploration bien élevés se font bloquer. Les sites web déploient des systèmes anti-bot qui recherchent des schémas récurrents : requêtes répétées provenant d'une même adresse IP, en-têtes User-Agent manquants ou génériques, et cadence de requêtes qu'aucun humain ne produirait. Voici comment rendre votre robot d'exploration web Python plus résilient.

Rotation des User-Agent

L'agent utilisateur par défaut de la plupart des bibliothèques HTTP les identifie comme des bots. Les sites qui filtrent cet en-tête rejetteront immédiatement vos requêtes. Définissez un en-tête réaliste, similaire à celui d'un navigateur :

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/124.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers, timeout=10)

Pour les sessions de crawling plus longues, alternez entre une liste de chaînes User-Agent afin d'éviter de présenter la même empreinte à chaque requête. Dans Scrapy, le scrapy-fake-useragent middleware gère automatiquement cette rotation.

Rotation des proxys

Les limites de débit basées sur l'adresse IP constituent le mécanisme de blocage le plus courant. Si toutes vos requêtes proviennent d'une seule adresse, le site détecte immédiatement ce schéma. Le routage du trafic via des proxys en rotation répartit vos requêtes sur de nombreuses adresses IP, faisant ainsi passer chacune d'entre elles pour un visiteur indépendant.

Les proxys résidentiels sont particulièrement efficaces car ils utilisent des adresses IP attribuées à de véritables foyers, ce qui les rend pratiquement impossibles à distinguer du trafic des utilisateurs lambda. Les adresses IP de centres de données, bien que plus rapides et moins chères, sont plus faciles à identifier et à bloquer en masse par les systèmes anti-bots.

Reconnaître quand vous êtes bloqué

Avant d'investir dans des contre-mesures, apprenez à reconnaître les symptômes :

  • HTTP 403 ou 429 : refus explicite ou réponse de limitation de débit.
  • Redirection vers une page CAPTCHA : le serveur veut la preuve que vous êtes un humain.
  • Code HTML vide ou de remplacement : la page se charge mais ne contient aucun contenu significatif, juste un squelette ou un message « veuillez patienter ».
  • Pics soudains de temps de réponse : le serveur vous ralentit intentionnellement (une technique appelée « tarpitting »).

Lorsque vous détectez un blocage, reculez avant de réessayer. Un recul exponentiel (attendre 1 s, puis 2 s, puis 4 s, puis 8 s) est un choix par défaut raisonnable. Le middleware de réessai de Scrapy gère automatiquement les échecs transitoires, mais les blocages persistants nécessitent généralement un changement de stratégie : des adresses IP différentes, des taux de requêtes plus lents, ou une couche de rendu pour les sites qui ne servent du contenu qu'aux navigateurs réels.

Les défenses anti-bot sont une course à l'armement. Dès que les défis JavaScript, l'empreinte digitale du navigateur et les CAPTCHA entrent en jeu, les simples scripts de crawling se heurtent à un mur. Le choix pragmatique consiste souvent à déléguer cette complexité à un service spécialisé plutôt que de maintenir votre propre pool de proxys et votre infrastructure d'automatisation de navigateur.

Gestion des pages rendues par JavaScript

Un nombre croissant de sites web s'appuient sur du JavaScript côté client pour afficher leur contenu. Lorsque vous récupérez l'une de ces pages avec requests, vous obtenez une coquille HTML avec des <div> et un ensemble de scripts JavaScript. Les données réelles se chargent après l'exécution des scripts dans un environnement de navigateur, ce qui signifie que les robots d'indexation traditionnels basés sur HTTP ne voient rien d'utile.

Vous disposez de trois options principales pour gérer cela lorsque vous développez un robot d'indexation web en Python :

1. Trouvez l'API sous-jacente. Avant de vous tourner vers un navigateur sans interface graphique, ouvrez les outils de développement de votre navigateur et consultez l'onglet Réseau. De nombreuses applications monopages récupèrent des données à partir d'une API JSON que vous pouvez appeler directement, contournant ainsi entièrement le problème de rendu. C'est l'approche la plus rapide et la plus économe en ressources lorsqu'elle fonctionne.

2. Utilisez un navigateur sans interface graphique. Des outils comme Playwright et Puppeteer vous permettent de contrôler une instance réelle (sans interface graphique) de Chrome ou de Firefox à partir de votre code. Le navigateur exécute le JavaScript, attend que le contenu s'affiche, puis vous extrayez les données du DOM entièrement chargé. Scrapy s'intègre à Playwright via le scrapy-playwright plugin, qui vous permet de marquer de manière sélective des requêtes spécifiques pour le rendu par le navigateur tout en conservant le reste sous forme d'appels HTTP rapides et légers :

# In a Scrapy spider, mark a request for Playwright rendering
yield scrapy.Request(
    url,
    meta={"playwright": True, "playwright_page_methods": [
        {"method": "wait_for_selector", "args": [".product-list"]},
    ]},
    callback=self.parse_products,
)

3. Utilisez un service de rendu géré. Si vous ne souhaitez pas gérer et maintenir une infrastructure de navigateurs sans interface graphique (qui consomme beaucoup de mémoire et de CPU), des services API gérés peuvent se charger du rendu à votre place. Ils renvoient le code HTML entièrement chargé afin que vous puissiez l'analyser avec vos sélecteurs BeautifulSoup ou Scrapy existants.

Le choix approprié dépend du volume et de la complexité. Pour quelques centaines de pages riches en JavaScript, un navigateur sans interface utilisateur local est tout à fait gérable. Pour des milliers de pages réparties sur des sites dotés de protections anti-bot, la charge opérationnelle liée à la gestion des instances de navigateur, au traitement des fuites de mémoire et à la récupération après des plantages s'accumule rapidement.

Simplifier les crawls complexes grâce à une API gérée

À un certain stade, la partie la plus difficile de la création d’un crawler web Python n’est plus la logique d’analyse, mais tout le reste : la gestion des pools de proxys, la résolution des CAPTCHA, la rotation des empreintes de navigateur et la mise à jour des systèmes anti-bot qui actualisent leurs défenses chaque semaine. Lorsque la charge liée à l’infrastructure l’emporte sur le travail d’extraction, il est judicieux de décharger cette couche et de se concentrer sur ce qui importe réellement à votre code : les données.

Un service d'API gérée s'intercale entre votre robot d'indexation et le site web cible. Vous envoyez une requête avec l'URL cible, et le service gère en arrière-plan la rotation des proxys, le rendu JavaScript, les tentatives de reconnexion et les contre-mesures anti-bot. Ce que vous recevez en retour est du code HTML propre (ou du JSON structuré) que vous analysez avec le même code BeautifulSoup ou Scrapy que vous utilisez déjà. Votre logique d'indexation ne change pas ; seule la couche de récupération change.

Cette approche est particulièrement pratique lorsque :

  • Vous explorez des sites dotés d'une détection de bots agressive qui bloque les adresses IP des centres de données en quelques minutes.
  • Vous avez besoin d'un rendu JavaScript à grande échelle, mais ne souhaitez pas gérer une flotte d'instances de navigateurs headless et les coûts de mémoire et de CPU associés.
  • Le temps de développement de votre équipe est mieux consacré à l'analyse des données et au développement de pipelines qu'à la maintenance de l'infrastructure proxy.
  • Vous devez explorer de nombreux sites cibles différents, chacun disposant de sa propre pile anti-bot, ce qui rend impraticable une solution locale universelle.

Le compromis réside dans le coût. Vous payez par requête réussie au lieu de gérer votre propre infrastructure. Pour les explorations à haut volume et de longue durée où les cibles ne sont pas fortement défendues, la rentabilité penche à nouveau en faveur des configurations autogérées. Pour la plupart des projets impliquant des sites protégés contre les bots, cependant, le temps gagné par les développeurs compense largement les frais par requête.

Points clés

  • Commencez simplement, puis évoluez de manière réfléchie. Un requests + BeautifulSoup suffit pour les petites tâches et l'apprentissage. Passez à Scrapy lorsque vous avez besoin de concurrence, de déduplication automatique et de pipelines de données structurés.
  • La déduplication est indispensable. Utilisez un ensemble d'URL vues correctement normalisées (ou laissez le planificateur de Scrapy s'en charger) pour éviter les boucles infinies et le gaspillage de bande passante.
  • Effectuez vos crawls de manière responsable à chaque fois. Respectez le fichier robots.txt, configurez des délais de crawl, utilisez AUTOTHROTTLE et identifiez votre bot avec un User-Agent descriptif. Cela protège à la fois le site cible et la réputation de votre propre adresse IP.
  • Gérez JavaScript de manière réfléchie. Vérifiez d'abord les API sous-jacentes, utilisez des navigateurs sans interface graphique si nécessaire, et envisagez des services gérés lorsque vous avez besoin d'un rendu JS à grande échelle.
  • Nettoyez les données pendant l'exploration, pas après. Les pipelines d'éléments de Scrapy vous permettent de valider, dédupliquer et transformer les enregistrements avant même qu'ils n'atteignent votre fichier d'exportation ou votre base de données.

FAQ

Quelle est la différence entre l'exploration Web et le scraping Web ?

L'exploration est le processus de découverte : un programme automatisé suit les hyperliens entre les pages pour cartographier la structure d'un site et trouver des URL. L'extraction est l'étape de récupération : extraire des champs de données spécifiques (prix, titres, dates) à partir de pages déjà localisées. La plupart des projets concrets combinent les deux, mais ils résolvent des problèmes différents et bénéficient souvent d'outils et de stratégies différents.

Est-il légal d'explorer un site web avec Python ?

Cela dépend de la juridiction, des conditions d'utilisation du site web et du type de données que vous collectez. Aux États-Unis, l'arrêt hiQ c. LinkedIn de 2022 a confirmé que l'accès à des données accessibles au public ne constitue pas une violation de la loi sur la fraude et les abus informatiques (Computer Fraud and Abuse Act). Cependant, les restrictions des conditions d'utilisation, le droit d'auteur et les réglementations en matière de confidentialité telles que le RGPD peuvent toujours s'appliquer. Consultez toujours un conseiller juridique avant de procéder à un crawling à grande échelle, en particulier à des fins commerciales.

Comment gérer les sites web riches en JavaScript lors de l'exploration ?

Vérifiez d'abord s'il existe une API sous-jacente en inspectant l'onglet Réseau du navigateur pour les requêtes XHR/Fetch. Si les données ne sont disponibles qu'après le rendu côté client, utilisez un navigateur headless comme Playwright ou Puppeteer pour exécuter le JavaScript et extraire le DOM entièrement rendu. Pour le crawling JS à haut volume, un service de rendu géré peut prendre en charge l'orchestration des navigateurs, vous évitant ainsi d'avoir à gérer cette infrastructure vous-même.

Comment puis-je empêcher mon robot d'indexation Python d'être bloqué ?

Faites tourner les chaînes User-Agent, utilisez des proxys résidentiels pour répartir les requêtes sur plusieurs adresses IP, ajoutez des délais aléatoires entre les requêtes et respectez les directives de délai de crawl du fichier robots.txt. Surveillez attentivement les codes de réponse : une augmentation soudaine des réponses 403 ou 429 signifie que le site a détecté votre profil de trafic. Prendre du recul et réduire la concurrence est presque toujours plus efficace que d'essayer de forcer le passage à travers les blocages par la force brute.

Quand dois-je utiliser Scrapy plutôt que requests et BeautifulSoup ?

Utilisez Scrapy lorsque votre exploration porte sur plus de quelques centaines de pages, nécessite des requêtes simultanées, requiert une déduplication intégrée ou tire parti de pipelines de données structurés et de l'exportation. Pour des scripts rapides et ponctuels ciblant un petit nombre de pages, requests et BeautifulSoup sont plus rapides à configurer et plus simples à déboguer. Si votre projet dépasse le cadre d'un simple fichier de script, l'architecture de Scrapy vous évitera de devoir réinventer ses fonctionnalités.

Conclusion

La création d'un robot d'indexation web en Python est un processus progressif, et non une étape unique. Vous commencez par quelques lignes utilisant requests et BeautifulSoup pour comprendre la boucle « récupération-analyse-extraction ». À partir de là, vous passez à Scrapy pour bénéficier de la concurrence, de la déduplication automatique, de la flexibilité des sélecteurs et du nettoyage des données basé sur des pipelines sans avoir à écrire vous-même cette infrastructure.

Les principes fondamentaux restent les mêmes quelle que soit l'échelle : respectez les sites que vous explorez, dédupliquez de manière agressive, gérez les erreurs avec élégance et nettoyez vos données avant de les stocker. Lorsque les sites cibles ripostent avec des CAPTCHA, des blocages d'IP ou un rendu exclusivement en JavaScript, vous disposez d'un arbre de décision clair : vérifiez d'abord s'il existe une API sous-jacente, utilisez des navigateurs sans interface graphique pour les volumes modérés et appuyez-vous sur des services gérés pour les tâches lourdes.

Si vous constatez que vous passez plus de temps à gérer la rotation des proxys, à résoudre des CAPTCHA et à contourner les mesures anti-bot qu'au traitement proprement dit des données, WebScrapingAPI peut prendre en charge cette couche d'infrastructure à votre place. Il gère les proxys, le rendu JavaScript et les tentatives de reconnexion derrière un point de terminaison unique, afin que vos robots Scrapy ou vos scripts BeautifulSoup continuent de fonctionner avec un minimum de modifications du code. Ainsi, vous restez concentré sur ce que les données vous révèlent, et non sur la manière de les obtenir.

À propos de l'auteur
Suciu Dan, cofondateur @ WebScrapingAPI
Suciu Dancofondateur

Suciu Dan est le cofondateur de WebScrapingAPI et rédige des guides pratiques destinés aux développeurs sur le web scraping avec Python et Ruby, ainsi que sur les infrastructures de proxy.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.