Monster est l'une des plus grandes plateformes d'emploi du web, avec des milliers d'annonces actives réparties dans tous les secteurs et régions. Chaque offre publique porte le type de signal structuré qui alimente la recherche sur le marché du travail, l'analyse concurrentielle des recruteurs et les outils de recherche d'emploi personnalisés : un intitulé de poste, l'entreprise qui recrute, une localisation, une date de publication et un lien vers l'offre complète. Le problème, c'est que Monster construit ses résultats de recherche dans le navigateur avec JavaScript et charge de façon différée davantage de cartes au fur et à mesure du défilement, donc une simple requête HTTP vous retourne une coquille presque vide plutôt que les annonces recherchées.

Ce guide vous montre comment scraper les offres Monster avec Python de manière fiable. Vous construisez un petit scraper exécutable qui récupère une page de recherche rendue via la Crawling API, analyse chaque carte d'offre avec BeautifulSoup, et affiche un résultat structuré et propre. L'ensemble du tutoriel se limite aux données d'annonces publiques, et la section juridique vers la fin n'est pas un simple boilerplate, alors lisez-la avant de pointer ce script sur un volume réel.

Ce que vous allez construire

Un script Python qui prend une URL de recherche Monster publique, récupère le HTML rendu via la Crawling API, et extrait un enregistrement structuré pour chaque offre sur la page. Nous utilisons une recherche d'offres de développeur comme exemple fil rouge et extrayons ces champs par offre :

  • Intitulé du poste le rôle proposé, par exemple "Java Developer".
  • Entreprise l'employeur derrière l'annonce.
  • Localisation où le poste est basé, comme "New York, NY".
  • Date de publication depuis combien de temps l'annonce est en ligne, quand Monster l'expose.
  • URL de l'offre le lien vers l'offre complète, pour un suivi par poste.

Pourquoi une simple requête échoue sur Monster

Si vous faites une requête à une URL de recherche Monster avec un client HTTP basique, vous obtenez une réponse avec le statut 200 et presque aucune donnée d'annonce dans le corps. Deux choses jouent contre vous. Premièrement, Monster rend ses cartes d'offres dans le navigateur avec JavaScript, donc le HTML initial est une coquille qui ne se remplit qu'après l'exécution des scripts de la page. Deuxièmement, la page de résultats utilise une pagination par défilement : plus de cartes se chargent lorsqu'un vrai utilisateur fait défiler vers le bas, donc même un snapshot rendu trop tôt ne capture que la première poignée d'offres.

Un scraper Monster fonctionnel nécessite donc trois choses dans une seule requête : un navigateur qui rende vraiment la page, une IP que la plateforme lit comme un vrai visiteur, et un moyen de piloter le défilement pour que les cartes chargées en différé apparaissent. Vous pouvez assembler cela vous-même avec un navigateur headless, un script de défilement et un pool de proxies résidentiels rotatifs, mais les assembler et les maintenir en bonne santé représente l'essentiel du travail. La Crawling API regroupe les trois en un seul appel : vous lui envoyez l'URL avec un token JavaScript, elle rend la page et la fait défiler derrière une IP de confiance, et vous retourne le HTML final prêt à être analysé.

Pourquoi le token JS

Crawlbase propose deux types de tokens. Le token normal récupère du HTML statique ; le token JavaScript (JS) rend la page dans un vrai navigateur d'abord. Monster est rendu côté client, donc vous avez besoin du token JS ici. Utiliser le token normal retourne la même coquille vide qu'une simple requête, et il n'y a rien à en extraire.

Prérequis

Quelques éléments doivent être 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 dans l'analyse de HTML, notre guide sur comment utiliser BeautifulSoup en Python couvre les bases des sélecteurs sur lesquelles ce tutoriel s'appuie.

Python 3.8 ou version ultérieure. Vérifiez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda.

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. Traitez le token comme un mot de passe : il authentifie vos requêtes, gardez-le donc hors du contrôle de version. Le niveau gratuit inclut suffisamment de requêtes pour suivre ce guide de bout en bout.

Configurer le projet

Créez un environnement virtuel pour isoler les dépendances du projet, puis installez les deux bibliothèques dont le scraper a besoin.

bash
python --version

python -m venv monster_env
source monster_env/bin/activate

pip install crawlbase beautifulsoup4

Sur Windows, activez l'environnement avec monster_env\Scripts\activate au lieu de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel de la Crawling API, et beautifulsoup4 analyse le HTML retourné pour que vous puissiez extraire des champs individuels par sélecteur CSS.

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

Commencez par obtenir la page finale. Importez la classe CrawlingAPI, initialisez-la avec votre token JS et faites une requête sur l'URL de recherche. Vérifier le code de statut avant d'analyser permet de rendre les échecs visibles plutôt que silencieux. Notez les deux options d'attente : ajax_wait indique à l'API d'attendre que le contenu asynchrone finisse de se charger, et page_wait maintient un délai fixe en millisecondes pour que les cartes à rendu tardif apparaissent avant la capture de la page.

python
from crawlbase import CrawlingAPI

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

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

if __name__ == "__main__":
    page_url = "https://www.monster.com/jobs/search?q=Java+Developer&where=New+York"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

Exécutez le script avec python scraper.py et vous devriez voir le vrai balisage des cartes d'offres, pas la coquille vide qu'une simple requête retourne. Cinq secondes est un bon page_wait de départ ; augmentez si les cartes reviennent vides. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.

Crawlbase Crawling API

Monster nécessite une page rendue et défilée 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, pilote le défilement pour que les cartes chargées en différé apparaissent, et fait tourner des adresses IP résidentielles côté serveur, vous épargnant ainsi la gestion d'une flotte headless et d'un pool de proxies. Pointez-la vers une page de recherche publique sur le niveau gratuit d'abord.

Étape 2 : Inspecter la structure des cartes d'offres

Avant d'écrire des sélecteurs, ouvrez une page de recherche Monster dans votre navigateur et inspectez une carte d'offre avec les outils de développement. Au moment de la rédaction de ce guide, chaque annonce se trouve dans un élément <article> portant un attribut data-testid="svx_jobCard", regroupé dans un conteneur avec l'id JobCardGrid. Dans chaque carte, les champs souhaités sont accrochés à des test ids stables :

  • Intitulé du poste et URL se trouvent sur un <a data-testid="jobTitle"> : le texte du lien est le titre, le href est l'URL de l'offre.
  • Entreprise se trouve dans un <span data-testid="company">.
  • Localisation se trouve dans un <span data-testid="jobDetailLocation">.
  • Date de publication apparaît dans un <span data-testid="jobDetailDateRecency"> quand Monster l'expose pour cette annonce.

Cibler les attributs data-testid plutôt que les noms de classes CSS est délibéré. Monster génère des noms de classes hashés qui changent à chaque déploiement, tandis que les test ids ont tendance à rester en place parce que les propres suites de tests du site en dépendent. Ce sont les hooks les plus durables que la page vous offre.

Étape 3 : Analyser les cartes d'offres avec BeautifulSoup

Avec le HTML rendu en main, chargez-le dans BeautifulSoup, sélectionnez chaque carte d'offre et extrayez chaque champ par son test id. Enveloppez chaque lecture de champ dans un petit helper qui retourne une chaîne vide quand un élément est manquant, pour qu'un champ absent ne fasse jamais planter l'exécution.

python
from bs4 import BeautifulSoup

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

def parse_jobs(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select('div#JobCardGrid article[data-testid="svx_jobCard"]')

    jobs = []
    for card in cards:
        title_link = card.select_one('a[data-testid="jobTitle"]')
        jobs.append({
            "title": title_link.get_text(strip=True) if title_link else "",
            "company": text_of(card, 'span[data-testid="company"]'),
            "location": text_of(card, 'span[data-testid="jobDetailLocation"]'),
            "posted": text_of(card, 'span[data-testid="jobDetailDateRecency"]'),
            "url": title_link["href"] if title_link else "",
        })
    return jobs

Le lien du titre est lu une fois et réutilisé, car il porte à la fois le texte du titre et l'URL de l'offre sur son href. Tout le reste passe par le helper text_of, qui interroge un seul élément et retourne une chaîne vide quand il est absent plutôt que de lever une exception sur un appel .get_text() contre rien. Cela rend l'extraction résistante quand une carte omet un champ, ce qui est fréquent puisque toutes les annonces n'exposent pas une date de publication.

Les sélecteurs évoluent

Le balisage de Monster change sans préavis, et les valeurs data-testid ci-dessus peuvent être renommées dans une refonte future. Traitez-les comme un modèle de départ, pas un contrat. Quand un champ revient vide pour chaque carte, inspectez à nouveau une page de recherche 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, ce n'est pas le signe que quelque chose est cassé.

Étape 4 : Capturer la page de résultats complète avec le défilement

La récupération de l'Étape 1 rend la page, mais Monster ne charge que le premier lot de cartes jusqu'à ce que l'utilisateur fasse défiler. Pour obtenir le jeu de résultats complet en une seule requête, confiez le défilement à la Crawling API plutôt que de faire tourner vous-même un navigateur headless. Deux options le contrôlent : scroll active la pagination par défilement, et scroll_interval définit combien de temps l'API continue à faire défiler, en secondes, jusqu'à un maximum de 60. Quand le défilement est activé, il n'est pas nécessaire de définir aussi page_wait, car la fenêtre de défilement laisse déjà le temps au contenu de se charger.

python
def crawl_with_scroll(page_url):
    options = {
        "ajax_wait": "true",
        "scroll": "true",
        "scroll_interval": "60",
    }
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

Remplacez crawl_with_scroll par le simple crawl quand vous voulez la page complète plutôt que seulement les premières cartes. L'analyseur de l'Étape 3 ne change pas : il sélectionne toujours chaque svx_jobCard dans le HTML retourné, seulement maintenant il y en a davantage.

Étape 5 : Assembler le tout et sauvegarder en JSON

Reliez maintenant la récupération avec défilement, l'analyseur et un petit écrivain JSON dans un script exécutable unique. Récupérez le HTML rendu et défilé, confiez-le à l'analyseur et écrivez les enregistrements structurés sur disque.

python
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

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

def crawl_with_scroll(page_url):
    options = {"ajax_wait": "true", "scroll": "true", "scroll_interval": "60"}
    response = api.get(page_url, options)
    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 el.get_text(strip=True) if el else ""

def parse_jobs(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select('div#JobCardGrid article[data-testid="svx_jobCard"]')
    jobs = []
    for card in cards:
        title_link = card.select_one('a[data-testid="jobTitle"]')
        jobs.append({
            "title": title_link.get_text(strip=True) if title_link else "",
            "company": text_of(card, 'span[data-testid="company"]'),
            "location": text_of(card, 'span[data-testid="jobDetailLocation"]'),
            "posted": text_of(card, 'span[data-testid="jobDetailDateRecency"]'),
            "url": title_link["href"] if title_link else "",
        })
    return jobs

def main():
    page_url = "https://www.monster.com/jobs/search?q=Java+Developer&where=New+York"
    html = crawl_with_scroll(page_url)
    if not html:
        return
    jobs = parse_jobs(html)
    with open("monster_jobs.json", "w") as f:
        json.dump(jobs, f, indent=2)
    print(f"Saved {len(jobs)} jobs to monster_jobs.json")

if __name__ == "__main__":
    main()

À quoi ressemble le résultat

Exécutez le script complet avec python scraper.py et vous obtenez une liste d'enregistrements structurés et propres, un par offre, prêts à écrire en CSV ou dans une base de données.

json
[
  {
    "title": "Java Developer (Core Java)",
    "company": "Georgia IT Inc.",
    "location": "New York, NY",
    "posted": "2 days ago",
    "url": "https://www.monster.com/job-openings/java-developer-core-java-new-york-ny"
  },
  {
    "title": "Java Backend Developer",
    "company": "Diverse Lynx",
    "location": "Manhattan, NY",
    "posted": "5 days ago",
    "url": "https://www.monster.com/job-openings/java-backend-developer-manhattan-ny"
  }
]

Boucler sur les pages de résultats

Une page de recherche est une démonstration ; un vrai travail s'étend sur de nombreuses pages et requêtes. Monster pagine avec un paramètre de requête page, vous pouvez donc parcourir les pages en l'incrémentant et en réutilisant la même paire récupération-analyse. Parce que chaque page de résultats partage la même structure de cartes, l'analyseur que vous avez déjà écrit fonctionne sur toutes sans modification. Cadencez la boucle pour ne pas enchaîner les requêtes sans délai.

python
import time

def scrape_pages(query, where, pages=3):
    all_jobs = []
    for page in range(1, pages + 1):
        url = (
            "https://www.monster.com/jobs/search"
            f"?q={query}&where={where}&page={page}"
        )
        html = crawl_with_scroll(url)
        if html:
            all_jobs.extend(parse_jobs(html))
        time.sleep(2)
    return all_jobs

Le time.sleep(2) entre les pages est délibéré. Marteler la recherche dans une boucle serrée est la façon la plus rapide d'être throttlé, même avec le rendu et la rotation gérés pour vous. Espacez les requêtes et arrêtez tôt quand une page ne retourne plus de nouvelles cartes.

Rester débloqué

Même avec le rendu et le défilement gérés, Monster surveille le trafic ayant l'apparence d'un scraper. Quelques habitudes maintiennent une exécution en bonne santé, et elles s'appliquent à toute cible commerciale difficile.

  • Cadencez vos requêtes. Espacez les requêtes et variez vos requêtes au lieu de crawler un seul chemin de recherche à 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 pour qu'aucune individuelle ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre stack, c'est la partie à bien maîtriser.
  • Lisez les codes de statut. Une exécution qui commence à retourner des challenges ou des erreurs vous indique que le débit ou le niveau d'IP actuel n'est plus suffisant. Traitez cela comme un signal pour reculer, pas comme du bruit à ignorer.

Pour le guide complet, consultez comment scraper des sites web sans se faire bloquer et l'analyse approfondie sur comment contourner les CAPTCHA lors du web scraping. Si votre projet touche aussi aux réseaux professionnels, notre guide sur comment scraper LinkedIn couvre une cible comparable sensible à l'authentification. Et si vous préférez router votre propre trafic via un pool rotatif plutôt que d'utiliser l'API gérée, le Smart AI Proxy (aussi appelé AI Proxy) vous donne la même rotation d'IP résidentielles comme point de terminaison proxy drop-in.

Est-il légal de scraper Monster ?

La légalité du scraping de Monster dépend des conditions d'utilisation de Monster, de votre juridiction et de l'usage que vous faites des données. Les conditions de Monster restreignent l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quelle que soit la prudence de vos outils. Aucun code présenté ici ne change cela ; il se contente de faire fonctionner la partie technique. Lisez les Conditions d'utilisation de Monster et son robots.txt, et traitez les deux comme la limite de ce que vous collectez.

Quelques lignes à respecter. Collectez uniquement des données d'annonces publiques : l'intitulé du poste, l'entreprise, la localisation, la date de publication et l'URL de l'offre que chacun peut voir sur une page de recherche publique sans se connecter. Respectez les attentes de débit déclarées de Monster et maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger ses serveurs. Limitez votre travail strictement aux offres d'emploi publiques.

Ce guide se limite délibérément aux offres d'emploi publiques parce que c'est la ligne qui rend le travail défendable. Il ne couvre pas les données personnelles des candidats ou des recruteurs, les CV ou les profils de candidats, quoi que ce soit derrière une connexion ou un niveau payant, ni aucune tentative de contournement de l'authentification. Les informations personnelles des chercheurs d'emploi et des recruteurs sont exactement le type de données à laisser de côté. Si votre projet nécessite plus que des offres publiques, un accord de données officiel ou les propres outils employeur de Monster est la voie correcte, pas un scraper plus sophistiqué.

Récapitulatif

Points clés

  • Monster est rendu côté client. Une simple requête retourne une coquille vide, donc vous devez rendre la page avant de l'analyser.
  • Rendu, IP de confiance et défilement vont ensemble. La Crawling API avec un token JS fait les trois en un seul appel ; scroll et scroll_interval font apparaître les cartes chargées en différé.
  • BeautifulSoup fait l'extraction. Mappez l'intitulé du poste, l'entreprise, la localisation, la date de publication et l'URL aux hooks data-testid de la carte, et attendez-vous à ce que ces hooks évoluent.
  • Mettez à l'échelle en bouclant sur les pages. Parcourez le paramètre page de Monster avec le même analyseur, et cadencez la boucle pour ne pas être throttlé.
  • Restez sur les annonces publiques. Respectez les CGU et le robots.txt de Monster, et ne touchez jamais aux données personnelles des candidats ou recruteurs, aux CV ni aux pages protégées par connexion.

Foire aux questions

Puis-je scraper des offres Monster avec seulement requests et BeautifulSoup ?

Pas de manière fiable. Monster rend ses cartes d'offres dans le navigateur avec JavaScript et charge de façon différée davantage au fil du défilement, donc un appel requests brut retourne le statut 200 avec les annonces vides. Vous avez besoin de quelque chose qui rende la page et pilote le défilement d'abord, ce que le token JS de la Crawling API combiné aux options de défilement gère avant que BeautifulSoup ne voie jamais le HTML.

Ai-je besoin du token normal ou du token JS pour Monster ?

Le token JS. Le token normal récupère du HTML statique, qui sur Monster est la même coquille vide qu'une simple requête retourne. Le token JS rend la page dans un vrai navigateur avant de retourner le HTML, de sorte que les cartes d'offres sont présentes quand BeautifulSoup les analyse.

Comment gérer la pagination par défilement de Monster ?

Passez scroll: "true" et scroll_interval: "60" dans les options de requête. La Crawling API fait alors défiler la page pendant 60 secondes au maximum, de sorte que les cartes chargées en différé se rendent avant la capture du HTML. Avec le défilement activé, vous n'avez pas besoin de page_wait en plus. Pour aller au-delà d'une page de résultats, incrémentez le paramètre de requête page de Monster et réutilisez la même paire récupération-analyse.

Mes sélecteurs retournent des chaînes vides. Qu'est-ce qui a changé ?

Presque certainement le balisage de Monster. Les hooks data-testid sur lesquels l'analyseur s'appuie peuvent être renommés dans une refonte, et les noms de classes générés par le build changent constamment. Inspectez à nouveau une page de recherche 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.

Puis-je scraper des CV ou les coordonnées de recruteurs sur Monster ?

Non, et ce guide ne le couvre pas. Les CV, les profils de candidats et les données personnelles de recruteurs ou candidats se trouvent derrière une connexion ou constituent des informations personnelles, pas des données d'annonces publiques. Scraper du contenu protégé par connexion ou contourner l'authentification pour y accéder est hors du périmètre ici et va à l'encontre des conditions de Monster. Limitez votre portée aux offres publiques sur les pages de recherche et d'annonces.

Comment éviter d'être bloqué lors du scraping de Monster ?

Maintenez votre taux de requêtes par IP bas, variez vos requêtes au lieu de boucler sur un seul chemin de recherche, et routez via des IP résidentielles rotatives pour qu'aucune adresse individuelle 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 stack, c'est la partie dans laquelle 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