GoodFirms est un annuaire B2B qui met en relation acheteurs et prestataires de services IT, éditeurs de logiciels et agences. Chaque fiche publique contient les champs structurés qui alimentent la veille concurrentielle, le dimensionnement de marché et la découverte de partenaires : nom de l'entreprise, note, services proposés, localisation, tranche tarifaire horaire et lien vers le profil complet. Pour quiconque cartographie un secteur vertical ou constitue une shortlist de fournisseurs, ces données d'annuaire public sont la matière première, et les collecter manuellement sur des dizaines d'agences est lent et source d'erreurs.

Ce guide vous montre comment scraper GoodFirms avec Python de manière fiable. Vous construirez un petit scraper fonctionnel qui récupère des pages GoodFirms rendues via la Crawling API, collecte des fiches d'entreprises depuis une liste de catégorie, analyse les champs avec BeautifulSoup, gère la pagination et exporte du JSON et du CSV propres. L'ensemble du tutoriel se limite aux données d'annonces commerciales publiques, et la section sur la légalité à la fin n'est pas un simple formulaire, alors lisez-la avant de l'utiliser à grande échelle.

Ce que vous allez construire

Un script Python qui prend une URL de catégorie GoodFirms publique, parcourt les listes de recherche paginées, extrait un enregistrement structuré par entreprise, puis examine les profils individuels pour les champs plus détaillés. L'exemple fil rouge porte sur les agences de développement web à Londres. Nous extrayons ces champs :

  • Nom de l'entreprise le nom commercial listé sur la carte de l'annuaire.
  • Note le score de revue public sur la liste.
  • Services la catégorie de service ou le slogan sous lequel l'entreprise est listée.
  • Localisation la ville et le pays affichés sur la carte.
  • Taux horaire la tranche tarifaire depuis le profil de l'entreprise.
  • URL du profil le lien canonique vers la page de profil complète.

Pourquoi une simple requête échoue sur GoodFirms

Demandez une URL de catégorie ou de profil GoodFirms avec un client HTTP basique et vous obtenez souvent le statut 200 avec seulement une fraction des données de liste dans le corps. Deux facteurs jouent contre vous. D'abord, GoodFirms charge une grande partie de sa grille d'annuaire et des détails de profil dans le navigateur avec JavaScript, donc le HTML initial est une coquille légère qui se remplit seulement après l'exécution des scripts de la page. Extraire les cartes d'entreprises de cette première réponse peut vous donner un ensemble partiel ou manquer des champs à rendu tardif. Ensuite, un annuaire B2B actif surveille le trafic automatisé : les IP de datacenter et les patterns de requêtes non-navigateur sont limités, bloqués ou challengés avant d'atteindre le contenu rendu.

Un scraper GoodFirms fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend la page, et une IP que la plateforme considère comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur headless et des proxies résidentiels rotatifs, mais les maintenir en bonne santé représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : envoyez-lui l'URL avec un token JavaScript, elle rend la page derrière une IP de confiance, et renvoie le HTML finalisé à analyser. Pour le contexte sur la raison pour laquelle le rendu côté client casse les scrapers naïfs, le guide pour crawler les sites web JavaScript le couvre en profondeur.

Pourquoi le token JS

Crawlbase propose deux types de token. Le token normal récupère le HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. Comme GoodFirms remplit certaines parties de son annuaire et de ses pages de profil côté client, le token JS est le choix sûr par défaut ici : il renvoie le balisage finalisé plutôt que la coquille légère qu'une simple requête donnerait, il y a donc quelque chose d'utile pour BeautifulSoup à analyser.

Prérequis

Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.

Python de base. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des paquets avec pip. Si vous débutez avec le côté analyse, le guide BeautifulSoup est un bon complément à ce tutoriel, et le tutoriel plus large scraper un site web avec Python couvre les fondamentaux.

Python 3.8 ou version ultérieure. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda, et assurez-vous que Python est dans votre PATH.

Un compte Crawlbase et un token JS. Inscrivez-vous, ouvrez votre tableau de bord, et copiez votre token JavaScript (JS) depuis la page de documentation du compte. Crawlbase inclut jusqu'à 20 000 requêtes gratuites pour commencer, largement suffisant pour travailler sur ce guide. 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 environnement virtuel pour garder les dépendances du projet isolées, puis installez les bibliothèques dont le scraper a besoin.

bash
python --version

python -m venv goodfirms_env
source goodfirms_env/bin/activate

pip install crawlbase beautifulsoup4

Sur Windows, activez l'environnement avec goodfirms_env\Scripts\activate à la place de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 analyse le HTML renvoyé pour que vous puissiez extraire des champs individuels par sélecteur CSS. json et csv font partie de la bibliothèque standard, donc il n'y a rien de plus à installer pour l'étape d'export.

Étape 1 : Récupérer une page GoodFirms rendue

Commencez par obtenir une page finalisée. Importez la classe CrawlingAPI, initialisez-la avec votre token JS, et demandez une URL de catégorie GoodFirms. Passez ajax_wait et page_wait pour que l'API maintienne le contenu dynamique avant la capture de la page. Vérifier le cb_status (legacy pc_status) de Crawlbase avant d'analyser permet de détecter les échecs rapidement plutôt que silencieusement.

python
from crawlbase import CrawlingAPI

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

OPTIONS = {
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0",
    "ajax_wait": "true",
    "page_wait": 5000,
}

def crawl(page_url):
    response = api.get(page_url, OPTIONS)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['headers']['cb_status']}")
    return None

if __name__ == "__main__":
    listing_url = "https://www.goodfirms.co/companies/web-development-agency/london"
    html = crawl(listing_url)
    print(html[:500] if html else "No HTML returned")

Les deux options d'attente sont importantes pour une cible rendue côté client. ajax_wait indique à l'API d'attendre le contenu asynchrone, et page_wait maintient un nombre fixe de millisecondes après le chargement pour que les cartes à rendu tardif apparaissent avant la capture. Cinq secondes est un bon point de départ ; augmentez si les résultats reviennent maigres. Exécutez le script avec python goodfirms_scraper.py et vous devriez voir le vrai balisage de l'annuaire GoodFirms, pas la coquille qu'une simple requête renvoie. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.

Crawlbase Crawling API

GoodFirms nécessite une page rendue derrière une IP de confiance, en un seul appel, ce qui est exactement ce que les options ajax_wait et page_wait ci-dessus configurent. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, effectue une rotation des IP résidentielles côté serveur, et vous remet le HTML finalisé, vous évitant de gérer vous-même une flotte headless et un pool de proxies. Pointez-la sur une page de catégorie publique avec le niveau gratuit d'abord.

Étape 2 : Identifier les sélecteurs de liste

Avant d'écrire le parseur, inspectez la page de catégorie dans les outils de développement de votre navigateur (clic droit et choisissez Inspecter, ou appuyez sur Ctrl + Shift + I) pour trouver les éléments qui enveloppent chaque entreprise. Sur les listes de recherche GoodFirms, chaque carte d'entreprise réside dans un élément de liste, et les champs correspondent à ces sélecteurs :

  • Nom de l'entreprise se trouve dans un <h3> avec la classe firm-name.
  • Localisation est un <div> avec la classe firm-location.
  • Catégorie de service est un <div> imbriqué sous firm-content avec la classe tagline.
  • Note apparaît dans un <span> avec la classe rating-number.
  • URL du profil est l'href d'un <a> avec la classe visit-profile, à l'intérieur de firm-urls.

Avec ces informations en main, chargez le HTML rendu dans BeautifulSoup et extrayez chaque champ par carte. Chaque recherche est protégée pour qu'un champ manquant renvoie une valeur par défaut au lieu de faire planter l'exécution.

python
from bs4 import BeautifulSoup

def extract_company(card):
    name = card.select_one("h3.firm-name")
    location = card.select_one("div.firm-location")
    category = card.select_one("div.firm-content > div.tagline")
    rating = card.select_one("span.rating-number")
    link = card.select_one("div.firm-urls > a.visit-profile")

    return {
        "name": name.get_text(strip=True) if name else "",
        "location": location.get_text(strip=True) if location else "",
        "category": category.get_text(strip=True) if category else "",
        "rating": rating.get_text(strip=True) if rating else "No rating",
        "profile_url": link["href"] if link else "",
    }

def parse_listings(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("ul.firm-directory-list > li.firm-wrapper")
    return [extract_company(card) for card in cards]

Le sélecteur de conteneur ul.firm-directory-list > li.firm-wrapper descend de la liste de l'annuaire jusqu'à chaque carte d'entreprise, et extract_company lit les cinq champs depuis l'intérieur. Les guards en ligne empêchent une carte qui omet, par exemple, une note de casser la boucle : elle revient à "No rating" à la place.

Les sélecteurs dérivent

Les sites d'annuaires révisent leur balisage sans préavis, et les noms de classes générés peuvent changer entre les visites. Traitez les sélecteurs ici comme un modèle de départ, pas comme un contrat. Quand une liste revient vide, réinspectez la page 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, pas le signe que quelque chose est cassé.

Étape 3 : Gérer la pagination sur les pages de liste

Une seule page de catégorie est une tranche du jeu de résultats. GoodFirms pagine avec un paramètre de requête page, vous parcourez donc chaque page et collectez les enregistrements. Un petit wrapper de nouvelle tentative autour de la récupération empêche une seule page lente de mettre fin à l'exécution.

python
import time

def fetch_html(page_url, max_retries=2):
    for attempt in range(max_retries + 1):
        html = crawl(page_url)
        if html:
            return html
        if attempt < max_retries:
            print(f"Retrying ({attempt + 1}/{max_retries})...")
            time.sleep(1)
    print(f"Unable to fetch {page_url}")
    return None

def scrape_all_pages(base_url, num_pages=5):
    all_companies = []
    for page in range(1, num_pages + 1):
        url = f"{base_url}?page={page}"
        print(f"Scraping page {page}...")
        html = fetch_html(url)
        if html:
            all_companies.extend(parse_listings(html))
        time.sleep(2)
    return all_companies

fetch_html réessaie une récupération échouée jusqu'à deux fois avec une courte pause, renvoyant le HTML en cas de succès et None une fois qu'il abandonne. scrape_all_pages ajoute le paramètre page, analyse les cartes et plafonne le crawl au plafond num_pages pour qu'une grande catégorie ne s'emballe pas. Le time.sleep(2) entre les pages cadence l'exécution.

Étape 4 : Assembler le scraper de listes

Maintenant, reliez les pièces en un seul script fonctionnel : parcourez les pages, collectez les fiches d'entreprises et exportez-les en JSON et CSV.

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

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

OPTIONS = {
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0",
    "ajax_wait": "true",
    "page_wait": 5000,
}

def crawl(page_url):
    response = api.get(page_url, OPTIONS)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['headers']['cb_status']}")
    return None

def fetch_html(page_url, max_retries=2):
    for attempt in range(max_retries + 1):
        html = crawl(page_url)
        if html:
            return html
        if attempt < max_retries:
            time.sleep(1)
    return None

def extract_company(card):
    name = card.select_one("h3.firm-name")
    location = card.select_one("div.firm-location")
    category = card.select_one("div.firm-content > div.tagline")
    rating = card.select_one("span.rating-number")
    link = card.select_one("div.firm-urls > a.visit-profile")

    return {
        "name": name.get_text(strip=True) if name else "",
        "location": location.get_text(strip=True) if location else "",
        "category": category.get_text(strip=True) if category else "",
        "rating": rating.get_text(strip=True) if rating else "No rating",
        "profile_url": link["href"] if link else "",
    }

def parse_listings(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("ul.firm-directory-list > li.firm-wrapper")
    return [extract_company(card) for card in cards]

def scrape_all_pages(base_url, num_pages=5):
    all_companies = []
    for page in range(1, num_pages + 1):
        url = f"{base_url}?page={page}"
        print(f"Scraping page {page}...")
        html = fetch_html(url)
        if html:
            all_companies.extend(parse_listings(html))
        time.sleep(2)
    return all_companies

def save_outputs(records):
    with open("goodfirms_companies.json", "w") as f:
        json.dump(records, f, indent=2)
    if not records:
        return
    with open("goodfirms_companies.csv", "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=records[0].keys())
        writer.writeheader()
        writer.writerows(records)

def main():
    base_url = "https://www.goodfirms.co/companies/web-development-agency/london"
    companies = scrape_all_pages(base_url, num_pages=3)
    save_outputs(companies)
    print(f"Saved {len(companies)} companies")

if __name__ == "__main__":
    main()

Le script parcourt jusqu'à trois pages de catégorie, analyse chacune en enregistrements et cadence la boucle avec un délai de deux secondes. save_outputs écrit à la fois JSON et CSV en utilisant les clés du premier enregistrement comme en-tête, pour que vous ayez les données dans la forme souhaitée par votre outil en aval. Ajustez num_pages et l'URL de catégorie selon votre secteur cible et votre ville.

À quoi ressemble la sortie

Exécutez le script complet avec python goodfirms_scraper.py et vous obtenez un enregistrement structuré propre par entreprise, prêt pour l'analyse, une base de données ou un tableur.

json
[
  {
    "name": "Unified Infotech",
    "location": "London, United Kingdom",
    "category": "Driving Digital Transformation with Advanced Tech",
    "rating": "5.0",
    "profile_url": "https://www.goodfirms.co/company/unified-infotech"
  },
  {
    "name": "instinctools",
    "location": "London, United Kingdom",
    "category": "Building Custom Software Solutions",
    "rating": "4.9",
    "profile_url": "https://www.goodfirms.co/company/instinctools"
  }
]

Le CSV correspondant reprend les mêmes colonnes, une ligne par entreprise, qui s'intègre directement dans pandas ou tout tableur pour filtrer par note, localisation ou catégorie de service.

Étape 5 : Scraper les pages de profil d'entreprise

Les listes vous donnent la largeur ; les pages de profil vous donnent la profondeur. Chaque URL de profil mène à une page avec la description complète de l'entreprise, la tranche tarifaire horaire, la taille de l'équipe, l'année de fondation et les services. Inspectez une page de profil de la même façon, et les champs plus profonds correspondent à ces sélecteurs :

  • Nom de l'entreprise est un <h1> avec itemprop="name".
  • Description est un <div> avec la classe profile-summary-text.
  • Taux horaire est un <span> à l'intérieur de div.profile-pricing.
  • Nombre d'employés est un <span> à l'intérieur de div.profile-employees.
  • Année de fondation est un <span> à l'intérieur de div.profile-founded.
  • Services proviennent de l'attribut data-name de chaque <button> dans ul.services-chart-list.
python
import re
import json
import time
from bs4 import BeautifulSoup

def text_of(soup, selector, default="N/A"):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else default

def extract_profile(html, url):
    soup = BeautifulSoup(html, "html.parser")
    summary = soup.select_one("div.profile-summary-text")
    description = re.sub(r"\s+", " ", summary.get_text(strip=True)) if summary else "N/A"
    services = [b["data-name"] for b in soup.select("ul.services-chart-list button[data-name]")]

    return {
        "name": text_of(soup, 'h1[itemprop="name"]'),
        "profile_url": url,
        "description": description,
        "hourly_rate": text_of(soup, "div.profile-pricing > span"),
        "no_of_employees": text_of(soup, "div.profile-employees > span"),
        "year_founded": text_of(soup, "div.profile-founded > span"),
        "services": services,
    }

def scrape_profiles(profile_urls):
    profiles = []
    for url in profile_urls:
        print(f"Scraping profile: {url}")
        html = fetch_html(url)
        if html:
            profiles.append(extract_profile(html, url))
        time.sleep(2)
    return profiles

if __name__ == "__main__":
    profile_urls = [
        "https://www.goodfirms.co/company/unified-infotech",
        "https://www.goodfirms.co/company/instinctools",
    ]
    data = scrape_profiles(profile_urls)
    with open("goodfirms_profiles.json", "w") as f:
        json.dump(data, f, indent=2)
    print(f"Saved {len(data)} profiles")

Ce code réutilise le wrapper fetch_html du scraper de listes, donc le rendu, les nouvelles tentatives et le token JS sont portés. L'appel re.sub(r"\s+", " ") compresse les espaces multiples que les descriptions d'entreprises tendent à porter, et la liste de services lit l'attribut data-name de chaque bouton de graphique. Un enregistrement de profil typique ressemble à ceci :

json
{
  "name": "Unified Infotech",
  "profile_url": "https://www.goodfirms.co/company/unified-infotech",
  "description": "Unified Infotech is a digital transformation partner serving enterprises with custom web, mobile, and software solutions...",
  "hourly_rate": "$50 - $99/hr",
  "no_of_employees": "50 - 249",
  "year_founded": "2010",
  "services": [
    "Web Development",
    "Software Development",
    "Web Designing (UI/UX)",
    "Mobile App Development",
    "E-commerce Development"
  ]
}

Alimentez les valeurs profile_url de l'étape de liste dans scrape_profiles et vous obtenez la tranche tarifaire horaire et les services pour chaque entreprise dans la même exécution, combinant largeur et profondeur en un seul jeu de données.

Rester non bloqué à grande échelle

Même avec le rendu géré, un annuaire actif surveille le trafic ressemblant à des scrapers. Quelques habitudes maintiennent une exécution plus longue saine sur toute cible commerciale.

  • Cadencez vos requêtes. Marteler les listes dans une boucle serrée est le moyen le plus rapide de se faire limiter ou challenger. Les délais de deux secondes ci-dessus sont le plancher, pas le plafond ; élargissez-les pour les grands travaux et variez vos cibles plutôt que de crawler une seule catégorie à pleine vitesse.
  • Misez sur la rotation. Un pool d'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 s'en charge pour vous ; si vous construisez votre propre stack, c'est la partie à bien faire.
  • Lisez les codes de statut. Une exécution qui commence à renvoyer des valeurs cb_status non-200 vous indique que le débit ou le niveau d'IP actuels ne sont plus suffisants. Traitez cela comme un signal pour ralentir, pas comme du bruit à ignorer.

Pour les crawls plus importants, le Crawler asynchrone met en file les requêtes et livre les résultats vers un webhook, ce qui convient à l'exécution de nombreuses pages de catégorie sans maintenir de connexions ouvertes. Pour le guide complet, consultez comment scraper des sites web sans se faire bloquer. Et si vous cartographiez le paysage plus large des fournisseurs B2B, la même approche s'applique à scraper Clutch, Superpages et d'autres listes d'entreprises locales.

Est-il légal de scraper GoodFirms ?

La question de savoir si le scraping de GoodFirms est autorisé dépend de ses Conditions d'utilisation, de votre juridiction et de ce que vous faites avec les données. GoodFirms restreint l'accès automatisé et la collecte en masse dans ses conditions, donc le scraping peut aller à l'encontre de celles-ci quelle que soit la prudence de vos outils. Aucun code ici ne change cela ; il fait seulement fonctionner la partie technique. Lisez les Conditions d'utilisation de GoodFirms et son robots.txt avant de commencer, respectez les limites de débit et les directives de crawl qu'ils déclarent, et maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger leurs serveurs.

La portée compte autant que la politesse. Limitez-vous aux données d'annonces commerciales publiques : le nom de l'entreprise, la note, la catégorie de service, la localisation, la tranche tarifaire horaire et le lien de profil que tout visiteur peut voir sans compte. Un profil d'entreprise peut également exposer des coordonnées pour l'entreprise ou des personnes nommées, et dès que vous collectez ou stockez quelque chose qui identifie une personne, la loi sur la protection des données s'applique. En vertu du RGPD, vous avez besoin d'une base légale pour traiter des données personnelles, les personnes peuvent demander à être supprimées, et si vous utilisez des coordonnées collectées à des fins de démarchage, des régimes comme le RGPD et le CAN-SPAM américain s'appliquent aussi : vous avez besoin d'un consentement ou d'une autre base légale, d'informations d'expéditeur exactes et d'un moyen de désinscription fonctionnel. Les personnes et les entreprises peuvent demander à ne pas être contactées, et vous devez respecter cela. Évitez de scraper quoi que ce soit derrière une connexion, et ne redistribuez pas le contenu éditorial ou les textes de revues de GoodFirms en masse, qui sont protégés par des droits d'auteur.

Ce guide est délibérément limité aux pages de liste et de profil publiques car c'est la ligne qui rend le travail défendable. Il ne couvre pas ce qui se trouve derrière un compte, la collecte en masse de coordonnées personnelles ou toute tentative de contournement de l'authentification. Données commerciales publiques uniquement. Si votre projet nécessite plus que cela, la bonne voie est une voie autorisée : GoodFirms publie des données via ses propres canaux et partenariats, donc vérifiez si une API officielle ou un flux sous licence couvre votre cas d'usage. C'est la route appropriée pour un usage commercial ou en masse, pas un scraper plus astucieux.

Récapitulatif

Points clés

  • GoodFirms rend certaines de ses pages côté client. Une simple requête peut renvoyer une coquille légère, donc rendez la page avec le token JS avant de l'analyser.
  • Vous avez besoin du rendu et d'une IP de confiance ensemble. La Crawling API avec un token JS fait les deux en un seul appel ; ajax_wait et page_wait contrôlent le temps d'attente du contenu.
  • Travaillez en deux couches. Analysez les listes de catégorie pour le nom, la note, les services, la localisation et le lien de profil, puis suivez chaque URL de profil pour la tranche tarifaire horaire, la taille de l'équipe et la liste des services.
  • Paginez et exportez. Parcourez le paramètre de requête page jusqu'à un plafond, cadencez l'exécution avec de courts délais, et écrivez les enregistrements en JSON et CSV.
  • Restez sur les données commerciales publiques. Respectez les CGU et robots.txt de GoodFirms, traitez toute coordonnée personnelle comme réglementée par le RGPD et le CAN-SPAM avec une base légale et un moyen de désinscription fonctionnel, et ne touchez jamais aux connexions ni au contenu éditorial protégé par des droits d'auteur.

Foire aux questions

Pourquoi une simple requête ne renvoie-t-elle qu'une partie des données GoodFirms ?

Parce que GoodFirms charge certaines parties de sa grille d'annuaire et des détails de profil côté client avec JavaScript. Le HTML initial peut être une coquille qui se remplit seulement après l'exécution des scripts, donc une requête brute peut renvoyer le statut 200 avec des cartes ou des champs de profil manquants. Rendez d'abord la page pour obtenir l'ensemble complet, ce que gère le token JS de la Crawling API.

Dois-je utiliser le token normal ou le token JS pour GoodFirms ?

Utilisez le token JS. Le token normal récupère le HTML statique, qui peut manquer les parties de GoodFirms qui se rendent dans le navigateur. Le token JS exécute d'abord la page dans un vrai navigateur, donc les cartes d'entreprises et les champs de profil sont présents quand BeautifulSoup les analyse.

Quelles données puis-je scraper de GoodFirms ?

Les champs d'annonces commerciales publiques : le nom de l'entreprise, sa note, la catégorie de service ou le slogan, la localisation, la tranche tarifaire horaire, la taille de l'équipe, l'année de fondation et le lien de profil. Limitez-vous aux données visibles par tout visiteur sans compte, et traitez toute coordonnée de personnes nommées comme des données personnelles qui tombent en dehors de la portée des annonces publiques que ce guide couvre.

Mes sélecteurs retournent des résultats vides. Qu'est-ce qui a changé ?

Presque certainement le balisage de GoodFirms. Les noms de classes comme firm-name, firm-location et rating-number, et les conteneurs de profil comme profile-pricing et services-chart-list, peuvent changer sans préavis, donc les sélecteurs qui fonctionnaient le mois dernier peuvent casser. Réinspectez une page en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. La maintenance périodique des sélecteurs est normale pour tout scraper en production.

Comment gérer la pagination sur une catégorie ?

GoodFirms ajoute un paramètre de requête page à l'URL de catégorie. Parcourez les pages dans une boucle, analysez les cartes d'entreprises sur chacune, plafonnez le crawl à un plafond num_pages pour qu'une grande catégorie ne s'emballe pas, et ajoutez un court délai entre les pages. La fonction scrape_all_pages ci-dessus montre la boucle complète.

Puis-je utiliser les données scrapées de GoodFirms pour du démarchage ou commercialement ?

Traitez cela comme une question juridique, pas technique. Toute coordonnée que vous collectez est une donnée personnelle, donc le démarchage est régi par des régimes comme le RGPD et le CAN-SPAM américain : vous avez besoin d'une base légale ou d'un consentement, d'informations d'expéditeur exactes et d'un moyen de désinscription fonctionnel, et les personnes peuvent demander à ne pas être contactées. Les Conditions d'utilisation de GoodFirms restreignent aussi la réutilisation de son contenu. Examinez les conditions, vérifiez si une API officielle ou un flux sous licence couvre votre cas d'usage, et demandez un conseil juridique avant de construire un produit ou une liste de démarchage à partir des données.

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