Clutch.co répertorie plus de 150 000 prestataires de services dans l'informatique, le marketing, le design et le développement, et chaque profil d'entreprise porte le type de données B2B structurées qu'un pipeline de génération de leads, un tableau de bord de veille concurrentielle ou une étude de marché recherche réellement : un nom d'entreprise, une note en étoiles, un nombre d'avis, une taille de projet minimale, une fourchette de tarif horaire, une localisation et un lien vers le profil complet. L'inconvénient est que Clutch se trouve derrière une lourde protection anti-bot, si bien qu'une simple requête HTTP n'atteint que rarement la liste.
Ce guide vous montre comment construire un scraper Clutch.co en Python de la bonne manière. Vous construisez un petit script exécutable qui récupère une page de catégorie rendue via la Crawling API, analyse chaque carte d'entreprise avec BeautifulSoup et écrit des lignes structurées propres. L'ensemble du guide reste limité aux données de listes d'entreprises publiques, et la section sur la légalité vers la fin n'est pas un texte standard, alors lisez-la avant de pointer ceci sur un volume réel.
Ce que vous allez construire
Un script Python qui prend une URL publique de catégorie Clutch.co, récupère le HTML rendu via la Crawling API et extrait un enregistrement structuré pour chaque entreprise de la page. Nous utiliserons le répertoire des services informatiques comme exemple courant et extrairons ces champs de chaque carte :
- Nom de l'entreprise la raison sociale répertoriée du prestataire.
- Note le score agrégé en étoiles affiché sur la carte.
- Nombre d'avis combien d'avis clients soutiennent cette note.
- Taille de projet minimale le plus petit engagement que le prestataire accepte, par exemple "$5,000+".
- Tarif horaire la fourchette de tarif horaire répertoriée, par exemple "$50 - $99 / hr".
- Localisation la ville ou la région principale du prestataire.
- URL du profil le lien vers le profil Clutch complet de l'entreprise.
Pourquoi une simple requête échoue sur Clutch.co
Si vous demandez une URL de catégorie Clutch.co avec un client HTTP nu, vous ne récupérez généralement aucune liste du tout. Clutch exploite une protection anti-bot agressive, et une IP de centre de données qui fait une requête manifestement automatisée est défiée ou reçoit un 403 avant qu'aucune donnée d'entreprise ne vous parvienne. Même quand une requête passe, des parties de la page se remplissent côté client, donc le HTML brut peut manquer précisément les cartes que vous êtes venu chercher.
Un scraper Clutch fonctionnel a donc besoin de deux choses dans une seule requête : un navigateur qui rend réellement la page, et une IP que la plateforme interprète comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur headless plus un pool de proxys résidentiels rotatifs, mais les coudre ensemble et les maintenir en bonne santé représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : vous lui envoyez l'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.
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 et achemine la requête à travers des IP résidentielles rotatives. Clutch est bien défendu et partiellement rendu côté client, vous voulez donc le token JS ici. Vous obtenez jusqu'à 20 000 requêtes gratuites pour vous lancer, sans carte de crédit requise.
Prérequis
Il vous faut quelques éléments en place avant d'écrire le moindre code. Aucun ne prend longtemps.
Python de base. Vous devez être à l'aise pour écrire et lancer un script Python et installer des paquets avec pip. Si BeautifulSoup est nouveau pour vous, notre guide sur l'utilisation de BeautifulSoup en Python couvre les bases de l'analyse que ce tutoriel suppose.
Python 3.8 ou ultérieur. Vérifiez votre version avec python --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 JS. Inscrivez-vous, ouvrez votre dashboard et copiez votre token JavaScript (JS). Traitez le token comme un mot de passe : il authentifie vos requêtes, alors gardez-le hors du contrôle de version.
Mettre en place le projet
Créez un environnement virtuel pour que les dépendances du projet restent isolées, puis installez les bibliothèques dont le scraper a besoin.
python --version python -m venv clutch_env source clutch_env/bin/activate pip install crawlbase beautifulsoup4 pandas
Sur Windows, activez l'environnement avec clutch_env\Scripts\activate au lieu de la ligne source. Trois dépendances font le travail : crawlbase est le client officiel de la Crawling API, beautifulsoup4 analyse le HTML renvoyé pour que vous puissiez extraire les champs par sélecteur CSS, et pandas transforme les lignes collectées en un CSV à la fin.
Étape 1 : Récupérer la page de catégorie rendue
Commencez par obtenir la page finie. Importez la classe CrawlingAPI, initialisez-la avec votre token JS et demandez l'URL de la catégorie. Vérifier le statut avant d'analyser garde les échecs bruyants plutôt que silencieux.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def fetch_html(url): response = api.get(url, {"ajax_wait": "true", "page_wait": 5000}) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None if __name__ == "__main__": base_url = "https://clutch.co/it-services" html = fetch_html(base_url) print(html[:500] if html else "No HTML returned")
Les deux options d'attente comptent pour une cible défendue et partiellement rendue côté client comme celle-ci. 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 cartes à rendu tardif apparaissent avant que la page ne soit capturée. Cinq secondes sont un bon départ ; augmentez-le si les cartes reviennent vides. Lancez le script avec python scraper.py et vous devriez voir un véritable balisage de prestataire, et non la page 403 qu'un simple requests.get renvoie. Cela confirme que la requête passe avant d'écrire le moindre sélecteur.
Clutch.co répond à une requête nue par un 403, vous avez donc besoin d'une page 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 le répertoire public des services informatiques sur le palier gratuit.
Étape 2 : Analyser les cartes d'entreprise avec BeautifulSoup
Avec le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez chaque prestataire par son sélecteur. Clutch dispose ses listes dans une structure répétée : chaque prestataire est un li.provider à l'intérieur de ul.providers__list, alors vous sélectionnez toutes les cartes une fois puis lisez les mêmes champs dans chacune. Inspectez la page en direct dans les outils de développement de votre navigateur (généralement F12) pour confirmer les noms de classes actuels ; les sélecteurs ci-dessous correspondent à la mise en page au moment de la rédaction.
import re from bs4 import BeautifulSoup def text_of(card, selector): el = card.select_one(selector) return re.sub(r"\s+", " ", el.get_text(strip=True)) if el else "N/A" def parse_html(html): soup = BeautifulSoup(html, "html.parser") data = [] companies = soup.select("ul.providers__list > li.provider") for company in companies: profile = company.select_one("h3.provider__title a") profile_url = profile["href"] if profile else "N/A" data.append({ "Company Name": text_of(company, "h3.provider__title"), "Rating": text_of(company, "span.sg-rating__number"), "Number of Reviews": text_of(company, "a.sg-rating__reviews"), "Min Project Size": text_of(company, "li.provider__highlights-item.min-project-size span"), "Hourly Rate": text_of(company, "li.provider__highlights-item.hourly-rate span"), "Location": text_of(company, "li.provider__highlights-item.location span.locality"), "Profile URL": profile_url, }) return data
L'utilitaire text_of fait deux choses utiles à la fois : il renvoie "N/A" quand un élément est manquant, au lieu de lever une erreur sur un appel .get_text() contre rien, et il réduit les suites d'espaces avec re.sub(r"\s+", " ", ...) pour qu'un nombre d'avis comme "\n 128 reviews " revienne propre. Cela garde l'extraction résiliente quand un champ est absent, ce qui est courant puisque tous les prestataires ne répertorient pas une taille de projet minimale ou un tarif horaire. L'URL du profil est lue depuis l'attribut href de l'ancre plutôt que depuis son texte, elle est donc gérée séparément.
Les noms de classes de Clutch changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand un champ revient à "N/A" pour chaque carte, ré-inspectez une liste 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, et non le signe que quelque chose est cassé.
Étape 3 : Gérer la pagination
Clutch répertorie les prestataires sur de nombreuses pages, et il utilise un paramètre de requête page pour passer de l'une à l'autre. Pour collecter un répertoire entier, vous parcourez les pages dans une boucle, récupérez chacune via la même fonction et rassemblez les lignes. Comme chaque page partage la même structure de carte, le parseur que vous avez déjà écrit fonctionne sur toutes sans modification.
import time def scrape_clutch_data(base_url, pages): all_data = [] for page in range(1, pages + 1): url = f"{base_url}?page={page}" html = fetch_html(url) if html: all_data.extend(parse_html(html)) time.sleep(3) return all_data
Le time.sleep(3) entre les requêtes est délibéré. Le rythme vous évite de marteler Clutch dans une boucle serrée, ce qui est le moyen le plus rapide de se faire limiter même quand chaque requête est rendue à travers une IP de confiance. Commencez avec une poignée de pages pendant que vous confirmez que les sélecteurs tiennent, puis augmentez le nombre une fois que la sortie a l'air correcte.
Étape 4 : Tout assembler et enregistrer en CSV
Reliez maintenant la récupération, l'analyse et la boucle de pagination en un seul script exécutable, puis confiez les lignes collectées à pandas pour écrire un CSV. Un CSV plat est la sortie la plus portable pour les données B2B : il s'ouvre dans n'importe quel tableur, se charge dans une base de données et alimente un import CRM sans travail supplémentaire.
import re import time import pandas as pd from bs4 import BeautifulSoup from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def fetch_html(url): response = api.get(url, {"ajax_wait": "true", "page_wait": 5000}) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None def text_of(card, selector): el = card.select_one(selector) return re.sub(r"\s+", " ", el.get_text(strip=True)) if el else "N/A" def parse_html(html): soup = BeautifulSoup(html, "html.parser") data = [] for company in soup.select("ul.providers__list > li.provider"): profile = company.select_one("h3.provider__title a") data.append({ "Company Name": text_of(company, "h3.provider__title"), "Rating": text_of(company, "span.sg-rating__number"), "Number of Reviews": text_of(company, "a.sg-rating__reviews"), "Min Project Size": text_of(company, "li.provider__highlights-item.min-project-size span"), "Hourly Rate": text_of(company, "li.provider__highlights-item.hourly-rate span"), "Location": text_of(company, "li.provider__highlights-item.location span.locality"), "Profile URL": profile["href"] if profile else "N/A", }) return data def scrape_clutch_data(base_url, pages): all_data = [] for page in range(1, pages + 1): html = fetch_html(f"{base_url}?page={page}") if html: all_data.extend(parse_html(html)) time.sleep(3) return all_data def main(): base_url = "https://clutch.co/it-services" data = scrape_clutch_data(base_url, pages=5) df = pd.DataFrame(data) df.to_csv("clutch_data.csv", index=False) print(f"Saved {len(data)} companies to clutch_data.csv") if __name__ == "__main__": main()
Lancez le script complet avec python scraper.py et il parcourt cinq pages du répertoire des services informatiques, analyse chaque prestataire sur chacune et écrit un seul CSV. Remplacez base_url par n'importe quelle autre catégorie publique, comme https://clutch.co/agencies/digital, et changez pages pour contrôler la profondeur d'exploration.
À quoi ressemble la sortie
Chaque ligne est un enregistrement structuré propre, prêt à ouvrir dans un tableur, à charger dans une base de données ou à alimenter un CRM. Voici un échantillon des données que le script produit, présenté en JSON pour la lisibilité.
[ { "Company Name": "Lorem Software Group", "Rating": "4.9", "Number of Reviews": "128 reviews", "Min Project Size": "$25,000+", "Hourly Rate": "$50 - $99 / hr", "Location": "Austin, TX", "Profile URL": "https://clutch.co/profile/lorem-software-group" }, { "Company Name": "Ipsum Digital Labs", "Rating": "4.7", "Number of Reviews": "54 reviews", "Min Project Size": "$10,000+", "Hourly Rate": "$100 - $149 / hr", "Location": "London, England", "Profile URL": "https://clutch.co/profile/ipsum-digital-labs" } ]
Passer à l'échelle sur plusieurs catégories et rester débloqué
Un seul répertoire est une démo ; un vrai travail s'exécute sur de nombreuses catégories. La forme reste la même : gardez une liste d'URL de catégories, exécutez scrape_clutch_data sur chacune, et concaténez les lignes avant d'écrire le CSV. Le parseur fonctionne sur toutes sans modification. Clutch est cependant une cible commerciale difficile, donc quelques habitudes gardent une longue exécution saine.
- Rythmez vos requêtes. Marteler les pages dans une boucle serrée est le moyen le plus rapide de se faire limiter. Gardez le sleep entre les requêtes et étalez un gros travail dans le temps plutôt que de crawler tout un répertoire à pleine vitesse.
- 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 de débit. La Crawling API s'en charge pour vous ; si vous montez votre propre pile, c'est la partie à bien faire.
-
Lisez les codes de statut. Une exécution qui se met à renvoyer
403ou d'autres défis vous dit que le rythme actuel est trop agressif. Traitez cela comme un signal pour réduire la cadence, pas comme un bruit à ignorer.
Pour le guide plus large, voyez comment scraper des sites web sans se faire bloquer et l'analyse plus approfondie sur comment contourner les captchas pendant le web scraping. Comme Clutch rend des parties de ses pages côté client, notre guide sur le scraping des pages JavaScript avec Python explique pourquoi le rendu compte. Et si vous préférez acheminer votre propre trafic à travers un pool rotatif plutôt que d'utiliser l'API managée, le Smart AI Proxy (aussi appelé AI Proxy) vous offre la même rotation d'IP résidentielles sous la forme d'un endpoint proxy prêt à l'emploi.
Est-il légal de scraper Clutch.co ?
Que le scraping de Clutch.co soit autorisé ou non dépend des conditions d'utilisation de Clutch, de votre juridiction et de ce que vous faites des données. Les conditions de Clutch placent des limites sur l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quel que soit le soin apporté à votre outillage. Rien dans le code ici n'y change quoi que ce soit ; cela ne fait que faire fonctionner la partie technique. Lisez les conditions d'utilisation de Clutch et son robots.txt, et traitez les deux comme la limite de ce que vous collectez. Clutch ne publie pas d'API publique et ouverte pour son répertoire, il n'y a donc pas d'endpoint autorisé à préférer à la page ; cela rend le respect des limites énoncées plus important, et non moins.
Quelques lignes à respecter. Ne collectez que des données de listes d'entreprises publiques : le nom de l'entreprise, la note, le nombre d'avis, la taille de projet minimale, le tarif horaire, la localisation et le lien du profil que n'importe qui peut voir sur une page de catégorie sans compte. Gardez votre volume de requêtes modeste pour ne pas surcharger les serveurs de Clutch, et rythmez l'exécution plutôt que de tirer un répertoire entier d'un coup. Si vous prévoyez de réutiliser les données commercialement, pour de la prospection, de la revente ou un produit, obtenez une permission ou un accord officiel plutôt que de supposer que le silence vaut consentement.
Ce guide est délibérément limité aux pages publiques de répertoire et de catégorie parce que c'est la ligne qui garde le travail défendable. Il ne couvre rien de ce qui est derrière une connexion, ni les données personnelles des évaluateurs, ni les coordonnées qui ne sont pas répertoriées publiquement, ni le texte d'avis protégé par le droit d'auteur que vous redistribueriez comme le vôtre. Uniquement des données de listes d'entreprises publiques. Si votre projet a besoin de plus que cela, un partenariat de données avec Clutch est la bonne voie, pas un scraper plus astucieux.
Points clés
-
Clutch bloque les requêtes simples. Un
requests.getnu renvoie généralement un 403, vous devez donc rendre la page derrière une IP de confiance avant de pouvoir l'analyser. -
Utilisez le token JS via la Crawling API. Un seul appel rend la page dans un vrai navigateur et fait tourner des IP résidentielles ;
ajax_waitetpage_waitcontrôlent combien de temps il attend le contenu. -
BeautifulSoup fait l'extraction. Sélectionnez chaque
li.providerdansul.providers__list, puis lisez le nom de l'entreprise, la note, les avis, la taille de projet minimale, le tarif horaire, la localisation et l'URL du profil, et attendez-vous à ce que les sélecteurs dérivent. -
Paginez avec le paramètre page. Clutch parcourt les pages via
?page=N, donc un vrai travail boucle sur les pages, réutilise le même parseur et patiente entre les requêtes. - Restez sur les données publiques. Respectez les CGU et le robots.txt de Clutch, gardez un volume modeste et obtenez une permission avant toute réutilisation commerciale.
Foire aux questions
Pourquoi une simple requête vers Clutch.co renvoie-t-elle un 403 ?
Clutch exploite une protection anti-bot agressive. Une IP de centre de données qui fait une requête manifestement automatisée est défiée ou bloquée avec un 403 avant qu'aucune donnée de liste ne vous parvienne. Pour obtenir de vraies données, il vous faut une requête qui rend la page et provient d'une IP que la plateforme interprète comme un vrai visiteur, ce que le token JS de la Crawling API gère pour vous.
Ai-je besoin du token normal ou du token JS pour Clutch.co ?
Le token JS. Le token normal récupère le HTML statique et n'exécute pas de navigateur, donc sur un site défendu et partiellement rendu côté client comme Clutch, il a tendance à revenir vide ou bloqué. Le token JS rend la page dans un vrai navigateur et achemine via des IP résidentielles rotatives avant de renvoyer le HTML, donc les cartes d'entreprise sont présentes quand BeautifulSoup les analyse.
Quelles données puis-je scraper d'une liste Clutch.co ?
Depuis une page de catégorie publique, vous pouvez lire pour chaque prestataire le nom de l'entreprise, la note agrégée, le nombre d'avis, la taille de projet minimale, la fourchette de tarif horaire, la localisation et le lien vers son profil complet. Ce guide extrait exactement ces champs. Tout ce qui est derrière une connexion, les données personnelles des évaluateurs ou les coordonnées privées est hors du périmètre et hors limites.
Comment scraper plusieurs pages de résultats Clutch.co ?
Clutch pagine avec un paramètre de requête page, vous construisez donc chaque URL comme f"{base_url}?page={page}" et bouclez sur les numéros de page. Récupérez chaque page via la même fonction, exécutez le même parseur et collectez les lignes. Ajoutez un court time.sleep entre les requêtes pour ne pas marteler le site, et augmentez le nombre de pages une fois que la sortie a l'air correcte.
Mes sélecteurs renvoient "N/A" pour chaque carte. Qu'est-ce qui a changé ?
Presque à coup sûr le balisage de Clutch. Ses noms de classes changent sans préavis, donc des sélecteurs qui fonctionnaient le mois dernier peuvent casser. Ré-inspectez une liste en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs, par exemple h3.provider__title ou span.sg-rating__number. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Comment éviter de se faire bloquer en scrapant Clutch.co ?
Gardez votre débit de requêtes par IP faible, rythmez l'exécution avec un sleep entre les requêtes, et acheminez via des IP résidentielles rotatives pour qu'aucune adresse seule ne déclenche une limite de débit. La Crawling API gère la rotation et un pool d'IP de confiance pour vous ; si vous construisez votre propre pile, c'est la partie dans laquelle investir. Surveillez les codes de statut et réduisez la cadence quand vous commencez à voir des réponses 403 ou d'autres 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.
