Cars and Bids organise des enchères publiques de voitures, et chaque page de vente est un bloc ordonné de données structurées : l'année, la marque et le modèle du véhicule, l'enchère en cours la plus élevée, le temps restant avant la clôture, la localisation du véhicule, et un lien vers la page de détail complète. Pour toute personne qui suit le marché des voitures de passionnés, cette grille d'enchères en direct constitue l'un des signaux publics les plus fiables qui soient, c'est pourquoi les concessionnaires, les chercheurs et les analystes la surveillent pour y détecter des tendances de prix, des signaux de demande et les modèles qui se vendent.

Ce guide vous montre comment scraper Cars and Bids pour récupérer les annonces d'enchères avec Python. Vous construisez un scraper simple et fonctionnel qui récupère une page d'annonces via la Crawling API, analyse un enregistrement propre pour chaque enchère, gère la pagination et exporte les résultats en JSON et CSV. L'ensemble de la démonstration se concentre sur les données d'enchères publiques : les titres, les enchères, le temps restant et les localisations que tout le monde peut voir sur une page d'annonces sans se connecter.

Ce que vous allez construire

Un script Python qui prend une URL de page d'annonces Cars and Bids, récupère la page rendue via la Crawling API et extrait un enregistrement structuré par carte d'enchère. Nous utilisons une page de recherche filtrée par marque comme exemple tout au long de ce tutoriel, la même approche que celle de l'ancien guide, et nous extrayons ces champs de chaque annonce :

  • Titre le titre de l'enchère, qui indique l'année, la marque et le modèle de la voiture.
  • Sous-titre la courte ligne descriptive sous le titre (niveau de finition, options notables, statut de réserve).
  • Enchère actuelle l'offre la plus élevée sur l'enchère au moment du crawl.
  • Temps restant le temps qu'il reste avant la clôture de l'enchère.
  • Localisation la ville et la région où la voiture est listée.
  • Lien l'URL vers la page de détail propre à l'enchère.

Pourquoi une requête simple échoue sur Cars and Bids

Si vous pointez un client HTTP basique vers une URL de page Cars and Bids, vous obtenez rarement les enchères que vous cherchez. Deux obstacles se dressent contre vous. Premièrement, la grille d'annonces est rendue côté client : le site envoie un squelette léger et renseigne les cartes d'enchères au fur et à mesure que le JavaScript de la page s'exécute, de sorte que le HTML initial que vous recevez est souvent un cadre vide sans aucune annonce. Deuxièmement, le trafic automatisé est détecté rapidement. Les plages d'adresses IP de datacenter et les modèles de requêtes qui ne ressemblent pas à un vrai navigateur se heurtent à un défi ou à un blocage direct avant même d'atteindre les enchères.

Un scraper Cars and Bids fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend la page, et une adresse IP que le site considère comme un visiteur réel. Vous pouvez assembler cela vous-même avec un navigateur sans tête et un pool de proxies résidentiels rotatifs, mais maintenir cette infrastructure en bon état représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : vous lui envoyez l'URL de l'annonce, elle rend la page derrière une adresse IP résidentielle de confiance, gère la rotation et la résolution des CAPTCHA, et vous renvoie le HTML terminé à analyser.

Prérequis

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

Bases de Python. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des paquets avec pip. Si vous débutez dans ce langage, la documentation officielle Python ou tout cours pour débutants couvre le niveau requis pour ce tutoriel. Le guide comment scraper un site web avec Python est un bon point de départ si vous en avez besoin.

Python 3.8 ou version ultérieure. Vérifiez 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 le PATH de votre système.

Un compte Crawlbase et un token. Inscrivez-vous pour un compte gratuit, ouvrez votre tableau de bord et copiez votre token. Comme Cars and Bids utilise JavaScript pour charger ses enchères, vous voulez le token JavaScript (JS) plutôt que le token normal. 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 ne le mettez pas dans le 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 la Crawling API, et beautifulsoup4 analyse le HTML retourné pour que vous puissiez extraire chaque champ des cartes d'enchères par sélecteur CSS.

bash
python --version

python -m venv carsandbids-scraper
source carsandbids-scraper/bin/activate

pip install crawlbase beautifulsoup4

Sous Windows, activez l'environnement avec carsandbids-scraper\Scripts\activate à la place de la ligne source. Une fois les deux bibliothèques installées, créez le fichier script que le reste du guide va construire :

bash
touch carsandbids_scraper.py

Comprendre la page d'annonces

Une recherche Cars and Bids est disponible à une URL stable. Une liste filtrée par marque, par exemple, se trouve à https://carsandbids.com/search/bmw, et le même modèle s'applique aux autres marques. La page présente une grille de cartes d'enchères, une par voiture, et chaque carte contient les mêmes quelques champs : le titre de l'enchère (année, marque, modèle), un sous-titre, une vignette, l'enchère en cours, le temps restant, la localisation et un lien vers la page propre à l'enchère.

Avant d'écrire des sélecteurs, ouvrez une page d'annonces dans votre navigateur, faites un clic droit sur une carte d'enchère et choisissez Inspecter. Chaque enchère se trouve dans un élément li marqué avec la classe auction-item. À l'intérieur, le titre se trouve dans un div.auction-title, le sous-titre dans un p.auction-subtitle, la localisation dans un p.auction-loc, la vignette dans un img, et le lien dans l'ancre de la carte. Ce sont les éléments que vous ciblez.

Étape 1 : Récupérer la page d'annonces rendue

Commencez par obtenir la page terminée. Importez la classe CrawlingAPI, initialisez-la avec votre token JS, définissez l'URL de l'annonce et faites la requête. Vérifier le code de statut avant d'analyser permet de rendre les échecs visibles plutôt que silencieux.

python
from crawlbase import CrawlingAPI

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

def make_crawlbase_request(url, options):
    response = crawling_api.get(url, options)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Failed to fetch the page. Crawlbase status: {response['headers']['cb_status']}")
    return None

if __name__ == "__main__":
    listing_url = "https://carsandbids.com/search/bmw"
    options = {"ajax_wait": "true", "page_wait": 10000}
    html = make_crawlbase_request(listing_url, options)
    print(html[:500] if html else "No HTML returned")

Les deux options sont importantes pour une grille qui se remplit après le chargement. ajax_wait indique à l'API d'attendre que le contenu asynchrone soit terminé, et page_wait maintient une pause pour un nombre fixe de millisecondes (10 000 ici) afin que les cartes d'enchères à rendu tardif apparaissent avant que la page soit capturée. La vérification du statut lit l'en-tête cb_status (legacy pc_status) que renvoie la Crawling API, et une valeur de 200 signifie que le rendu a réussi. Exécutez le script et vous devriez voir le vrai balisage de la page d'annonces, non pas un squelette vide. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.

Crawlbase Crawling API

La grille d'annonces Cars and Bids n'apparaît qu'après l'exécution de JavaScript, et un rendu seul ne suffit pas si la requête provient d'une IP en laquelle le site n'a pas confiance. La Crawling API prend votre token, exécute la page dans un vrai navigateur avec les options ajax_wait et page_wait que vous venez de définir, effectue une rotation entre des IP résidentielles côté serveur et gère la résolution des CAPTCHA, puis vous remet le HTML terminé. Vous évitez de devoir exploiter vous-même une flotte de navigateurs sans tête et un pool de proxies. Essayez-la sur le niveau gratuit de 20 000 requêtes d'abord.

Étape 2 : Analyser les cartes d'enchères avec BeautifulSoup

Avec le HTML rendu en main, chargez-le dans BeautifulSoup, trouvez chaque carte d'enchère et extrayez chaque champ par son sélecteur. Chaque carte est un li.auction-item ; le titre, le sous-titre, la localisation, l'enchère actuelle, le temps restant, la vignette et le lien se trouvent tous à l'intérieur. Protéger chaque recherche par une vérification de présence rend l'extraction résiliente quand un champ manque, ce qui arrive car toutes les cartes n'affichent pas les mêmes données.

python
from bs4 import BeautifulSoup

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

def text_of(listing, tag, css_class):
    el = listing.find(tag, class_=css_class)
    return el.text.strip() if el else None

def scrape_listing_page(html_content):
    soup = BeautifulSoup(html_content, "html.parser")
    car_listings = soup.find_all("li", class_="auction-item")

    extracted_data = []
    for listing in car_listings:
        link_tag = listing.find("a")
        thumbnail = listing.find("img")
        extracted_data.append({
            "title": text_of(listing, "div", "auction-title"),
            "sub_title": text_of(listing, "p", "auction-subtitle"),
            "current_bid": text_of(listing, "span", "bid-value"),
            "time_left": text_of(listing, "span", "td-time"),
            "location": text_of(listing, "p", "auction-loc"),
            "thumbnail": thumbnail["src"] if thumbnail else None,
            "link": BASE + link_tag["href"] if link_tag else None,
        })
    return extracted_data

Le helper text_of interroge un élément à l'intérieur d'une carte et renvoie None quand il est absent, plutôt que de lever une exception sur un appel .text contre rien. Le titre vient de div.auction-title et contient l'année, la marque et le modèle ; le sous-titre vient de p.auction-subtitle ; la localisation de p.auction-loc ; l'enchère actuelle du span de valeur d'enchère ; et le temps restant du span d'affichage du temps. Le lien est construit en préfixant le href relatif de la carte avec la base du site afin de stocker une URL absolue.

Selectors drift

Le balisage du site change sans préavis, et les noms de classe des cartes d'enchères ci-dessus sont un modèle de départ, pas un contrat. Le conteneur li.auction-item et les classes auction-title / auction-subtitle / auction-loc sont les ancres durables ; les spans de l'enchère et du temps restant sont les plus susceptibles d'évoluer. Quand un champ revient comme None pour chaque carte, ré-inspectez une annonce en direct dans les outils de développement de votre navigateur et mettez à jour le sélecteur. La maintenance périodique des sélecteurs est normale pour tout scraper en production.

Étape 3 : Assembler le script et exporter en JSON et CSV

Maintenant, reliez la récupération et l'analyse en un script fonctionnel, puis écrivez les enregistrements en JSON et CSV pour pouvoir les charger dans un notebook ou un tableur. Récupérez la page d'annonces rendue, transmettez-la à l'analyseur et exportez les lignes structurées.

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

crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})
BASE = "https://www.carsandbids.com"
FIELDS = ["title", "sub_title", "current_bid", "time_left", "location", "thumbnail", "link"]

def make_crawlbase_request(url, options):
    response = crawling_api.get(url, options)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Failed to fetch the page. Crawlbase status: {response['headers']['cb_status']}")
    return None

def text_of(listing, tag, css_class):
    el = listing.find(tag, class_=css_class)
    return el.text.strip() if el else None

def scrape_listing_page(html_content):
    soup = BeautifulSoup(html_content, "html.parser")
    car_listings = soup.find_all("li", class_="auction-item")

    extracted_data = []
    for listing in car_listings:
        link_tag = listing.find("a")
        thumbnail = listing.find("img")
        extracted_data.append({
            "title": text_of(listing, "div", "auction-title"),
            "sub_title": text_of(listing, "p", "auction-subtitle"),
            "current_bid": text_of(listing, "span", "bid-value"),
            "time_left": text_of(listing, "span", "td-time"),
            "location": text_of(listing, "p", "auction-loc"),
            "thumbnail": thumbnail["src"] if thumbnail else None,
            "link": BASE + link_tag["href"] if link_tag else None,
        })
    return extracted_data

def export(rows, name="carsandbids_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)} auctions to {name}.json and {name}.csv")

def main():
    url = "https://carsandbids.com/search/bmw"
    options = {"ajax_wait": "true", "page_wait": 10000}
    html = make_crawlbase_request(url, options)
    if not html:
        return
    rows = scrape_listing_page(html)
    export(rows)

if __name__ == "__main__":
    main()

Exécutez le script complet avec python carsandbids_scraper.py. Il récupère la page d'annonces rendue, analyse une ligne par enchère et écrit à la fois carsandbids_listings.json et carsandbids_listings.csv. La liste FIELDS partagée maintient l'ordre des colonnes CSV en accord avec les clés du dictionnaire, de sorte que les deux exports ne divergent jamais.

À quoi ressemble la sortie

Vous obtenez une liste propre d'enregistrements d'enchères, dans l'ordre de la page, prête à être écrite en JSON, CSV ou dans une base de données.

json
[
  {
    "title": "2014 BMW 335i Sedan",
    "sub_title": "No Reserve: Turbo 6-Cylinder, M Sport Package, California-Owned",
    "current_bid": "$9,500",
    "time_left": "2 days",
    "location": "Los Angeles, CA 90068",
    "thumbnail": "https://media.carsandbids.com/cdn-cgi/image/width=768/photos/rkVPlNqQ.jpg",
    "link": "https://www.carsandbids.com/auctions/9QxJ8nV7/2014-bmw-335i-sedan"
  },
  {
    "title": "2009 BMW 328i Sports Wagon",
    "sub_title": "No Reserve: Inspected 3.0-Liter 6-Cylinder, Premium Package",
    "current_bid": "$12,750",
    "time_left": "5 hours",
    "location": "San Diego, CA 92120",
    "thumbnail": "https://media.carsandbids.com/cdn-cgi/image/width=768/photos/3g6kOmG9.jpg",
    "link": "https://www.carsandbids.com/auctions/30n7Yqaj/2009-bmw-328i-sports-wagon"
  }
]

Gérer la pagination

Une page de recherche est une démonstration ; un vrai travail de recherche s'étend sur toutes les pages de résultats. Cars and Bids pagine ses résultats avec un paramètre ?page=, vous pouvez donc parcourir les pages en incrémentant le nombre jusqu'à ce qu'une page ne retourne plus aucune carte d'enchère, puis s'arrêter. Rythmer les requêtes avec un court délai permet de ne pas surcharger le site dans une boucle rapide.

python
import time

def scrape_all_pages(search_url, max_pages=10):
    options = {"ajax_wait": "true", "page_wait": 10000}
    all_rows = []
    for page in range(1, max_pages + 1):
        page_url = f"{search_url}?page={page}"
        html = make_crawlbase_request(page_url, options)
        if not html:
            break
        found = scrape_listing_page(html)
        if not found:
            print(f"No auctions on page {page}; stopping.")
            break
        all_rows.extend(found)
        print(f"Page {page}: {len(found)} auctions")
        time.sleep(2)
    return all_rows

La sortie sur résultats vides vous arrête tôt quand la recherche est épuisée, et le time.sleep(2) entre les requêtes rythme l'exécution pour que vous ne soyez pas signalé pour un trafic en rafale. Remplacez la récupération unique dans main par un appel à scrape_all_pages("https://carsandbids.com/search/bmw") et le reste du pipeline (analyse, export) transporte la liste combinée directement. Pour suivre une enchère dans le temps, exécutez la tâche selon un calendrier et horodatez chaque export, puis comparez les instantanés successifs pour voir comment les enchères et le temps restant ont évolué.

Rester non bloqué

Même avec le rendu géré, le site surveille le trafic qui ressemble à celui d'un scraper. Quelques habitudes maintiennent une exécution en bonne santé, et elles s'appliquent à toute cible difficile.

  • Rythmez vos requêtes. Étalez les requêtes avec un délai entre les pages plutôt que de tout crawler à pleine vitesse, et planifiez les tâches plus lourdes pendant les heures creuses pour alléger la charge sur le site.
  • Misez sur la rotation. Un pool d'adresses IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels afin qu'aucune ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre infrastructure, c'est la partie à bien maîtriser.
  • Ne conservez que ce dont vous avez besoin. Stockez les champs d'enchère que votre projet utilise et écartez le reste, et vérifiez périodiquement vos sélecteurs pour que le scraper suive les évolutions du balisage.

Pour le manuel général sur la façon d'éviter les blocages, consultez comment scraper des sites web sans être bloqué, et pour en savoir plus sur l'importance du rendu ici, comment crawler des sites web JavaScript. Si vous collectez ces données pour du travail de tarification, le guide sur le web scraping pour l'intelligence tarifaire explique comment transformer des annonces brutes en signal utilisable.

Est-il légal de scraper Cars and Bids ?

Que le scraping de Cars and Bids soit autorisé dépend des conditions d'utilisation du site, de votre juridiction et de ce que vous faites avec les données. Les conditions du site régissent l'accès automatisé, de sorte que le scraping peut aller à l'encontre de ces conditions quelle que soit la prudence de votre outillage. Aucun code ici ne change cela ; il fait simplement fonctionner la partie technique. Lisez les conditions d'utilisation de Cars and Bids et son robots.txt, et traitez les deux comme la limite de ce que vous collectez. Pour une utilisation commerciale ou concurrentielle, le tableau juridique devient plus complexe, et consulter un expert juridique sur votre cas spécifique est la démarche raisonnable.

Quelques lignes à tenir. Ne collectez que les données d'enchères publiques : les titres, sous-titres, enchères actuelles, temps restants, localisations et liens d'annonces que tout le monde peut voir sur une page de recherche sans compte. Maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger les serveurs du site, et évitez les données personnelles, y compris tout ce qui est lié à des vendeurs, enchérisseurs ou commentateurs identifiables au-delà de ce qui est listé publiquement. Ne redistribuez pas les photos ou descriptions d'annonces comme si elles vous appartenaient, car ces médias sont protégés par le droit d'auteur.

Ce guide est délibérément limité aux pages d'annonces publiques car c'est la ligne qui rend le travail défendable. Il ne couvre rien derrière une connexion, les données de compte ou d'enchère, les informations personnelles, ni toute tentative de contourner une authentification ou un CAPTCHA que vous n'êtes pas autorisé à passer. Si votre projet a besoin de plus que des données d'annonces publiques, la bonne voie est un accord officiel de données avec le site plutôt qu'un scraper plus ingénieux.

Récapitulatif

Points clés

  • Les annonces Cars and Bids sont un signal d'enchères en direct. Chaque page de recherche affiche le titre actuel, l'enchère, le temps restant et la localisation de chaque voiture, ce qui la rend si utile pour la recherche de marché et la tarification.
  • Vous avez besoin du rendu et d'une IP de confiance ensemble. La grille d'annonces se charge côté client et le trafic des bots est bloqué, donc la Crawling API rend la page derrière une IP résidentielle en un seul appel avec ajax_wait et page_wait définis.
  • BeautifulSoup effectue l'extraction. Parcourez les cartes li.auction-item et associez titre, sous-titre, enchère actuelle, temps restant, localisation et lien aux sélecteurs actuels, et attendez-vous à ce que ces sélecteurs évoluent.
  • Parcourez les pages et exportez. Incrémentez le paramètre ?page= jusqu'à ce qu'une page ne retourne plus de cartes, puis écrivez les lignes combinées en JSON et CSV avec une liste de champs partagée pour garder les deux fichiers synchronisés.
  • Restez sur les données publiques. Respectez les conditions d'utilisation et le robots.txt du site, maintenez le volume des requêtes modeste et ne touchez jamais aux comptes, aux enchères, aux données personnelles ou aux médias protégés par le droit d'auteur que vous redistribueriez.

Foire aux questions

Pourquoi une requête simple ne retourne-t-elle aucune enchère depuis Cars and Bids ?

La grille d'annonces se rend côté client : le site envoie un squelette quasi-vide et remplit les cartes d'enchères au fur et à mesure que son JavaScript s'exécute, de sorte qu'une requête brute obtient souvent un cadre sans aucune annonce. De plus, le site challenge ou bloque le trafic qui ne ressemble pas à un vrai navigateur. Rendre la page via la Crawling API derrière une IP de confiance, avec ajax_wait et page_wait définis, résout les deux problèmes, c'est pourquoi le scraper ici achemine sa requête à travers elle.

Quel token Crawlbase dois-je utiliser pour Cars and Bids ?

Utilisez le token JavaScript (JS). Cars and Bids charge ses enchères de manière dynamique, il a donc besoin du rendu qu'active le token JS ; le token normal renvoie le squelette non rendu. Le niveau gratuit inclut jusqu'à 20 000 requêtes sans carte bancaire, ce qui est suffisant pour construire et tester le scraper.

Comment scraper une marque ou une recherche spécifique sur Cars and Bids ?

Pointez le scraper sur l'URL de recherche que vous voulez. Un filtre par marque est simplement un chemin, par exemple https://carsandbids.com/search/bmw pour BMW, donc remplacer la marque à la fin de l'URL cible un ensemble d'enchères différent. Pour couvrir l'ensemble complet des résultats, parcourez le paramètre ?page= jusqu'à ce qu'une page ne retourne plus de cartes.

Quels champs puis-je extraire d'une annonce Cars and Bids ?

De chaque carte d'enchère, vous pouvez extraire le titre (année, marque, modèle), le sous-titre, l'enchère actuelle, le temps restant, la localisation, l'image vignette et le lien vers la page de détail de l'enchère. L'analyseur mappe chacun d'eux par son sélecteur CSS, et vous pouvez supprimer les champs dont vous n'avez pas besoin du dictionnaire de sortie.

Comment gérer la pagination sur de nombreuses enchères ?

Cars and Bids pagine ses résultats avec un paramètre ?page=. Bouclez sur le numéro de page, récupérez et analysez chaque page, et arrêtez-vous quand une page ne retourne plus de cartes d'enchères. Ajoutez un court délai entre les requêtes pour rythmer l'exécution plutôt que d'envoyer les pages en rafale, et collectez les lignes dans une seule liste avant d'exporter.

Comment éviter d'être bloqué lors du scraping de Cars and Bids ?

Maintenez un faible taux de requêtes par IP, ajoutez un délai entre les pages et routez via des IP résidentielles rotatives afin qu'aucune adresse unique ne déclenche une limite de débit. La Crawling API gère la rotation, un pool d'IP de confiance et la gestion des CAPTCHA pour vous ; si vous construisez votre propre infrastructure, c'est la partie dans laquelle investir. Surveillez les valeurs cb_status 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