Python est le langage de référence pour le scraping web, et pour de bonnes raisons : quelques lignes de requests et BeautifulSoup transforment une page web en données structurées que vous pouvez sauvegarder, interroger et analyser. Si vous savez lire du HTML et écrire une boucle, vous pouvez construire un scraper fonctionnel dès aujourd'hui.
Ce guide vous montre comment scraper un site web avec Python de bout en bout. Vous installez la pile standard, récupérez une page, analysez-la, sélectionnez les éléments souhaités, extrayez des champs propres, parcourez la pagination et écrivez les résultats en CSV. Nous utilisons un site d'entraînement public pour que chaque extrait fonctionne réellement. Vient ensuite la partie honnête : requests simple échoue sur les pages rendues en JavaScript et se fait bloquer à grande échelle, vous verrez donc comment la Crawling API résout les deux problèmes en un seul appel.
Ce que vous allez construire
Un petit scraper Python qui lit une liste paginée de citations depuis un site d'entraînement public, extrait le texte, l'auteur et les tags de chaque citation, suit le lien "suivant" jusqu'à la fin des pages et sauvegarde tout dans un fichier CSV. Le même schéma, récupérer puis analyser puis boucler puis stocker, est le squelette de presque tous les scrapers que vous écrirez jamais.
Nous ciblons quotes.toscrape.com, un site conçu spécialement pour apprendre le scraping. Il est statique, bien structuré et utilisable librement, ce qui vous permet de vous concentrer sur la technique sans combattre des blocages dès le premier essai.
Prérequis
Vous n'avez pas besoin de grand chose pour commencer.
Bases de Python. Vous devez être à l'aise pour exécuter un script et installer des paquets avec pip. Les boucles, fonctions et dictionnaires suffisent.
Python 3.8 ou supérieur. Vérifiez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org.
C'est tout pour la première moitié du tutoriel. Les deux bibliothèques dont vous avez besoin s'installent en une seule commande, que nous abordons ensuite.
Configurer le projet
Créez un environnement virtuel pour que les dépendances du projet restent isolées du reste de votre système, puis installez les deux bibliothèques qui font le travail.
python --version python -m venv scraper_env source scraper_env/bin/activate pip install requests beautifulsoup4
Sur Windows, activez l'environnement avec scraper_env\Scripts\activate plutôt que la ligne source. Deux dépendances portent le tutoriel : requests récupère la page via HTTP, et beautifulsoup4 analyse le HTML retourné pour que vous puissiez extraire des éléments par balise et classe CSS.
Étape 1 : Récupérer une page
Tout scraping commence par une seule requête HTTP. Envoyez un GET à l'URL, vérifiez que le code de statut est 200 avant de faire quoi que ce soit d'autre, et vous avez le HTML de la page en main.
import requests url = "https://quotes.toscrape.com/page/1/" headers = {"User-Agent": "Mozilla/5.0 (scraper tutorial)"} response = requests.get(url, headers=headers, timeout=10) if response.status_code == 200: print(response.text[:500]) else: print(f"Request failed: {response.status_code}")
Deux petites habitudes portent immédiatement leurs fruits. Un en-tête User-Agent fait ressembler votre requête à un navigateur plutôt qu'à un script anonyme, ce que de nombreux sites préfèrent. Un timeout empêche votre scraper de rester bloqué indéfiniment quand un serveur stagne. Exécutez ceci et vous devriez voir les 500 premiers caractères du vrai HTML s'afficher dans votre terminal. Cela confirme que la récupération fonctionne avant d'écrire un seul sélecteur.
Étape 2 : Analyser le HTML avec BeautifulSoup
Le HTML brut n'est qu'une chaîne de caractères. Pour sélectionner des éléments, vous le chargez dans BeautifulSoup, qui transforme le balisage en un arbre que vous pouvez interroger par nom de balise et classe CSS. Ouvrez la page dans votre navigateur, faites un clic droit sur une citation et choisissez Inspecter pour voir la structure : sur ce site, chaque citation se trouve dans un div.quote, avec le texte dans span.text, l'auteur dans small.author et les tags dans a.tag.
from bs4 import BeautifulSoup soup = BeautifulSoup(response.text, "html.parser") quotes = soup.select("div.quote") print(f"Found {len(quotes)} quotes on this page")
L'argument html.parser indique à BeautifulSoup quel moteur utiliser ; il est livré avec Python et ne nécessite pas d'installation supplémentaire. La méthode select prend un sélecteur CSS et renvoie chaque élément correspondant sous forme de liste, de sorte que div.quote vous donne les dix blocs de citation sur la page. Si vous préférez find et find_all, ils font le même travail avec un style d'appel de méthode plutôt que des sélecteurs. Pour une visite plus approfondie des deux, consultez comment utiliser BeautifulSoup en Python.
Étape 3 : Extraire les champs
Extrayez maintenant les données de chaque bloc de citation. Parcourez les éléments, lisez le texte de chaque élément enfant et collectez un dictionnaire propre par citation. Envelopper les sélecteurs dans un petit helper évite qu'un champ manquant fasse planter toute l'exécution.
def text_of(element, selector): el = element.select_one(selector) return el.get_text(strip=True) if el else None def parse_quotes(soup): rows = [] for quote in soup.select("div.quote"): tags = [t.get_text(strip=True) for t in quote.select("a.tag")] rows.append({ "text": text_of(quote, "span.text"), "author": text_of(quote, "small.author"), "tags": ", ".join(tags), }) return rows
Le helper text_of fait deux choses utiles à la fois : il interroge un seul élément et retourne None quand l'élément est manquant, au lieu de lever une exception sur un appel .get_text() sur rien. Les tags nécessitent une compréhension de liste car il y en a plusieurs par citation, et les joindre en une seule chaîne maintient chaque ligne plate et compatible CSV. Appelez parse_quotes(soup) et vous obtenez une liste ordonnée de dictionnaires, un par citation.
Étape 4 : Suivre la pagination
Une page est une démonstration ; la vraie liste s'étend sur de nombreuses pages. Ce site relie la page suivante avec un élément li.next a, et quand il disparaît vous avez atteint la fin. La boucle est donc simple : récupérez la page courante, analysez-la, trouvez le lien suivant et répétez jusqu'à ce qu'il n'y ait plus de lien suivant.
import time BASE = "https://quotes.toscrape.com" def scrape_all(): all_rows = [] next_url = f"{BASE}/page/1/" while next_url: response = requests.get(next_url, headers=headers, timeout=10) if response.status_code != 200: print(f"Stopped at {next_url}: {response.status_code}") break soup = BeautifulSoup(response.text, "html.parser") all_rows.extend(parse_quotes(soup)) next_link = soup.select_one("li.next a") next_url = BASE + next_link["href"] if next_link else None time.sleep(1) return all_rows
La boucle while next_url s'exécute jusqu'à ce que le sélecteur du lien suivant ne retourne rien, à ce moment next_url devient None et la boucle se termine naturellement. Le href sur le site est relatif, ajoutez donc l'URL de base pour le rendre absolu. Le time.sleep(1) entre les pages n'est pas une politesse optionnelle sur une vraie cible : cadencer vos requêtes est le moyen le plus simple de rester sous les limites de débit d'un site.
Étape 5 : Sauvegarder en CSV
Les données qui ne vivent qu'en mémoire disparaissent à la fin du script. Écrivez-les dans un fichier CSV pour pouvoir les ouvrir dans un tableur, les charger dans pandas ou les transmettre à ce qui vient ensuite. Le module csv intégré à Python gère cela sans dépendances supplémentaires.
import csv def save_csv(rows, filename="quotes.csv"): if not rows: return with open(filename, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=rows[0].keys()) writer.writeheader() writer.writerows(rows) if __name__ == "__main__": data = scrape_all() save_csv(data) print(f"Saved {len(data)} quotes to quotes.csv")
DictWriter associe les clés de chaque dictionnaire aux colonnes CSV, de sorte que la ligne d'en-tête s'écrit elle-même depuis les noms de champs que vous avez déjà choisis. L'argument newline="" empêche les lignes vides entre les lignes sur Windows, et encoding="utf-8" conserve intacts les guillemets et les noms d'auteurs accentués. Exécutez le script et vous avez un CSV complet de toutes les citations sur toutes les pages. C'est un scraper complet et fonctionnel.
Là où plain requests cesse de fonctionner
Le site d'entraînement ci-dessus est statique, ce qui est exactement pourquoi c'est une bonne première cible. Les sites réels sont rarement aussi cléments. Deux problèmes apparaissent dès que vous pointez ce code sur une cible sérieuse, et aucun n'est résolvable en modifiant les sélecteurs.
Pages rendues en JavaScript
De nombreux sites modernes envoient une coquille HTML presque vide et construisent le contenu visible dans le navigateur avec JavaScript. requests ne récupère que cette coquille initiale ; il n'exécute aucun script. Donc quand vous analysez la réponse, vous ne trouvez aucune des données que vous avez vues dans votre navigateur, car ces données n'apparaissent qu'après l'exécution du JavaScript de la page. Un simple fetch ne peut tout simplement pas les voir. Pour le tableau complet de ce problème, consultez comment scraper des pages JavaScript avec Python.
Blocages à grande échelle
Le deuxième mur est la défense anti-bot. Les IP de datacenter, les schémas de requêtes répétitifs et le trafic qui ne ressemble pas à un vrai navigateur sont challengés avec des CAPTCHAs ou bloqués purement et simplement. Votre scraper peut fonctionner pendant dix requêtes puis commencer à retourner des 403 ou des pages vides. Ajouter des en-têtes et des pauses aide un peu, mais à tout volume réel vous avez besoin d'IP qui se lisent comme de vrais visiteurs, ce qu'une seule machine ne peut pas fournir. Le guide plus complet se trouve dans comment scraper des sites sans se faire bloquer.
La solution : rendre et pivoter en un seul appel
Vous pouvez résoudre les deux problèmes vous-même en exécutant un navigateur headless pour rendre JavaScript et en maintenant un pool de proxies résidentiels rotatifs pour les IP. Cela fonctionne, mais assembler ces pièces et les maintenir en bonne santé représente la majeure partie de l'effort d'ingénierie, et cela n'a rien à voir avec les données que vous voulez réellement.
La Crawling API regroupe les deux en une seule requête. Vous lui envoyez l'URL, elle rend la page dans un vrai navigateur derrière une IP rotative de confiance, et elle renvoie le HTML complet que vous pouvez analyser avec exactement le même code BeautifulSoup que vous avez déjà écrit. Installez le client officiel en plus des bibliothèques que vous avez.
pip install crawlbase
Voici l'avant et l'après. Le fetch ordinaire sur une page lourde en JavaScript renvoie une coquille ; l'appel à la Crawling API renvoie la page rendue. La couche d'analyse en dessous ne change pas du tout.
# Before: plain requests, breaks on JS pages and blocks response = requests.get(url, headers=headers, timeout=10) html = response.text # After: Crawling API renders the page behind a trusted IP from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) options = {"ajax_wait": "true", "page_wait": 5000} result = api.get(url, options) html = result["body"].decode("utf-8") if result["status_code"] == 200 else None # Same parser as before, unchanged soup = BeautifulSoup(html, "html.parser") rows = parse_quotes(soup)
Les deux options d'attente importent sur une cible rendue côté client. ajax_wait indique à l'API d'attendre que le contenu asynchrone finisse de se charger, et page_wait attend un nombre fixe de millisecondes après le chargement pour que les éléments tardifs apparaissent avant la capture. Utilisez le token JavaScript pour les sites qui rendent dans le navigateur ; pour les pages statiques, le token normal est plus rapide. L'important est que html s'écoule dans la même fonction parse_quotes que vous avez écrite à l'étape 3, donc adopter l'API est un remplacement d'une ligne, pas une réécriture.
Crawlbase propose deux types de tokens. Le token normal récupère du HTML statique, ce qui est tout ce dont vous avez besoin pour un site comme la page d'entraînement de citations. Le token JavaScript (JS) rend d'abord la page dans un vrai navigateur, ce dont vous avez besoin pour tout site qui construit son contenu côté client. Si vos champs analysés reviennent vides sur une vraie cible, passer au token JS est généralement la solution.
Plain requests échoue sur les pages JavaScript et se fait bloquer à grande échelle. La Crawling API rend la page dans un vrai navigateur, effectue une rotation via des IP résidentielles côté serveur et vous remet le HTML complet, de sorte que le code BeautifulSoup que vous avez déjà écrit continue de fonctionner sur des cibles qu'un simple fetch ne peut pas atteindre. Essayez-la avec l'offre gratuite avant de mettre en place votre propre flotte headless.
Bibliothèques Python de scraping utiles
La pile à deux bibliothèques gère la plupart des travaux statiques, mais quelques autres valent la peine d'être connues à mesure que vos besoins évoluent.
- requests est le client HTTP de référence pour récupérer des pages. Simple, fiable et la valeur par défaut correcte pour les cibles statiques.
- BeautifulSoup analyse HTML et XML en un arbre navigable. Il est tolérant avec le balisage imparfait, que les vraies pages ont toujours.
- Selenium pilote un vrai navigateur, il peut donc rendre JavaScript et interagir avec des pages en cliquant et tapant. Puissant, mais lourd à exécuter et lent à grande échelle.
- Scrapy est un framework de crawling complet avec concurrence intégrée, retries et pipelines. Privilégiez-le quand un script devient un vrai projet.
- pandas n'est pas un scraper, mais c'est là où les données scrapées atterrissent souvent pour le nettoyage, l'analyse et l'export vers d'autres formats.
Habitudes qui maintiennent un scraper en bonne santé
Un scraper qui fonctionne une fois est facile ; un qui continue de fonctionner demande quelques disciplines. Celles-ci s'appliquent que vous utilisiez plain requests ou une API gérée.
-
Cadencez vos requêtes. Un petit délai entre les requêtes, comme le
time.sleep(1)ci-dessus, vous maintient sous les limites de débit et hors des listes de blocage. Marteler un site en boucle serrée est la façon la plus rapide d'être limité. -
Gérez les erreurs. Les pages changent, les champs disparaissent et les serveurs ont des ratés. Vérifiez les codes de statut, protégez les sélecteurs contre
Noneet enveloppez l'analyse risquée pour qu'une mauvaise page ne tue pas toute l'exécution. - Attendez-vous à ce que le balisage évolue. Les noms de classes et les structures changent sans préavis. Quand un champ commence à revenir vide, réinspectez la page en direct et mettez à jour le sélecteur. Une maintenance périodique est normale, pas le signe d'un scraper défaillant.
-
Respectez la cible. Lisez le
robots.txtet les conditions du site, maintenez votre volume raisonnable et collectez uniquement des données publiques.
Points clés
- La boucle principale est récupérer, analyser, boucler, stocker. requests obtient le HTML, BeautifulSoup extrait les champs, la pagination parcourt les pages et le module csv sauvegarde le résultat.
- Inspectez avant de sélectionner. Ouvrez les outils de développement de la page pour trouver les balises et classes qui contiennent vos données, puis mappez chaque champ sur un sélecteur CSS.
-
Cadencez et protégez votre code. Ajoutez un délai entre les requêtes et retournez
Nonesur les éléments manquants pour qu'une mauvaise page ne fasse pas planter l'exécution. - Plain requests a deux limites. Il ne peut pas exécuter JavaScript et se fait bloquer à grande échelle, aucune des deux ne peut être résolue avec des sélecteurs.
- La Crawling API résout les deux en un seul appel. Elle rend la page derrière une IP rotative de confiance et renvoie le HTML complet, de sorte que votre parseur BeautifulSoup existant continue de fonctionner sans modification.
Foire aux questions
Ai-je besoin de requests et de BeautifulSoup ?
Pour un site statique typique, oui, et ils s'associent naturellement. requests récupère la page via HTTP et vous donne le HTML brut sous forme de chaîne ; BeautifulSoup transforme cette chaîne en un arbre que vous pouvez interroger par balise et classe CSS pour extraire des champs individuels. requests télécharge, BeautifulSoup extrait.
Pourquoi mes données scrapées sont-elles vides alors que la page a clairement du contenu ?
Presque toujours parce que le site rend son contenu avec JavaScript. requests ne récupère que la coquille HTML initiale et n'exécute pas les scripts, de sorte que les données que vous voyez dans votre navigateur ne sont pas présentes dans ce que vous analysez. Vous devez d'abord rendre la page, soit avec un navigateur headless soit avec le token JavaScript de la Crawling API, avant que BeautifulSoup puisse trouver les champs.
Comment scraper plusieurs pages ?
Trouvez le lien ou le schéma que le site utilise pour sa page suivante, puis bouclez. S'il y a un bouton "suivant", suivez son href jusqu'à ce qu'il disparaisse, comme illustré à l'étape 4. Si les URLs suivent un schéma numérique comme /page/2/, vous pouvez les construire dans une boucle range à la place. Dans tous les cas, ajoutez un court délai entre les pages pour rester poli et non bloqué.
Comment éviter d'être bloqué lors du scraping ?
Cadencez vos requêtes avec un délai, envoyez un en-tête User-Agent réaliste et variez vos cibles au lieu de marteler un chemin. À grande échelle, vous avez également besoin d'IP qui ressemblent à de vrais visiteurs, ce qu'une seule machine ne peut pas fournir. Router via des IP résidentielles rotatives, que ce soit via la Crawling API ou le Smart AI Proxy, est ce qui maintient les exécutions à fort volume sans déclencher les limites de débit.
Quand utiliser la Crawling API plutôt que plain requests ?
Utilisez plain requests pour les cibles statiques à faible volume où un simple fetch renvoie les données, comme le site d'entraînement de ce guide. Passez à la Crawling API quand la page est rendue en JavaScript, quand vous êtes bloqué ou challengé, ou quand vous devez scraper à un volume qu'une seule IP ne peut pas soutenir. Puisque l'API renvoie du HTML, votre parseur existant ne change pas.
Le scraping web avec Python est-il légal ?
Scraper des données publiques est généralement permis, mais cela dépend des conditions d'utilisation du site, de votre juridiction et de ce que vous faites avec les données. Vérifiez le robots.txt et les conditions du site avant de commencer, évitez les données personnelles couvertes par des lois sur la vie privée comme le RGPD, et ne scrapez jamais du contenu derrière un accès connecté. En cas de doute, collectez uniquement des données publiques et maintenez votre volume suffisamment bas pour ne pas solliciter le serveur.
Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.
Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.

