Stack Overflow est l'une des plus grandes bases de connaissances publiques pour les développeurs, et chaque page de liste de questions porte des signaux structurés qui méritent d'être collectés : le titre de la question, les tags sous lesquels elle a été classée, son nombre de votes, le nombre de réponses, le nombre de personnes l'ayant consultée et un lien vers le fil complet. Agrégées sur un tag, ces données vous indiquent quels sujets montent en puissance, quels problèmes restent sans réponse et comment les questions d'une technologie évoluent dans le temps.
Ce guide vous montre comment scraper des questions Stack Overflow avec JavaScript et Node.js en utilisant cheerio. Vous construisez un petit script fonctionnel qui récupère une page publique de liste de questions via la Crawling API, analyse un enregistrement par question, gère la pagination sur un tag et exporte les résultats en JSON et CSV. L'ensemble du tutoriel reste limité aux données de liste publiques, et la section sur la légalité près de la fin n'est pas un simple boilerplate, lisez-la avant de pointer ceci sur un volume réel.
Ce que vous allez construire
Un script Node.js qui prend une URL de tag Stack Overflow public, récupère le HTML de la page via la Crawling API et extrait un enregistrement structuré pour chaque question dans la liste. Nous utilisons le tag javascript comme exemple fil conducteur et extrayons ces champs par question :
- Title le texte de la question, par exemple "How do I return the response from an asynchronous call?".
- Tags la liste des tags sous lesquels la question a été classée, comme "javascript, async-await, promise".
- Votes le nombre de votes nets affiché sur la carte récapitulative.
- Answers le nombre de réponses, avec "0 answers" quand il n'en existe pas encore.
- Views le nombre de vues tel qu'affiché sur la carte.
- Link l'URL absolue vers la page de la question individuelle.
Pourquoi une requête simple peut être insuffisante sur Stack Overflow
Stack Overflow sert une bonne partie du balisage de liste côté serveur, donc une requête HTTP brute vous mène plus loin ici que sur un site fortement rendu côté client. Le problème est la cohérence à volume. Stack Overflow surveille le trafic automatisé, et une adresse IP de centre de données effectuant des requêtes rapides et répétitives se fait limiter en débit ou reçoit une page de défi au lieu du balisage des questions. Quand cela arrive, votre analyseur voit une mise en page inattendue et l'exécution se dégrade silencieusement.
Un scraper Stack Overflow fiable a donc besoin d'une adresse IP que le site perçoit comme un vrai visiteur et, sur les pages qui s'appuient sur des scripts, d'un navigateur qui rend avant que vous n'analysiez. Vous pouvez assembler cela vous-même avec un pool de proxies résidentiels rotatifs et un navigateur sans interface graphique, 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 l'URL, elle récupère derrière une adresse IP de confiance (et rend la page quand vous passez le token JavaScript), et retourne le HTML finalisé à analyser.
Crawlbase propose deux types de tokens. Le token normal récupère le HTML statique et suffit pour la liste Stack Overflow rendue côté serveur utilisée ici. Le token JavaScript (JS) rend d'abord la page dans un vrai navigateur, que vous utilisez quand une cible charge son contenu côté client. Commencez avec le token normal pour ces pages de liste ; passez au token JS si une page que vous ciblez revient avec des champs manquants.
Prérequis
Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.
Node.js 16 ou version ultérieure. Vérifiez votre version avec node --version. Si vous ne l'avez pas, installez-le depuis le site Node.js ou via un gestionnaire de versions comme nvm.
JavaScript et Node.js de base. Vous devez être à l'aise pour écrire et exécuter un script Node et installer des paquets avec npm. Si vous êtes novice avec Node, la documentation officielle et n'importe quel cours pour débutants vous amèneront au niveau que ce tutoriel suppose. Pour un tutoriel plus complet, consultez notre guide sur comment construire un scraper web avec Node.js.
Un compte Crawlbase et un token. Inscrivez-vous, ouvrez votre tableau de bord et copiez votre token de requêtes normales depuis la page de documentation du compte. Traitez le token comme un mot de passe : il authentifie vos requêtes, donc ne le mettez pas dans le contrôle de version.
Configurer le projet
Créez un dossier de projet, initialisez-le et installez les deux bibliothèques dont le scraper a besoin.
node --version mkdir stackoverflow-scraper && cd stackoverflow-scraper npm init -y npm install crawlbase cheerio
Deux dépendances font le travail : crawlbase est le client Node officiel pour la Crawling API, et cheerio analyse le HTML retourné avec une API de style jQuery pour que vous puissiez extraire des champs individuels par sélecteur CSS. Si les sélecteurs sont nouveaux pour vous, le guide sur XPath et les sélecteurs CSS est un bon complément.
Étape 1 : Récupérer la page de liste de questions
Commencez par obtenir la page. Importez la classe CrawlingAPI, initialisez-la avec votre token et demandez l'URL du tag. Vérifier le code de statut avant d'analyser permet de détecter les échecs de manière explicite plutôt que silencieuse.
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); async function crawl(pageUrl) { const response = await api.get(pageUrl); if (response.statusCode === 200) { return response.body; } console.error(`Request failed: ${response.statusCode}`); return null; } const tagUrl = 'https://stackoverflow.com/questions/tagged/javascript'; crawl(tagUrl).then((html) => { console.log(html ? html.slice(0, 500) : 'No HTML returned'); });
L'URL du tag suit une forme fixe : https://stackoverflow.com/questions/tagged/<tag>. Remplacez javascript par n'importe quel tag que vous souhaitez étudier, comme python ou node.js. Exécutez le script avec node scraper.js et vous devriez voir du vrai balisage de questions, pas une page de défi. Cela confirme que la requête fonctionne avant d'écrire un seul sélecteur.
Cet unique appel api.get fait plus qu'une requête ordinaire : il récupère la page de tag derrière une adresse IP de confiance et fait tourner les adresses résidentielles côté serveur, de sorte que Stack Overflow perçoit votre trafic comme un vrai visiteur plutôt qu'un scraper à limiter. Vous n'avez pas besoin de gérer vous-même une flotte de navigateurs sans interface graphique ni un pool de proxies, et quand une cible nécessite un rendu, il suffit d'ajouter le token JavaScript. Pointez-le d'abord sur une page de tag public via le niveau gratuit.
Étape 2 : Analyser chaque question avec cheerio
HTML en main, chargez-le dans cheerio et parcourez les cartes de questions. Stack Overflow présente chaque question dans un bloc .js-post-summary répété à l'intérieur de #questions, donc vous sélectionnez chaque récapitulatif puis lisez le titre, les tags, les votes, les réponses, les vues et le lien à l'intérieur. La chaîne .replace(/\s+/g, ' ').trim() condense les espaces blancs que Stack Overflow remplit dans son balisage en texte à espacement simple propre.
const cheerio = require('cheerio'); const clean = (text) => text.replace(/\s+/g, ' ').trim(); function parseQuestions(html) { const $ = cheerio.load(html); const questions = []; $('#questions .js-post-summary').each((_, element) => { const el = $(element); const title = clean(el.find('.s-post-summary--content-title').text()); const link = el.find('.s-link').attr('href') || ''; const votes = clean( el.find('.js-post-summary-stats .s-post-summary--stats-item:first-child').text() ); const answers = clean(el.find('.js-post-summary-stats .has-answers').text()) || '0 answers'; const views = clean( el.find('.js-post-summary-stats .s-post-summary--stats-item:last-child').text() ); const tags = el .find('.js-post-tag-list-item') .map((__, tag) => clean($(tag).text())) .get() .filter(Boolean); questions.push({ title, tags, votes, answers, views, link: link.includes('https://') ? link : `https://stackoverflow.com${link}`, }); }); return questions; }
Quelques détails garantissent la fidélité à la page. Les votes et les vues se trouvent tous deux sous .js-post-summary-stats comme entrées .s-post-summary--stats-item, donc le premier correspond aux votes et le dernier aux vues. Le nombre de réponses porte une classe .has-answers uniquement quand une question a des réponses, c'est pourquoi il revient à '0 answers' quand le sélecteur retourne vide. Les tags proviennent de chaque .js-post-tag-list-item, mappés dans un tableau pour les conserver structurés. Le lien est lu depuis le href de l'ancre et rendu absolu, car Stack Overflow retourne un chemin relatif comme /questions/123/....
Les noms de classes de Stack Overflow (js-post-summary, s-post-summary--content-title, js-post-tag-list-item et autres) peuvent changer sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand un champ revient vide, ré-inspectez la page en direct dans les outils de développement de votre navigateur et mettez le sélecteur à jour. La maintenance périodique des sélecteurs est normale pour tout scraper en production, pas le signe que quelque chose est cassé.
Étape 3 : Assembler le tout et exporter
Câblez maintenant la récupération et l'analyse dans un script exécutable, puis écrivez les enregistrements en JSON et CSV. JSON conserve le tableau de tags imbriqué intact pour un usage programmatique ; le CSV aplatit chaque enregistrement en une ligne pour les feuilles de calcul, en joignant les tags avec un séparateur.
const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const fs = require('fs'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const clean = (text) => text.replace(/\s+/g, ' ').trim(); async function crawl(pageUrl) { const response = await api.get(pageUrl); if (response.statusCode === 200) return response.body; console.error(`Request failed: ${response.statusCode}`); return null; } function parseQuestions(html) { const $ = cheerio.load(html); const questions = []; $('#questions .js-post-summary').each((_, element) => { const el = $(element); const link = el.find('.s-link').attr('href') || ''; questions.push({ title: clean(el.find('.s-post-summary--content-title').text()), tags: el .find('.js-post-tag-list-item') .map((__, tag) => clean($(tag).text())) .get() .filter(Boolean), votes: clean( el.find('.js-post-summary-stats .s-post-summary--stats-item:first-child').text() ), answers: clean(el.find('.js-post-summary-stats .has-answers').text()) || '0 answers', views: clean( el.find('.js-post-summary-stats .s-post-summary--stats-item:last-child').text() ), link: link.includes('https://') ? link : `https://stackoverflow.com${link}`, }); }); return questions; } function toCsv(rows) { const headers = ['title', 'tags', 'votes', 'answers', 'views', 'link']; const escape = (value) => `"${String(value).replace(/"/g, '""')}"`; const lines = [headers.join(',')]; for (const row of rows) { lines.push( [ escape(row.title), escape(row.tags.join('|')), escape(row.votes), escape(row.answers), escape(row.views), escape(row.link), ].join(',') ); } return lines.join('\n'); } async function main() { const tagUrl = 'https://stackoverflow.com/questions/tagged/javascript'; const html = await crawl(tagUrl); if (!html) return; const questions = parseQuestions(html); fs.writeFileSync('questions.json', JSON.stringify(questions, null, 2)); fs.writeFileSync('questions.csv', toCsv(questions)); console.log(`Saved ${questions.length} questions to questions.json and questions.csv`); } main();
Exécutez le script complet avec node scraper.js. Il récupère la page de tag, analyse chaque carte de question et écrit questions.json et questions.csv dans votre dossier de projet. Le CSV échappe les guillemets et joint le tableau de tags avec un pipe afin qu'une question avec plusieurs tags reste dans une seule cellule.
À quoi ressemble le résultat
Le fichier JSON contient un objet par question, avec les tags conservés sous forme de tableau structuré, prêt à charger dans un script d'analyse ou une base de données.
[ { "title": "How do I return the response from an asynchronous call?", "tags": ["javascript", "ajax", "asynchronous", "promise"], "votes": "8632 votes", "answers": "42 answers", "views": "2.1m views", "link": "https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call" }, { "title": "What does \"use strict\" do in JavaScript?", "tags": ["javascript", "syntax", "jslint", "use-strict"], "votes": "9201 votes", "answers": "32 answers", "views": "1.0m views", "link": "https://stackoverflow.com/questions/1335851/what-does-use-strict-do-in-javascript" } ]
Le miroir CSV des mêmes données est une ligne d'en-tête plus une ligne par question, avec les tags joints dans une seule cellule délimitée par des pipes.
title,tags,votes,answers,views,link "How do I return the response from an asynchronous call?","javascript|ajax|asynchronous|promise","8632 votes","42 answers","2.1m views","https://stackoverflow.com/questions/14220321/..." "What does ""use strict"" do in JavaScript?","javascript|syntax|jslint|use-strict","9201 votes","32 answers","1.0m views","https://stackoverflow.com/questions/1335851/..."
Parcourir les pages d'un tag
Une page de questions est une démonstration ; un vrai job parcourt la pagination. Stack Overflow expose le numéro de page via le paramètre de requête page, donc vous pouvez construire chaque URL de page dans une boucle, la récupérer via la Crawling API, l'analyser avec la même fonction et collecter les lignes. Comme chaque page de liste partage la même structure de carte, l'analyseur que vous avez déjà écrit fonctionne sur toutes sans modification.
async function scrapeTag(tag, totalPages) { const all = []; for (let page = 1; page <= totalPages; page++) { const url = `https://stackoverflow.com/questions/tagged/${tag}?tab=newest&page=${page}`; const html = await crawl(url); if (html) all.push(...parseQuestions(html)); } return all; } scrapeTag('javascript', 3).then((rows) => { console.log(`Collected ${rows.length} questions`); });
Pour enrichir chaque ligne avec le corps complet de la question, la réponse acceptée ou le fil de commentaires, prenez le link de chaque carte et récupérez cette page de question individuelle via la même fonction crawl, puis écrivez un petit analyseur pour la mise en page de la question. Le modèle est identique : récupérer, puis analyser. Pour plus d'informations sur les cibles avec rendu intensif, voir comment crawler des sites web JavaScript.
Rester non bloqué
Même avec une adresse IP de confiance gérée pour vous, Stack Overflow surveille le trafic aux allures de scraper. Quelques habitudes maintiennent une exécution saine et s'appliquent à n'importe quel site que vous scrapez à volume.
- Espacez vos requêtes. Marteler des pages dans une boucle serrée est le moyen le plus rapide d'être limité en débit. Répartissez les requêtes et variez vos tags au lieu de crawler un seul chemin à pleine vitesse.
- Misez sur la rotation. Un pool d'adresses IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels de sorte qu'aucune ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous montez votre propre pile, c'est la partie à soigner.
- Lisez les codes de statut. Une exécution qui commence à retourner des défis ou des erreurs vous dit que le débit ou le niveau d'IP actuel ne suffit plus. Traitez cela comme un signal pour lever le pied, pas comme du bruit à ignorer.
Pour le guide plus large, voir comment scraper des sites web sans être bloqué. Si vous souhaitez comparer des piles d'analyse au-delà de cheerio, le tour d'horizon des meilleures bibliothèques de scraping open source est une carte utile. Et si vous collectez des données de communauté de développeurs plus largement, le même modèle de récupération puis analyse s'applique à scraper des dépôts et profils GitHub.
Est-il légal de scraper Stack Overflow ?
La légalité du scraping de Stack Overflow dépend de ses conditions d'utilisation, de votre juridiction et de ce que vous faites des données. Stack Overflow, qui fait partie du réseau Stack Exchange, publie des conditions d'utilisation du réseau public et une politique d'utilisation acceptable qui restreignent l'accès automatisé, de sorte que le scraping peut aller à l'encontre de ces conditions quelle que soit la précaution de vos outils. Aucun code présenté ici ne change cela ; il ne fait que rendre la partie technique fonctionnelle. Lisez les conditions Stack Exchange et le fichier robots.txt du site, et traitez les deux comme la frontière de ce que vous collectez et à quelle vitesse.
Avant d'écrire un scraper, vérifiez si la voie officielle couvre votre besoin, car pour Stack Overflow c'est souvent le cas. Stack Exchange propose une Stack Exchange API officielle qui retourne les questions, tags, votes, réponses et vues en JSON propre, et publie des dumps de données périodiques du contenu public complet sous licence Creative Commons. Pour la recherche, l'analyse ou tout travail à volume, l'API et les dumps de données sont les bons outils : ils sont structurés, limités en débit selon des conditions que vous acceptez, et ils vous maintiennent du bon côté des politiques du réseau. Recourez au scraping uniquement pour des besoins publics ponctuels et limités que l'API ne couvre pas.
Limitez le travail aux données publiques non personnelles. Les titres de questions, les tags et les comptages agrégés de votes, réponses et vues utilisés dans ce guide sont des signaux de liste publics. Le contenu des utilisateurs est une autre affaire : les noms d'utilisateur, la réputation, les détails de profil et les textes que les gens écrivent sont des données personnelles, et republier le contenu d'un individu ou le lier à son identité peut déclencher des obligations en vertu des lois sur la vie privée telles que le RGPD et la CCPA, notamment une base légale pour le traitement et le respect des demandes de suppression. Ce guide ne couvre rien derrière une connexion, les messages privés, ni la construction de profils d'utilisateurs identifiables. Agrégez quand vous pouvez, et préférez l'API officielle ou les dumps de données chaque fois que votre projet touche des données au niveau utilisateur.
Points clés
- Récupérez derrière une adresse IP de confiance. Stack Overflow limite le trafic aux allures de scraper, donc la Crawling API récupère chaque page de tag depuis une adresse IP résidentielle rotative et retourne du HTML propre à analyser.
-
cheerio effectue l'extraction. Sélectionnez chaque carte
.js-post-summaryà l'intérieur de#questions, puis mappez title, tags, votes, answers, views et le lien sur les sélecteurs actuels, en sachant que ces sélecteurs évolueront. -
Conservez les tags structurés. Lisez chaque
.js-post-tag-list-itemdans un tableau pour que les tags d'une question restent interrogeables en JSON et s'effondrent en une cellule dans le CSV. -
Passez à l'échelle en parcourant les pages. Le paramètre
pageparcourt la liste d'un tag, et le même analyseur fonctionne sur chaque page avec un espacement sensible. - Préférez la voie officielle. La Stack Exchange API et les dumps de données sous licence CC sont la voie officielle pour le volume ; restez sur les données publiques, respectez les conditions d'utilisation et le robots.txt, et évitez les données personnelles au niveau utilisateur.
Foire aux questions
Ai-je besoin du token normal ou du token JS pour Stack Overflow ?
Le token normal suffit pour les pages de liste de questions dans ce guide, car Stack Overflow sert ce balisage côté serveur. Utilisez le token JS quand une page cible charge son contenu côté client et revient avec des champs manquants. Commencez avec le token normal ici, et ne changez que si une page que vous scrapez retourne des sélecteurs vides.
Quels champs puis-je extraire d'une liste de questions Stack Overflow ?
De chaque carte récapitulative vous pouvez extraire le titre de la question, les tags sous lesquels elle a été classée, le nombre de votes nets, le nombre de réponses, le nombre de vues et le lien vers la question complète. Ce guide mappe chacun d'eux sur un sélecteur CSS et les assemble en un enregistrement par question, exporté en JSON et CSV.
Mes sélecteurs retournent des valeurs vides. Qu'est-ce qui a changé ?
Très certainement le balisage de Stack Overflow. Ses classes de cartes js-post-summary, l'enveloppe de titre s-post-summary--content-title et les marqueurs de tags js-post-tag-list-item peuvent changer sans préavis. Ré-inspectez une page en direct dans les outils de développement de votre navigateur et mettez les sélecteurs à jour. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Dois-je utiliser la Stack Exchange API ou scraper le site ?
Si vous avez besoin de volume, de structure garantie ou du contenu public complet, utilisez la Stack Exchange API officielle ou ses dumps de données Creative Commons. Ils sont conçus pour cela et vous maintiennent du bon côté des conditions du réseau. Scraper les pages de liste publiques avec l'approche de ce guide convient aux besoins publics limités et ponctuels que l'API ne couvre pas, à condition de respecter les conditions d'utilisation, le robots.txt et les limites de débit.
Puis-je scraper les profils utilisateurs ou la réputation de Stack Overflow ?
Ce guide ne le fait délibérément pas. Les noms d'utilisateur, la réputation et le contenu que les gens écrivent sont des données personnelles, et construire des profils d'utilisateurs identifiables peut déclencher des obligations en vertu des lois sur la vie privée telles que le RGPD et la CCPA. Restez sur les signaux de liste publics tels que les titres, les tags et les comptages agrégés, agrégez quand vous pouvez et utilisez l'API officielle si votre projet a genuinement besoin de données au niveau utilisateur.
Comment éviter d'être bloqué en scrapant Stack Overflow ?
Maintenez un faible taux de requêtes par adresse IP, variez vos tags au lieu de boucler sur un seul chemin, et acheminez via des adresses IP résidentielles rotatives de sorte qu'aucune adresse unique ne déclenche une limite de débit. La Crawling API gère la rotation et un pool d'adresses IP de confiance pour vous ; si vous construisez votre propre pile, c'est la partie sur laquelle investir. Surveillez les codes de statut et levez le pied lorsque vous commencez à voir des défis.
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.

