Gumtree est l'un des sites de petites annonces les plus visités au Royaume-Uni, un marketplace unique où les gens annoncent des voitures, des meubles, des biens immobiliers, de l'électronique et des emplois dans leur zone locale. Chaque page de résultats de recherche est un flux public et structuré de ce qui est à vendre en ce moment : chaque vignette affiche un titre, un prix demandé, un emplacement et un lien vers l'annonce complète. C'est donc une source propre pour la comparaison de prix, la recherche sur la demande régionale et le suivi de l'évolution des annonces au fil du temps.

Ce guide vous montre comment scraper les annonces classifiées Gumtree avec Python. Vous construisez un petit scraper exécutable qui récupère une page de recherche Gumtree via l'API Crawling, analyse un enregistrement propre pour chaque vignette d'annonce, gère la pagination et exporte les résultats en JSON et CSV. L'ensemble du tutoriel reste limité aux données d'annonces publiques : les titres, prix, emplacements et liens que tout le monde peut voir sur une page de résultats de recherche sans se connecter.

Ce que vous allez construire

Un script Python qui prend une URL de recherche Gumtree, récupère la page de résultats rendue via l'API Crawling, et extrait un enregistrement structuré par vignette d'annonce. Nous utilisons une recherche de casque audio comme exemple fil conducteur, la même requête qu'utilisait l'ancien tutoriel, et récupérons ces champs de chaque vignette :

  • Titre le titre de l'annonce affiché sur la vignette, par exemple la marque et le modèle d'une voiture.
  • Prix le prix demandé par le vendeur, quand la vignette en affiche un.
  • Emplacement la zone où l'article est annoncé, utile pour l'analyse de la demande régionale.
  • Lien l'URL absolue vers la page de l'annonce propre à cette annonce.

Pourquoi une requête simple échoue sur Gumtree

Si vous pointez un client HTTP basique sur une URL de recherche Gumtree, vous obtenez rarement les vignettes que vous cherchez. Deux facteurs jouent contre vous. Premièrement, Gumtree rend une grande partie de la grille de résultats côté client : le serveur envoie une coquille légère et les vignettes d'annonces se remplissent à mesure que le JavaScript de la page s'exécute, donc le HTML initial obtenu d'un simple requests.get manque souvent précisément les vignettes que vous souhaitez analyser. Deuxièmement, Gumtree surveille le trafic automatisé. Les plages d'adresses IP de centres de données et les schémas de requêtes qui ne ressemblent pas à un vrai navigateur se heurtent à une page de challenge, une limite de débit ou un blocage direct avant que vous n'atteigniez les annonces.

Un scraper Gumtree fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend la page, et une adresse IP que Gumtree interprète comme un visiteur réel. Vous pouvez assembler cela vous-même avec un navigateur headless et un pool de proxies résidentiels rotatifs, mais maintenir ce stack en bon état représente l'essentiel du travail. L'API Crawling combine les deux en un seul appel : vous lui envoyez l'URL de recherche, elle rend la page derrière une IP résidentielle de confiance, gère la rotation et tout CAPTCHA, et vous renvoie du HTML terminé à analyser.

Prérequis

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

Python de base. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des packages avec pip. Si vous débutez avec le langage, le guide sur le web scraping avec Python couvre le niveau que ce tutoriel suppose.

Python 3.8 ou supérieur. Confirmez votre version avec python --version (ou python3 --version). Si vous ne l'avez pas, installez-le depuis python.org et assurez-vous que Python est dans votre PATH système.

Un compte Crawlbase et un token. Inscrivez-vous pour un compte gratuit, ouvrez votre tableau de bord et copiez votre token. Le niveau gratuit inclut jusqu'à 20 000 requêtes sans carte bancaire, ce qui est largement suffisant pour construire et tester ce scraper. Traitez le token comme un mot de passe et gardez-le hors du contrôle de version.

Configurer le projet

Créez un environnement virtuel pour que les dépendances du projet restent isolées, puis installez les deux bibliothèques dont le scraper a besoin. crawlbase est le client officiel pour l'API Crawling, et beautifulsoup4 analyse le HTML renvoyé pour que vous puissiez extraire chaque champ des vignettes d'annonces par sélecteur CSS.

bash
python --version

python -m venv gumtree_env
source gumtree_env/bin/activate

pip install crawlbase beautifulsoup4

Sur Windows, activez l'environnement avec gumtree_env\Scripts\activate au lieu de la ligne source. Avec les deux bibliothèques installées, créez le fichier script que le reste du guide va construire :

bash
touch gumtree_scraper.py

Inspecter la page de recherche pour les sélecteurs

Avant d'écrire des sélecteurs, ouvrez une page de résultats de recherche Gumtree dans votre navigateur, faites un clic droit sur une vignette d'annonce et choisissez Inspecter. Gumtree enveloppe chaque résultat dans un élément article marqué avec un attribut data-q="search-result", et expose les champs à l'intérieur de ce conteneur avec leurs propres attributs data-* stables. Appuyez-vous sur ces attributs structurels plutôt que sur une chaîne fragile de noms de classes générés : ils survivent bien mieux aux refontions cosmétiques.

En inspectant une page de résultats, voici les sélecteurs sur lesquels l'ancien tutoriel s'appuyait, et ils restent le bon point de départ :

  • Titre un <div> avec data-q="tile-title".
  • Prix un <div> avec data-testid="price".
  • Emplacement un <div> avec data-q="tile-location".
  • Lien le href sur la balise <a> avec data-q="search-result-anchor", qui est relatif et nécessite l'ajout du préfixe d'hôte https://www.gumtree.com.

Étape 1 : Récupérer la page de recherche rendue

Commencez par obtenir la page terminée. Importez la classe CrawlingAPI, initialisez-la avec votre token, définissez l'URL de recherche et demandez-la. Vérifier le code de statut avant d'analyser rend les échecs visibles plutôt que silencieux.

python
from crawlbase import CrawlingAPI

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

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

if __name__ == "__main__":
    search_url = "https://www.gumtree.com/search?q=headset"
    html = crawl(search_url)
    print(html[:500] if html else "No HTML returned")

Les deux options d'attente sont importantes pour une grille qui se remplit au chargement de la page. ajax_wait indique à l'API d'attendre la fin du contenu asynchrone, et page_wait maintient un nombre fixe de millisecondes après le chargement pour que les vignettes à rendu tardif apparaissent avant la capture de la page. Exécutez le script et vous devriez voir de vraies balises d'annonces, pas une coquille de challenge. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.

Crawlbase Crawling API

La grille de résultats de Gumtree se rend côté client et le site challenge le trafic qui ne ressemble pas à un vrai visiteur. L'API Crawling prend votre token, exécute la page de recherche dans un vrai navigateur, fait tourner des IP résidentielles côté serveur et gère tout CAPTCHA, puis vous remet du HTML terminé. Vous évitez de faire tourner vous-même une flotte de navigateurs headless et un pool de proxies. Pointez-la sur une URL de recherche avec le niveau gratuit de 20 000 requêtes d'abord.

Étape 2 : Analyser les vignettes d'annonces avec BeautifulSoup

Avec le HTML rendu en main, chargez-le dans BeautifulSoup, trouvez chaque vignette de résultat et extrayez chaque champ par son sélecteur. Gumtree enveloppe chaque résultat dans un conteneur article[data-q="search-result"] et expose le titre, le prix et l'emplacement à l'intérieur sur leurs propres attributs data-*. Lisez le lien de l'annonce depuis l'ancre de la vignette et normalisez-le en URL absolue. Enveloppez chaque vignette dans un try/except pour qu'une annonce mal formée ne fasse pas planter l'exécution.

python
from bs4 import BeautifulSoup

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

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

def parse_link(tile):
    a = tile.select_one('a[data-q="search-result-anchor"]')
    if not a or not a.get("href"):
        return None
    href = a["href"]
    return href if href.startswith("http") else BASE + href

def scrape_gumtree_search(html):
    soup = BeautifulSoup(html, "html.parser")
    tiles = soup.select('article[data-q="search-result"]')
    results = []
    for tile in tiles:
        try:
            results.append({
                "title": text_of(tile, 'div[data-q="tile-title"]'),
                "price": text_of(tile, 'div[data-testid="price"]'),
                "location": text_of(tile, 'div[data-q="tile-location"]'),
                "link": parse_link(tile),
            })
        except Exception as e:
            print(f"Skipped a tile: {e}")
    return results

L'assistant text_of interroge un élément à l'intérieur d'une vignette et renvoie None quand il est absent, au lieu de lever une exception lors d'un appel .get_text() sur rien. Cela maintient l'extraction robuste quand un champ est absent, ce qui est courant car toutes les annonces n'affichent pas un prix propre. Le lien vient de l'ancre a[data-q="search-result-anchor"] et est normalisé en URL absolue, car Gumtree sert un href relatif qui pointe vers la page d'annonce.

Selectors drift

Les attributs data-q et data-testid de Gumtree sont plus durables que les noms de classes générés, mais ils ne sont pas permanents : une refonte peut les renommer ou les supprimer. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand un champ revient comme None pour chaque vignette, inspectez à nouveau la page de recherche en direct avec les outils de développement de votre navigateur et mettez à jour le sélecteur. Une maintenance périodique des sélecteurs est normale pour tout scraper en production.

Étape 3 : Gérer la pagination et exporter JSON et CSV

Une page de résultats est une démonstration ; un vrai travail en parcourt plusieurs. Gumtree pagine ses résultats de recherche avec un paramètre de requête ?page=N, vous pouvez donc boucler sur un nombre fixe de pages, récupérer chacune d'elles et collecter les vignettes. Reliez maintenant la récupération, l'analyse et la boucle de pagination en un script exécutable, puis écrivez les enregistrements en JSON et en CSV pour pouvoir les charger dans un notebook ou un tableur.

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

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})
BASE = "https://www.gumtree.com"
FIELDS = ["title", "price", "location", "link"]

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

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

def parse_link(tile):
    a = tile.select_one('a[data-q="search-result-anchor"]')
    if not a or not a.get("href"):
        return None
    href = a["href"]
    return href if href.startswith("http") else BASE + href

def scrape_gumtree_search(html):
    soup = BeautifulSoup(html, "html.parser")
    tiles = soup.select('article[data-q="search-result"]')
    results = []
    for tile in tiles:
        try:
            results.append({
                "title": text_of(tile, 'div[data-q="tile-title"]'),
                "price": text_of(tile, 'div[data-testid="price"]'),
                "location": text_of(tile, 'div[data-q="tile-location"]'),
                "link": parse_link(tile),
            })
        except Exception as e:
            print(f"Skipped a tile: {e}")
    return results

def scrape_pages(base_url, max_pages):
    all_listings = []
    for page in range(1, max_pages + 1):
        page_url = f"{base_url}&page={page}"
        html = crawl(page_url)
        if not html:
            break
        found = scrape_gumtree_search(html)
        if not found:
            break
        all_listings.extend(found)
        print(f"Page {page}: {len(found)} listings")
        time.sleep(2)
    return all_listings

def export(rows, name="gumtree_listings"):
    with open(f"{name}.json", "w", encoding="utf-8") as f:
        json.dump(rows, f, indent=2, ensure_ascii=False)
    with open(f"{name}.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(rows)
    print(f"Saved {len(rows)} listings to {name}.json and {name}.csv")

def main():
    base_url = "https://www.gumtree.com/search?q=headset"
    rows = scrape_pages(base_url, max_pages=5)
    export(rows)

if __name__ == "__main__":
    main()

Exécutez le script complet avec python gumtree_scraper.py. Il parcourt jusqu'à cinq pages de la recherche de casques audio, analyse une ligne par vignette d'annonce, et écrit à la fois gumtree_listings.json et gumtree_listings.csv. La boucle scrape_pages ajoute &page=N à l'URL de recherche, s'arrête tôt quand une page ne renvoie aucune vignette (pour s'arrêter à la fin des résultats plutôt que de récupérer des pages vides), et le time.sleep(2) entre les requêtes cadence l'exécution. La liste partagée FIELDS maintient l'ordre des colonnes CSV en phase avec les clés du dictionnaire, pour que les deux exports ne divergent jamais.

À quoi ressemble la sortie

Vous obtenez une liste propre d'enregistrements d'annonces, dans l'ordre des pages, prête à écrire en JSON, CSV ou dans une base de données.

json
[
  {
    "title": "SteelSeries Arctis 7 Wireless Gaming Headset",
    "price": "£65.00",
    "location": "Manchester, Greater Manchester",
    "link": "https://www.gumtree.com/p/headphones/steelseries-arctis-7/1488114476"
  },
  {
    "title": "Sony WH-1000XM4 Noise Cancelling Headphones",
    "price": "£180.00",
    "location": "Leeds, West Yorkshire",
    "link": "https://www.gumtree.com/p/headphones/sony-wh-1000xm4/1483456978"
  }
]

Le CSV reproduit ces lignes avec une ligne d'annonce par enregistrement sous un en-tête title,price,location,link. À partir de là, vous pouvez trier par prix, regrouper par emplacement pour voir où un article donné est le plus courant, ou différencier des exécutions successives pour suivre l'évolution des annonces et des prix demandés au fil du temps. Pour un exemple concret de transformation des exports d'annonces en vue comparative, voir le web scraping e-commerce.

Mettre à l'échelle au-delà d'une seule recherche

Le même schéma s'étend à autant de recherches que nécessaire. Gardez une correspondance entre noms de requêtes et URL de recherche, bouclez dessus et exécutez le scraper paginé pour chacune, en indexant la sortie par requête pour que les listes restent séparées. Si vous souhaitez des enregistrements plus riches, suivez le link de chaque vignette vers sa page d'annonce et analysez les champs plus complets là-bas (la description complète, la date de mise en ligne, les URL des images), puis fusionnez-les dans l'enregistrement d'annonce. Dans tous les cas, maintenez un débit de requêtes modeste et appuyez-vous sur la rotation de l'API Crawling pour qu'un déploiement sur de nombreuses recherches ne déclenche pas une limite de débit.

Pour la recherche sur les marchés locaux, le champ d'emplacement est le levier : filtrer par zone, ou scraper la même requête sur plusieurs recherches de villes, permet de comparer prix et offre région par région. C'est exactement le type de signal régional pour lequel les annonces publiques de Gumtree sont bien adaptées, et c'est pourquoi les données de petites annonces valent la peine d'être collectées sous forme structurée plutôt que de parcourir le site à l'oeil nu.

Rester non bloqué

Même avec le rendu géré, Gumtree surveille le trafic ayant l'aspect d'un scraper. Quelques habitudes maintiennent une exécution en bonne santé, et elles s'appliquent à toute cible de petites annonces ou de marketplace.

  • Cadencez vos requêtes. Espacez les requêtes avec un délai entre les pages et les recherches plutôt que de tout crawler à pleine vitesse. Planifiez les travaux plus lourds pendant les heures creuses pour réduire la charge sur les serveurs de Gumtree.
  • Misez sur la rotation. Un pool d'adresses IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels pour qu'aucune ne déclenche une limite de débit. L'API Crawling gère cela pour vous ; si vous construisez votre propre stack, c'est la partie à soigner.
  • Ne conservez que ce dont vous avez besoin. Stockez les champs d'annonces publics que votre projet utilise et ignorez le reste. Revérifiez régulièrement vos sélecteurs pour que le scraper reste à jour avec les changements de balisage.

Pour le guide complet sur la façon d'éviter les blocages, consultez comment scraper des sites web sans être bloqué, et pour plus d'informations sur l'importance du rendu ici, comment crawler des sites web JavaScript. Si vous souhaitez approfondir le côté BeautifulSoup, le guide sur l'utilisation de BeautifulSoup en Python couvre la bibliothèque d'analyse en détail.

Est-il légal de scraper Gumtree ?

La question de savoir si le scraping de Gumtree est autorisé dépend des Conditions d'utilisation de Gumtree, de votre juridiction, et de ce que vous faites avec les données. Les conditions de Gumtree imposent des limites à l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quelle que soit la prudence de vos outils. Aucun code ici ne change cela ; il rend simplement la partie technique fonctionnelle. Lisez les Conditions d'utilisation de Gumtree et son robots.txt, et traitez les deux comme la limite de ce que vous collectez. Pour un usage commercial ou concurrentiel, la situation juridique devient plus complexe, et consulter un expert juridique sur votre cas spécifique est la démarche sensée.

Quelques lignes à respecter. Collectez uniquement des données d'annonces publiques : les titres, prix, emplacements et liens d'annonces que tout le monde peut voir sur une page de résultats de recherche sans compte. Ne collectez pas les données personnelles des vendeurs privés : noms, numéros de téléphone, adresses e-mail, ou tout ce qui pourrait identifier une personne, même quand cela apparaît sur une annonce publique. Maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger les serveurs de Gumtree, et ne scrapez jamais rien derrière une connexion ou une étape de révélation de contact qui existe pour filtrer les coordonnées d'un vendeur.

Ce guide est délibérément limité aux champs publics d'annonces de recherche car c'est la ligne qui rend le travail défendable. Il ne couvre pas les données de compte ou de messagerie, les coordonnées personnelles d'un vendeur, ni toute tentative de contournement d'une authentification ou d'un CAPTCHA que vous n'êtes pas autorisé à passer. Si votre projet nécessite plus que des données d'annonces publiques, la bonne voie est une autorisation ou un accord de données, pas un scraper plus habile. Quand un site offre une API ou un flux de données officiel, préférez-le pour l'accès sous licence ou en masse.

Récapitulatif

Points clés

  • Les pages de recherche Gumtree sont un flux public de petites annonces. Chaque vignette comporte un titre, un prix, un emplacement et un lien d'annonce, ce qui rend les données utiles pour la comparaison de prix et la recherche sur la demande régionale.
  • Vous avez besoin du rendu et d'une IP de confiance ensemble. Gumtree remplit la grille de résultats côté client et challenge le trafic automatisé, donc l'API Crawling rend la page derrière une IP résidentielle en un seul appel.
  • BeautifulSoup fait l'extraction. Bouclez sur les vignettes article[data-q="search-result"] et mappez titre, prix, emplacement et lien depuis leurs attributs data-*, et attendez-vous à ce que ces attributs évoluent.
  • La pagination est un paramètre de requête. Ajoutez &page=N pour parcourir plusieurs pages, arrêtez quand une page ne renvoie aucune vignette, et cadencez les requêtes avec un court délai.
  • Restez sur les données d'annonces publiques. Respectez les Conditions d'utilisation et le robots.txt de Gumtree, et ne collectez jamais les coordonnées personnelles d'un vendeur privé ni rien derrière une connexion.

Foire aux questions

Pourquoi une requête simple ne renvoie-t-elle aucune annonce depuis Gumtree ?

Pour deux raisons. Gumtree remplit une grande partie de la grille de résultats côté client au chargement de la page, donc un simple requests.get obtient souvent une coquille sans les vignettes d'annonces. En plus, Gumtree challenge ou bloque le trafic qui ne ressemble pas à un vrai navigateur. Rendre la page via l'API Crawling derrière une IP de confiance résout les deux, ce qui explique pourquoi le scraper ici route sa requête à travers elle.

Quelles données puis-je scraper depuis une page de recherche Gumtree ?

Depuis chaque vignette de résultat, vous pouvez lire les champs publics que l'annonce affiche : le titre, le prix demandé, l'emplacement et le lien vers l'annonce complète. Ce tutoriel extrait exactement ces quatre champs. Suivre le lien vers la page d'annonce vous donne des champs plus complets comme la description, la date de mise en ligne et les URL des images, mais limitez la collecte au contenu d'annonce public et évitez les coordonnées personnelles d'un vendeur privé.

Comment gérer la pagination sur Gumtree ?

Gumtree pagine les résultats de recherche avec un paramètre de requête ?page=N, donc vous ajoutez &page=2, &page=3, et ainsi de suite à l'URL de recherche et récupérez chacune. La fonction scrape_pages dans ce guide boucle sur une plage de pages, s'arrête tôt quand une page ne renvoie aucune vignette, et ajoute un court délai entre les requêtes pour ne pas marteler le site.

Quels sélecteurs le scraper utilise-t-il ?

Chaque résultat est un article[data-q="search-result"]. À l'intérieur, le titre est div[data-q="tile-title"], le prix est div[data-testid="price"], l'emplacement est div[data-q="tile-location"], et le lien d'annonce est le href sur a[data-q="search-result-anchor"]. Ces attributs data-* sont plus durables que les noms de classes générés, mais réinspectez la page en direct si un champ commence à revenir vide.

Puis-je exporter les données vers un tableur ?

Oui. La fonction export écrit à la fois un fichier JSON et un CSV avec un en-tête title,price,location,link, une ligne par annonce. Ouvrez le CSV directement dans Excel, Google Sheets, ou tout outil de tableur, ou chargez le JSON dans un DataFrame pandas ou un notebook pour l'analyse et la création de graphiques.

Comment éviter d'être bloqué en scrapant Gumtree ?

Maintenez un faible débit de requêtes par IP, ajoutez un délai entre les pages et les recherches, et routez via des IP résidentielles rotatives pour qu'aucune adresse unique ne déclenche une limite de débit. L'API Crawling gère la rotation, un pool d'IP de confiance et la gestion des CAPTCHAs pour vous ; si vous construisez votre propre stack, c'est là qu'il faut investir. Surveillez les codes de statut et reculez quand vous commencez à voir des challenges.

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