Reddit est l'une des plus grandes archives de discussions publiques sur le web, et les listes publiques d'un subreddit sont un signal utile pour la recherche : quels sujets sont en tendance, comment une communauté classe les liens, quelles sources externes sont partagées, et comment les scores et les nombres de commentaires évoluent dans le temps. Ce guide vous montre comment scraper des données publiques Reddit avec Python à l'aide de la Crawlbase Crawling API, l'ensemble du guide étant limité aux seules pages de listes publiques.

Pour être clair d'emblée : tout ce qui suit reste sur des données publiques et agrégées issues de subreddits publics et de listes de sujets. Cela signifie les titres des publications, les scores et le nombre de votes positifs, le nombre de commentaires, le subreddit auquel appartient une publication et le lien vers lequel chaque publication pointe. Cela ne couvre rien de ce qui se trouve derrière une connexion, ni les subreddits privés, ni les messages directs, ni les données personnelles d'utilisateurs individuels. Les conditions d'utilisation de Reddit restreignent l'accès automatisé, alors lisez la section sur la légalité vers la fin avant de pointer ceci sur quoi que ce soit de réel, et préférez l'API officielle Reddit pour tout usage en production.

Ce que vous allez construire

Un petit script Python qui prend l'URL d'une liste publique de subreddit ou de sujet, récupère la page entièrement rendue via la Crawling API avec un token JavaScript, et analyse une poignée de champs publics pour chaque publication de la liste :

  • Titre le texte du titre public de chaque publication.
  • Score / votes positifs le nombre de votes agrégé qu'affiche une publication.
  • Nombre de commentaires combien de commentaires la publication compte, sous forme de nombre.
  • Subreddit la communauté à laquelle appartient la publication (par exemple r/technology).
  • Lien le permalien ou l'URL sortante vers laquelle la publication pointe.

Notez ce qui est délibérément absent : aucun nom d'utilisateur, aucun profil d'auteur, aucun texte de commentaire, aucun détail vote par vote. Ce sont des données personnelles d'individus, et leur collecte est volontairement hors du périmètre ici. Nous agrégeons au niveau de la publication et de la communauté, jamais de la personne.

Pourquoi une simple requête échoue sur Reddit

Demandez une URL de liste Reddit avec un client HTTP nu et vous obtiendrez généralement quelque chose de quasiment inutile : une coquille JavaScript, un bandeau de consentement aux cookies ou une page de défi. Le front-end actuel de Reddit rend les listes de publications côté client, si bien que les titres, les scores et les liens n'apparaissent qu'après l'exécution des scripts de la page dans un navigateur. En plus de cela, Reddit signale rapidement le trafic automatisé. Les plages d'IP de centres de données, l'absence de comportement de navigateur et les schémas de requêtes répétitifs sont limités en débit ou bloqués bien avant que la liste ne se charge.

Un scraper Reddit fonctionnel a donc besoin de deux choses dans la même requête : un vrai navigateur qui rend la page, et une adresse IP que Reddit interprète comme un visiteur ordinaire. Vous pouvez construire cela vous-même avec un navigateur headless et un pool de proxys résidentiels rotatifs, mais maintenir cette pile en bonne santé représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel. Vous lui envoyez une URL avec un token JavaScript, elle rend la page derrière une IP résidentielle de confiance, et elle renvoie un HTML fini que vous pouvez analyser. Si vous voulez le contexte approfondi, consultez notre guide sur comment crawler les sites JavaScript.

Pourquoi le token JS

Crawlbase propose deux types de token. Le token normal récupère le HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. Les listes Reddit sont rendues côté client, vous avez donc besoin du token JS ici. Le token normal renvoie la même coquille qu'une simple récupération, sans rien d'utile à en extraire.

Prérequis

Quelques éléments à mettre en place au préalable. Aucun ne prend longtemps.

Python de base. Vous devez être à l'aise pour lancer un script et installer des paquets avec pip. Si vous débutez dans l'analyse de HTML, notre introduction sur comment utiliser BeautifulSoup en Python couvre le volet extraction.

Python 3.8 ou ultérieur. Vérifiez avec python --version. Si vous ne l'avez pas, installez-le depuis python.org.

Un compte Crawlbase et un token JS. Inscrivez-vous, ouvrez votre dashboard et copiez votre token JavaScript (JS) depuis la page de documentation du compte. Traitez-le comme un mot de passe : il authentifie vos requêtes, alors gardez-le hors du contrôle de version. Le palier gratuit vous donne jusqu'à 20 000 requêtes pour tester.

Mettre en place le projet

Créez un environnement virtuel isolé, puis installez les deux bibliothèques dont le scraper a besoin.

bash
python --version

python -m venv reddit_env
source reddit_env/bin/activate

pip install crawlbase beautifulsoup4

Sur Windows, activez avec reddit_env\Scripts\activate au lieu de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel de la Crawling API, et beautifulsoup4 analyse le HTML renvoyé pour que vous puissiez extraire les champs individuels par sélecteur.

Étape 1 : Récupérer la liste rendue

Commencez par obtenir la page finie. Importez CrawlingAPI, initialisez-la avec votre token JS et demandez l'URL d'une liste publique. La version historique de ce tutoriel pointait vers une liste de sujets publique, https://www.reddit.com/t/technology/, qui est une bonne cible impersonnelle pour démarrer. Vérifiez le code de statut avant d'analyser pour que les échecs restent bruyants plutôt que silencieux.

python
from crawlbase import CrawlingAPI

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

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    listing_url = "https://www.reddit.com/t/technology/"
    html = crawl(listing_url)
    print(html[:500] if html else "No HTML returned")

Les deux options d'attente comptent pour une cible rendue côté client. ajax_wait indique à l'API d'attendre que le contenu asynchrone finisse de se charger, et page_wait patiente un nombre fixe de millisecondes après le chargement pour que les publications à rendu tardif apparaissent avant que la page ne soit capturée. Cinq secondes sont un point de départ raisonnable ; augmentez-le si la liste revient vide. Lancez le script et vous devriez voir un véritable balisage de liste, ce qui confirme que le rendu fonctionne avant d'écrire le moindre sélecteur.

Crawlbase Reddit Scraper

Cette liste ne s'est remplie que parce que la page a été rendue derrière une IP de confiance en un seul appel. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, fait tourner des IP résidentielles côté serveur et vous remet un HTML fini, ce qui vous évite d'exploiter une flotte headless et un pool de proxys vous-même. Pointez-la d'abord vers un subreddit public sur le palier gratuit.

Étape 2 : Analyser les champs publics des publications

Avec le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez les champs publics de chaque publication. Le balisage de liste de Reddit regroupe chaque publication dans un élément personnalisé shreddit-post, et les valeurs utiles résident dans les attributs de cet élément plutôt que dans des classes CSS profondément imbriquées et fréquemment renommées. Les attributs que vous voulez sont post-title, score, comment-count, subreddit-prefixed-name et permalink. Lire les attributs est bien plus durable que de courir après des widgets rendus.

python
from bs4 import BeautifulSoup

BASE = "https://www.reddit.com"

def to_int(value):
    try:
        return int(value)
    except (TypeError, ValueError):
        return None

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")
    posts = []
    for post in soup.select("shreddit-post"):
        permalink = post.get("permalink", "")
        link = f"{BASE}{permalink}" if permalink.startswith("/") else permalink
        posts.append({
            "title": post.get("post-title"),
            "score": to_int(post.get("score")),
            "comment_count": to_int(post.get("comment-count")),
            "subreddit": post.get("subreddit-prefixed-name"),
            "link": link,
        })
    return posts

Chaque publication devient un enregistrement plat de champs publics et agrégés. Les attributs score et comment-count reviennent sous forme de chaînes, donc to_int les convertit en nombres et renvoie None lorsqu'un attribut est manquant plutôt que de faire planter l'exécution. L'attribut permalink est un chemin relatif au site comme /r/technology/comments/<id>/<slug>/, alors nous le joignons à l'hôte de base pour obtenir un lien complet. Notez qu'il n'y a aucun champ auteur nulle part dans cet enregistrement, à dessein.

Les sélecteurs dérivent

Reddit modifie son balisage sans préavis, et c'est pourquoi ce code lit des attributs sur l'élément shreddit-post plutôt que des classes imbriquées fragiles. Si un champ revient à None, ré-inspectez la page en direct dans les outils de développement de votre navigateur et mettez à jour le nom de l'attribut. La maintenance périodique est normale pour tout scraper en production, et non le signe que quelque chose est cassé. Pour une remise à niveau sur le choix de sélecteurs résilients, consultez scraper des sites web sans se faire bloquer.

Étape 3 : Tout assembler avec la pagination

Une seule page de liste n'affiche que le premier lot de publications. Les anciens endpoints de liste de Reddit, plus favorables au rendu, acceptent un token after pour la page suivante, mais le moyen le plus fiable de paginer une liste rendue en JS via la Crawling API est de demander l'endpoint compagnon .json de la liste, qui est public et renvoie les mêmes publications ainsi qu'un curseur after. Ici, nous restons simples et paginons la liste publique du subreddit, en collectant un nombre fixe de pages et en faisant une pause entre les requêtes.

python
import json
import time
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

crawlbase_token = "YOUR_CRAWLBASE_TOKEN"
api = CrawlingAPI({"token": crawlbase_token})
BASE = "https://www.reddit.com"

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def scrape_subreddit(subreddit, max_pages=3):
    records = []
    after = None
    for _ in range(max_pages):
        url = f"{BASE}/r/{subreddit}/.json?limit=25"
        if after:
            url += f"&after={after}"
        body = crawl(url)
        if not body:
            break
        data = json.loads(body)["data"]
        for child in data["children"]:
            post = child["data"]
            records.append({
                "title": post.get("title"),
                "score": post.get("score"),
                "comment_count": post.get("num_comments"),
                "subreddit": f"r/{post.get('subreddit')}",
                "link": f"{BASE}{post.get('permalink', '')}",
            })
        after = data.get("after")
        if not after:
            break
        time.sleep(3)
    return records

if __name__ == "__main__":
    posts = scrape_subreddit("technology", max_pages=3)
    print(json.dumps(posts, indent=2, ensure_ascii=False))

L'endpoint public .json renvoie les mêmes champs agrégés avec des clés plus propres : title, score, num_comments, subreddit et permalink. Le curseur after sur chaque réponse est le seul état dont la pagination a besoin, donc la boucle demande la page suivante jusqu'à ce que Reddit cesse de renvoyer un curseur ou que vous atteigniez max_pages. Le time.sleep(3) entre les pages n'est pas une décoration : le rythme est le facteur le plus déterminant pour qu'une exécution reste saine. Si vous préférez analyser le HTML rendu à la place, remplacez crawl(url) par l'URL de liste de l'Étape 1 et passez le corps à scrape_listing.

À quoi ressemble la sortie

Lancez le script et vous obtenez une liste propre d'enregistrements publics et agrégés, prête à écrire au format JSON ou CSV.

json
[
  {
    "title": "Researchers demo a swallowable device that tracks vital signs",
    "score": 8421,
    "comment_count": 312,
    "subreddit": "r/technology",
    "link": "https://www.reddit.com/r/technology/comments/17xmvmg/swallowable_device_tracking_vital_signs_inside/"
  }
]

Pour persister ces enregistrements, quelques lignes de bibliothèque standard transforment la liste en un fichier CSV ou JSON. Les champs sont plats et uniformes, donc aucun traitement spécial n'est nécessaire.

python
import csv
import json

def save_json(records, path="reddit_posts.json"):
    with open(path, "w", encoding="utf-8") as f:
        json.dump(records, f, indent=2, ensure_ascii=False)

def save_csv(records, path="reddit_posts.csv"):
    fields = ["title", "score", "comment_count", "subreddit", "link"]
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(records)

À partir de là, les données sont prêtes pour l'analyse : classer les subreddits par score médian, suivre les domaines externes vers lesquels une communauté renvoie, ou tracer l'activité de commentaires sur une fenêtre de pages. Si vous prévoyez de les alimenter dans un modèle plus tard, notre guide sur comment structurer et nettoyer des données issues du web scraping pour l'IA et le ML couvre l'étape de normalisation, et le web scraping pour le machine learning couvre ce qu'il faut en faire ensuite.

Gérer les limites de débit et les erreurs

Deux couches peuvent freiner une exécution Reddit, et un script résilient tient compte des deux. Reddit limite le débit du trafic automatisé et renvoie 429 Too Many Requests ou 403 Forbidden lorsque vous poussez trop fort ; la Crawling API a elle aussi des limites par formule. Les habitudes ci-dessous maintiennent une exécution à l'intérieur des deux.

  • Rythmez vos requêtes. Le time.sleep(3) entre les pages est un plancher, pas un maximum. Marteler la liste dans une boucle serrée est le moyen le plus rapide de se faire limiter, alors ajoutez de vrais délais et résistez à l'envie de paralléliser de façon agressive.
  • Lisez les codes de statut. Une exécution qui se met à renvoyer 429 ou 403 vous dit que le rythme actuel ne suffit plus. Réduisez la cadence plutôt que de pousser plus fort, et envisagez un backoff exponentiel avec quelques tentatives.
  • Appuyez-vous sur la rotation. Un pool d'IP résidentielles répartit les requêtes sur de nombreuses adresses de vrais utilisateurs pour qu'aucune seule ne déclenche une limite. La Crawling API s'en charge pour vous ; si vous construisez votre propre pile, c'est la partie à bien faire.
  • Gardez un volume faible et des cibles variées. La recherche sur données publiques n'exige pas de crawler tout l'historique d'un subreddit. Échantillonnez les pages dont vous avez besoin et arrêtez-vous.

Est-il légal de scraper Reddit ?

C'est la section à lire avant d'écrire du code de production. L'accord d'utilisation de Reddit et sa politique sur le contenu public restreignent l'accès automatisé et la collecte en masse de contenu, et le robots.txt de Reddit précise ce que les crawlers peuvent toucher. Le scraping automatisé peut aller à l'encontre de ces conditions quel que soit le soin apporté à votre outillage, et rien dans le code ci-dessus n'y change quoi que ce soit. Cela ne fait que faire fonctionner la partie technique. Lisez d'abord les conditions de Reddit et son robots.txt, et traitez les deux comme la limite de ce que vous collectez.

Les règles honnêtes et restrictives à respecter. Ne collectez que des données publiques et agrégées : titres des publications, scores, nombres de commentaires, le subreddit et le lien, autant d'éléments que n'importe qui peut voir sans se connecter. Traitez les noms d'utilisateur, les pseudos d'auteur, les détails de profil et le texte des commentaires individuels comme des données personnelles, et ne les récoltez pas, ne construisez pas de profils de personnes identifiables et ne reliez pas le contenu à une personne. Ne scrapez jamais de subreddits privés, de contenu derrière une connexion, de messages directs ou quoi que ce soit qui exige une authentification, et ne contournez jamais une connexion ou un défi pour y accéder. Lorsque des données personnelles sont en jeu, des lois sur la vie privée comme le RGPD et le CCPA s'appliquent : il vous faut une base légale pour les traiter et vous devez honorer les demandes de suppression. Le scraper de ce guide reste volontairement du côté agrégé et non personnel de toutes ces lignes.

Pour tout usage réel ou commercial, le bon outil est l'API officielle Reddit. Elle est conçue pour un accès autorisé, vous offre une structure garantie, expose des limites de débit claires et vous maintient dans les conditions de Reddit. Cet article est un guide technique étroitement limité aux données de listes publiques et agrégées. Ce n'est pas une approbation de la collecte massive de données personnelles, et il ne couvre rien de ce qui se trouve derrière une connexion. Si votre projet a besoin de plus qu'un petit échantillon de champs publics, l'API Reddit ou un accord de données formel est la bonne voie, pas un scraper plus astucieux.

Récapitulatif

Points clés

  • Les listes Reddit sont rendues côté client et protégées contre les bots. Une simple récupération renvoie une coquille ou un défi, vous devez donc rendre la page avant de l'analyser.
  • Le rendu et une IP de confiance appartiennent à un seul appel. La Crawling API avec un token JS fait les deux ; ajax_wait et page_wait contrôlent combien de temps elle attend le contenu.
  • Analysez des signaux stables. Les attributs de shreddit-post (ou les clés de l'endpoint public .json) sont plus durables que des classes imbriquées fragiles.
  • Uniquement des champs publics et agrégés. Extrayez titre, score, nombre de commentaires, subreddit et lien ; jamais les noms d'utilisateur, les profils d'auteur ou le texte des commentaires.
  • Rythmez, faites tourner et préférez l'API officielle. Gardez un volume faible, appuyez-vous sur la rotation résidentielle et utilisez l'API Reddit pour tout ce qui est réel ou commercial.

Foire aux questions

Pourquoi une simple requête ne renvoie-t-elle aucune donnée de Reddit ?

Parce que le front-end actuel de Reddit rend les listes de publications côté client avec JavaScript, et qu'il défie le trafic automatisé. Une requête HTTP brute renvoie une coquille quasi vide, un bandeau de cookies ou une page de blocage. Pour obtenir de vraies données publiques, vous devez d'abord rendre la page, ce que le token JS de la Crawling API gère pour vous.

Ai-je besoin du token normal ou du token JS pour Reddit ?

Le token JS pour les pages de liste rendues, car le token normal renvoie la même coquille qu'une simple récupération. Si vous paginez plutôt l'endpoint public .json, cette réponse est du JSON brut, mais le faire transiter par la Crawling API bénéficie tout de même de l'IP de confiance et de la rotation qui empêchent l'exécution de se faire bloquer.

Quelles données Reddit peut-on scraper en toute sécurité ?

Uniquement des données publiques et agrégées : titres des publications, scores et nombre de votes positifs, nombres de commentaires, le subreddit et le lien vers lequel chaque publication pointe. Les noms d'utilisateur, les profils d'auteur et le texte des commentaires individuels sont des données personnelles et sont hors limites ici. Les subreddits privés, le contenu derrière une connexion et les messages directs sont entièrement hors du périmètre.

Faut-il utiliser l'API officielle Reddit ou scraper le site ?

Pour tout usage réel, continu ou commercial, utilisez l'API officielle Reddit. C'est la voie autorisée, elle offre une structure garantie et publie des limites de débit claires. Scraper un petit échantillon de champs de listes publiques avec l'approche présentée ici convient à une recherche légère sur données publiques lorsqu'aucun accès à l'API n'est en place, tant que vous respectez les conditions de Reddit, son robots.txt et ses limites de débit.

Comment gérer la pagination sur de nombreuses pages ?

Demandez l'endpoint .json de la liste publique avec une limit, lisez le curseur after de chaque réponse et repassez-le sous la forme &after=<cursor> à la requête suivante. Bouclez jusqu'à ce que Reddit cesse de renvoyer un curseur ou que vous atteigniez votre plafond de pages, et patientez quelques secondes entre les pages pour rester dans les limites de débit.

Comment éviter de se faire bloquer en scrapant Reddit ?

Gardez un débit de requêtes faible, ajoutez de vrais délais entre les pages, variez vos cibles au lieu de crawler tout l'historique d'un seul subreddit, et passez par des IP résidentielles rotatives pour qu'aucune adresse seule ne déclenche une limite. La Crawling API gère la rotation et un pool d'IP de confiance pour vous. Surveillez les réponses 429 et 403 et réduisez la cadence dès que vous les voyez.

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