Scraper une page ne représente que la moitié du travail. Les enregistrements que vous récupérez doivent vivre quelque part de durable, quelque part accessible à vos coéquipiers et vos outils d'analyse, et quelque part où un plantage d'ordinateur portable ne peut pas tout effacer. Un CSV local convient pour une tâche ponctuelle, mais dès qu'un scrape devient un pipeline récurrent, un seul disque dur devient un risque : la capacité s'épuise, les transferts entre machines deviennent maladroits, et une panne de disque vous coûte un travail que vous ne pouvez pas récupérer.

Ce guide vous montre comment stocker des données scrapées sur le cloud avec Python de bout en bout. Vous construisez un flux simple et exécutable qui récupère une page via la Crawling API, structure les résultats en enregistrements propres, puis les écrit vers des destinations cloud durables : un object store utilisant des buckets de style S3 et une base de données relationnelle managée. Tout ici utilise une URL d'exemple neutre et des variables d'environnement pour les identifiants, vous pouvez donc l'adapter à votre propre cible et fournisseur sans changer la forme du code.

Ce que vous allez construire

Un script Python qui scrape un petit jeu de données depuis une page de liste d'exemple publique via la Crawling API, normalise chaque ligne en un enregistrement typé, et envoie ces enregistrements vers le cloud de deux façons. Vous pouvez garder un chemin ou les deux. Les éléments sont :

  • Scraper une page rendue récupérée via la Crawling API, renvoyant du HTML terminé.
  • Transformer le HTML brut en une liste d'enregistrements structurés avec des noms de champs et des types cohérents.
  • Object storage : un fichier JSON Lines uploadé vers un bucket de style S3 pour l'archivage bon marché et durable.
  • Base de données managée : les mêmes enregistrements insérés dans une table Postgres pour les requêtes et les jointures.
  • Crawlbase Cloud Storage : un chemin à un seul paramètre qui sauvegarde la réponse de crawl brute côté serveur.

Pourquoi stocker des données scrapées dans le cloud

Le stockage local est pratique jusqu'à ce qu'il ne le soit plus. Quand un scrape passe de quelques centaines de lignes à une tâche récurrente alimentant un tableau de bord, trois problèmes surgissent en même temps. La capacité devient un coût récurrent : vous achetez des disques pour garder les sauvegardes en sécurité et passez du temps à les gérer. L'accès devient difficile : les données piégées sur une machine sont difficiles à partager avec une équipe ou à alimenter dans un outil qui s'exécute ailleurs. Et la durabilité est fragile : des problèmes d'alimentation, une corruption de firmware et la simple erreur humaine peuvent faire tomber un seul disque, et avec lui tout travail qui n'a pas été copié ailleurs.

Le stockage cloud répond aux trois problèmes. Les object stores et les bases de données managées sont conçus pour la redondance, vos données sont donc répliquées sur plusieurs emplacements plutôt que de rester sur un seul disque. Ils évoluent sans que vous provisionniez du matériel, ils sont accessibles de partout avec des identifiants, et ils délèguent la sauvegarde et la durabilité au fournisseur. Pour un pipeline de scraping, cela signifie que vous pouvez traiter les données collectées comme un actif durable dès qu'elles arrivent, pas quelque chose que vous devez surveiller sur un disque local.

Deux destinations, et quand utiliser chacune

Ce tutoriel écrit vers deux types de stockage cloud car les données scrapées en veulent généralement les deux. L'object storage (buckets compatibles S3) est le bon endroit pour les données brutes et d'archivage : bon marché par gigaoctet, indifférent à la forme de ce que vous y mettez, et idéal pour conserver la sortie du scrape intacte afin de pouvoir la retraiter plus tard. Une base de données relationnelle managée (Postgres ici) est le bon endroit pour la copie structurée et interrogeable, où des colonnes et types cohérents vous permettent de filtrer, agréger et joindre avec SQL. Le schéma courant est d'écrire vers les deux, et le code ci-dessous fait exactement cela. Pour une comparaison approfondie, voir stockage cloud versus stockage local et les avantages du stockage cloud.

Prérequis

Quelques éléments doivent être en place avant d'écrire du code. Aucun ne prend longtemps.

Python 3.8 ou version ultérieure. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda, et assurez-vous que Python est dans votre PATH.

Un compte Crawlbase et un token. Inscrivez-vous, ouvrez votre tableau de bord, et copiez votre token. Crawlbase inclut jusqu'à 20 000 requêtes gratuites pour commencer, ce qui est largement suffisant pour suivre ce guide. Traitez le token comme un mot de passe et gardez-le hors du contrôle de version. Si votre cible rend le contenu côté client, utilisez le token JavaScript ; pour une page statique, le token normal convient.

Identifiants cloud. Pour le chemin object storage vous avez besoin d'un bucket de style S3 et d'une paire de clés d'accès. Pour le chemin base de données vous avez besoin d'une chaîne de connexion vers une instance Postgres managée. Les deux sont fournis via des variables d'environnement dans le code ci-dessous, jamais codés en dur.

Familiarité avec Python et le scraping basique. Si le côté analyse est nouveau pour vous, le guide BeautifulSoup et le tutoriel scraping avec Python sont de bons compagnons.

Configurer le projet

Créez un environnement virtuel pour que les dépendances restent isolées, puis installez les bibliothèques dont le flux a besoin.

bash
python --version

python -m venv cloud_env
source cloud_env/bin/activate

pip install crawlbase beautifulsoup4 boto3 psycopg2-binary

Sur Windows, activez l'environnement avec cloud_env\Scripts\activate à la place de la ligne source. Quatre dépendances font le travail : crawlbase est le client officiel pour la Crawling API, beautifulsoup4 analyse le HTML renvoyé, boto3 communique avec l'object storage de style S3, et psycopg2-binary se connecte à Postgres. Le module json est inclus dans la bibliothèque standard, donc le format d'archivage ne nécessite rien de plus.

Étape 1 : Scraper une page via la Crawling API

Commencez par récupérer une page terminée. Importez la classe CrawlingAPI, initialisez-la avec votre token, et demandez l'URL cible. Vérifier le cb_status (legacy pc_status) Crawlbase avant d'analyser maintient les échecs visibles plutôt que silencieux. Nous utilisons ici une page de liste d'exemple neutre ; remplacez par votre propre URL quand vous adaptez le flux.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    response = api.get(page_url)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['headers']['cb_status']}")
    return None

if __name__ == "__main__":
    page_url = "https://example.com/products"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

Exécutez cela avec python cloud_pipeline.py et vous devriez voir le vrai balisage de la page affiché, confirmant que la récupération fonctionne avant d'écrire un seul sélecteur. Si votre cible remplit le contenu côté client, initialisez le client avec votre token JavaScript et passez {"ajax_wait": "true", "page_wait": 5000} à api.get pour que l'API rende la page en premier. Pour les cibles lourdes en JS, le guide scraping de pages JavaScript avec Python couvre les détails.

Crawlbase Crawling API

Cet unique appel api.get ci-dessus fait plus qu'une requête ordinaire. La Crawling API rend la page quand vous passez un token JavaScript, fait tourner des adresses IP résidentielles côté serveur, et gère les CAPTCHAs, de sorte que vous récupérez du HTML terminé sans faire tourner une flotte de navigateurs headless ou un pool de proxies vous-même. Pointez-la vers une page publique sur le niveau gratuit d'abord, puis montez en charge avec le même code.

Étape 2 : Transformer le HTML en enregistrements structurés

Le HTML brut n'est pas quelque chose que vous voulez stocker directement dans une base de données. L'étape de transformation le convertit en une liste de dictionnaires avec des noms de champs et des types cohérents, afin que chaque enregistrement ait la même forme. Chargez le HTML dans BeautifulSoup, parcourez chaque élément de la page, et extrayez les champs qui vous intéressent. Les sélecteurs ici sont illustratifs ; remplacez-les par ceux qui correspondent à votre cible.

python
from bs4 import BeautifulSoup

def text_of(node, selector):
    el = node.select_one(selector)
    return el.get_text(strip=True) if el else None

def to_price(raw):
    if not raw:
        return None
    digits = raw.replace("$", "").replace(",", "").strip()
    return float(digits) if digits.replace(".", "").isdigit() else None

def transform(html):
    soup = BeautifulSoup(html, "html.parser")
    records = []
    for card in soup.select("div.product"):
        records.append({
            "name": text_of(card, "h2.title"),
            "price": to_price(text_of(card, "span.price")),
            "sku": text_of(card, "span.sku"),
            "in_stock": text_of(card, "span.stock") == "In stock",
        })
    return [r for r in records if r["name"]]

Trois petits helpers maintiennent les enregistrements propres. text_of renvoie le texte épuré d'un élément ou None quand il est absent, afin qu'une lacune dans une carte ne plante pas la boucle. to_price retire le symbole monétaire et les séparateurs de milliers et caste en float, afin que la colonne de base de données puisse être numérique plutôt que textuelle. Le filtre final supprime les lignes sans nom, qui sont généralement des artefacts de mise en page plutôt que de vrais éléments. Le résultat est une liste d'enregistrements typés prêts à stocker. Pour plus d'informations sur la mise en forme correcte des données scrapées, voir structurer et nettoyer les données web scrapées.

Étape 3 : Uploader vers l'object storage de style S3

La première destination cloud est un object store. L'object storage est le foyer naturel pour les données brutes ou d'archivage : bon marché, durable, et indifférent à la forme de ce que vous y mettez. Nous écrivons les enregistrements en JSON Lines (un objet JSON par ligne), ce qui est facile à compléter et à lire en streaming plus tard. Les identifiants viennent de variables d'environnement pour qu'aucune information sensible n'atterrisse dans la source.

python
import os
import json
import boto3

def upload_to_s3(records, key):
    s3 = boto3.client(
        "s3",
        endpoint_url=os.environ.get("S3_ENDPOINT_URL"),
        aws_access_key_id=os.environ["S3_ACCESS_KEY"],
        aws_secret_access_key=os.environ["S3_SECRET_KEY"],
    )
    body = "\n".join(json.dumps(r) for r in records)
    s3.put_object(
        Bucket=os.environ["S3_BUCKET"],
        Key=key,
        Body=body.encode("utf-8"),
        ContentType="application/x-ndjson",
    )
    print(f"Uploaded {len(records)} records to s3://{os.environ['S3_BUCKET']}/{key}")

Le paramètre endpoint_url est ce qui rend cela de style S3 plutôt qu'uniquement AWS : laissez-le non défini pour AWS S3, ou pointez-le vers n'importe quel fournisseur compatible S3 (par exemple une instance MinIO auto-hébergée ou l'object store d'un autre cloud). Définissez les quatre variables d'environnement avant d'exécuter, par exemple export S3_BUCKET=my-scrape-archive et les clés correspondantes. Le Key est le chemin d'objet dans le bucket ; une clé avec horodatage de date comme scrapes/2026-06-11/products.jsonl garde les exécutions successives séparées et faciles à retrouver.

Gardez les identifiants hors du code

Ne codez jamais en dur les clés d'accès ou les chaînes de connexion dans un script que vous commitez. Lisez-les depuis des variables d'environnement ou un gestionnaire de secrets, comme le fait le code ici. Une clé commitée dans le contrôle de version est une clé que vous devez faire pivoter.

Étape 4 : Insérer dans une base de données managée

La deuxième destination est une base de données Postgres managée, là où vit la copie structurée pour les requêtes. La fonction ci-dessous ouvre une connexion depuis une seule variable d'environnement, s'assure que la table cible existe, et insère les enregistrements. L'utilisation de requêtes paramétrées (les espaces réservés %s) garde les valeurs correctement échappées au lieu d'être concaténées dans le SQL.

python
import os
import psycopg2
from psycopg2.extras import execute_values

CREATE = """
CREATE TABLE IF NOT EXISTS products (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    price NUMERIC,
    sku TEXT,
    in_stock BOOLEAN,
    scraped_at TIMESTAMPTZ DEFAULT now()
)
"""

def save_to_db(records):
    conn = psycopg2.connect(os.environ["DATABASE_URL"])
    with conn, conn.cursor() as cur:
        cur.execute(CREATE)
        rows = [(r["name"], r["price"], r["sku"], r["in_stock"]) for r in records]
        execute_values(
            cur,
            "INSERT INTO products (name, price, sku, in_stock) VALUES %s",
            rows,
        )
    conn.close()
    print(f"Inserted {len(records)} rows into products")

Définissez DATABASE_URL sur votre chaîne de connexion Postgres managée, par exemple postgresql://user:pass@host:5432/dbname, et gardez-la dans l'environnement plutôt que dans le fichier. Le CREATE TABLE IF NOT EXISTS rend la fonction sûre à exécuter plusieurs fois, la colonne scraped_at horodate chaque chargement afin que vous puissiez suivre les changements dans le temps, et execute_values regroupe les insertions en un seul aller-retour au lieu d'une requête par ligne. Une fois les lignes en place, vous pouvez filtrer et agréger avec du SQL simple, puis les charger dans pandas pour l'analyse.

Étape 5 : Assembler le pipeline complet

Maintenant reliez les étapes en un seul script exécutable : scraper, transformer, puis envoyer les enregistrements vers les deux destinations. Gardez l'appel de stockage qui convient à votre flux de travail ; les deux sont montrés ici.

python
import os
import json
from datetime import date
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

# crawl, transform, upload_to_s3 and save_to_db are defined above

def main():
    page_url = "https://example.com/products"
    html = crawl(page_url)
    if not html:
        print("Nothing scraped, stopping.")
        return

    records = transform(html)
    print(f"Parsed {len(records)} records")
    if not records:
        return

    key = f"scrapes/{date.today().isoformat()}/products.jsonl"
    upload_to_s3(records, key)
    save_to_db(records)

if __name__ == "__main__":
    main()

Le flux est linéaire et facile à raisonner : récupérer la page, sortir tôt si le scrape a échoué, transformer le HTML en enregistrements, sortir à nouveau s'il n'y a rien à stocker, puis archiver les enregistrements dans le bucket et les charger dans la base de données. La clé d'objet horodatée garde l'archive de chaque exécution séparée, tandis que la base de données accumule chaque chargement avec un timestamp. Exécutez avec python cloud_pipeline.py une fois vos variables d'environnement définies.

À quoi ressemble la sortie

Le chemin object storage écrit un fichier JSON Lines, un enregistrement par ligne, qui atterrit dans le bucket :

json
{"name": "Aluminium Tripod", "price": 129.99, "sku": "TRP-014", "in_stock": true}
{"name": "USB-C Hub", "price": 39.5, "sku": "HUB-203", "in_stock": false}
{"name": "Wireless Mouse", "price": 24.0, "sku": "MSE-088", "in_stock": true}

Le chemin base de données stocke les mêmes enregistrements en colonnes typées, donc une requête rapide confirme le chargement et montre la forme que vous pouvez analyser :

sql
SELECT name, price, in_stock FROM products WHERE in_stock = true ORDER BY price;

--      name       | price  | in_stock
-- ----------------+--------+----------
--  Wireless Mouse |  24.00 | t
--  Aluminium Tripod| 129.99 | t

Avec les deux copies en place, vous disposez d'une archive bon marché et durable des enregistrements bruts et d'une table structurée interrogeable, écrits par la même exécution.

Un raccourci à un paramètre : Crawlbase Cloud Storage

Si votre objectif est simplement de conserver une copie côté serveur de chaque réponse de crawl sans créer votre propre bucket ou base de données d'abord, Crawlbase Cloud Storage offre un chemin à un seul paramètre. Ajoutez &store=true à une requête Crawling API et une copie de la réponse est sauvegardée automatiquement sur le cloud, où vous pouvez la rechercher, la récupérer ou la supprimer plus tard via l'API ou votre tableau de bord.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

response = api.get("https://example.com/products", {"store": "true"})
# the response also includes a storage RID you can use to fetch it later
print(response["headers"].get("storage_url"))

Chaque requête sauvegardée reçoit un identifiant unique (un RID) que vous pouvez utiliser pour la consulter ou la supprimer. Ce chemin est le moyen le plus rapide de conserver les réponses brutes, et il s'associe bien au Crawler asynchrone quand vous faites tourner de nombreuses requêtes et voulez que le stockage soit géré côté serveur. Pour les jeux de données structurés plus importants, vous aurez toujours besoin de votre propre base de données, mais pour l'archivage des réponses brutes, le paramètre store est difficile à battre en simplicité.

Faire évoluer le pipeline

Le flux ci-dessus scrape une page. Le transformer en tâche récurrente concerne principalement le rythme et la résilience. Quelques habitudes maintiennent une exécution plus grande en bonne santé :

  • Regroupez vos écritures. Accumulez les enregistrements et uploadez ou insérez-les par lots plutôt qu'une ligne à la fois. execute_values regroupe déjà les insertions en base de données ; faites de même pour les uploads objets en écrivant un fichier par exécution plutôt que par enregistrement.
  • Horodatez vos clés. Utilisez une clé d'objet datée comme scrapes/2026-06-11/products.jsonl pour que chaque exécution soit isolée et que vous n'écrasisez jamais l'historique. La colonne scraped_at de la base de données joue le même rôle côté requête.
  • Exécutez selon un calendrier. Enveloppez le script dans un cron job ou une tâche planifiée pour que la copie cloud reste à jour. Parce que la table utilise IF NOT EXISTS et que la clé de bucket est datée, les exécutions répétées sont sûres.
  • Déléguez la récupération à grande échelle. Pour de nombreuses pages, le Crawler asynchrone met les requêtes en file d'attente et livre les résultats à un webhook, ce qui convient aux volumes élevés sans maintenir des connexions ouvertes.

Scraper de façon responsable

Collectez de façon responsable. Scrapez uniquement des données publiques, respectez les conditions d'utilisation de chaque site et son robots.txt, et maintenez votre taux de requêtes raisonnable pour ne pas surcharger les serveurs dont vous dépendez. Quand les données que vous collectez incluent quoi que ce soit lié à des individus identifiables, les lois sur la vie privée telles que le RGPD et le CCPA s'appliquent, alors évitez les données personnelles sauf si vous avez une base légale et un objectif clair pour les conserver. Stocker des données dans le cloud ne change rien à cela : le même soin que vous prenez pour les collecter s'étend à la façon dont vous les conservez, et garder des données personnelles plus longtemps que nécessaire n'ajoute que du risque.

Récapitulatif

Points clés

  • Le stockage fait partie du pipeline. Traitez les enregistrements scrapés comme un actif durable dès qu'ils arrivent ; un CSV local convient pour une tâche ponctuelle mais pas pour une tâche récurrente.
  • Transformez avant de stocker. Normalisez le HTML brut en enregistrements typés avec des noms de champs cohérents pour que la colonne de base de données puisse être numérique et que l'archive reste cohérente.
  • Utilisez le bon store pour la tâche. L'object storage (buckets de style S3) est bon marché et durable pour les données brutes ou d'archivage ; une base de données managée est pour la copie structurée et interrogeable, et écrire vers les deux est un schéma courant.
  • Gardez les identifiants hors du code. Lisez les clés d'accès et les chaînes de connexion depuis des variables d'environnement ou un gestionnaire de secrets, jamais codés en dur dans un script commité.
  • Le paramètre store est le raccourci. Ajouter &store=true sauvegarde une réponse de crawl brute sur Crawlbase Cloud Storage en un paramètre, ce qui est le moyen le plus rapide de conserver des réponses sans créer votre propre infrastructure d'abord.

Foire aux questions

Dois-je stocker les données scrapées dans l'object storage ou une base de données ?

Cela dépend de ce que vous en faites. L'object storage (buckets compatibles S3) est bon marché, durable, et idéal pour les données brutes ou d'archivage de toute forme, c'est donc le bon endroit pour la sortie du scrape intacte. Une base de données relationnelle managée est pour la copie structurée que vous interrogez, filtrez et joignez avec SQL. De nombreux pipelines écrivent vers les deux : archiver les enregistrements bruts dans un bucket et charger les enregistrements nettoyés dans une base de données.

Comment garder mes identifiants cloud hors du code ?

Lisez-les depuis des variables d'environnement ou un gestionnaire de secrets plutôt que de les coder en dur. Le code de ce guide extrait les clés S3 et la chaîne de connexion Postgres depuis os.environ, pour qu'aucune information sensible ne vive dans le fichier commité. Une clé commitée dans le contrôle de version est une clé que vous devez faire pivoter, alors gardez-les dans l'environnement.

Quelle est la différence entre Crawlbase Cloud Storage et l'upload vers mon propre bucket ?

Crawlbase Cloud Storage est un chemin à un seul paramètre : ajoutez &store=true à une requête Crawling API et la réponse brute est sauvegardée côté serveur, récupérable par un RID, sans infrastructure à configurer vous-même. Uploader vers votre propre bucket ou base de données vous donne un contrôle total sur le format, le schéma, la rétention et l'emplacement, ce que vous voulez pour les jeux de données structurés. Les deux sont complémentaires : le paramètre store pour l'archivage rapide des réponses brutes, vos propres stores pour les données traitées.

Le code S3 fonctionnera-t-il avec des fournisseurs autres qu'AWS ?

Oui. Le client boto3 prend un paramètre endpoint_url ; laissez-le non défini pour AWS S3, ou pointez-le vers n'importe quel fournisseur compatible S3 comme une instance MinIO auto-hébergée ou l'object store d'un autre cloud. Le reste du code est inchangé, c'est pourquoi l'exemple lit l'endpoint depuis une variable d'environnement.

Comment exécuter cela selon un calendrier pour que la copie cloud reste à jour ?

Enveloppez le script dans un cron job ou une tâche planifiée qui s'exécute à la cadence à laquelle vos données changent. Le pipeline est sûr à répéter : la table de base de données utilise CREATE TABLE IF NOT EXISTS, la clé d'objet est horodatée pour que les exécutions ne s'écrasent jamais, et chaque ligne de base de données porte un timestamp scraped_at pour suivre les changements dans le temps. Pour de nombreuses pages, déléguez la récupération au Crawler asynchrone pour que la tâche ne soit pas bloquée sur une connexion.

Est-il sûr de stocker des données personnelles scrapées dans le cloud ?

Traitez cela d'abord comme une question légale et de vie privée. Évitez de collecter des données liées à des individus identifiables sauf si vous avez une base légale et un objectif clair, car les lois sur la vie privée comme le RGPD et le CCPA s'appliquent quelle que soit l'endroit où les données sont stockées. Si vous conservez des données personnelles, stockez uniquement ce dont vous avez besoin, conservez-les le moins longtemps que nécessaire, et sécurisez l'accès. Garder des données personnelles plus longtemps que nécessaire n'ajoute que du risque sans ajouter de valeur.

Commencer à construire

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.

En libre-service · Sans appel commercial requis · Volumes de crawl entreprise disponibles