Comment extraire des données Indeed avec Python (et éviter les erreurs 403)

Dernière mise à jour le April 16, 2026

Quelque part autour de ma cinquantième copie-coller d’un intitulé de poste depuis Indeed vers un tableur, j’ai commencé à remettre mes choix de carrière en question. Si vous avez déjà essayé d’extraire des données structurées d’Indeed par programmation, vous connaissez sûrement la suite : l’erreur 403 n’est pas un bug — c’est une fonctionnalité du système de défense d’Indeed.

Indeed est le plus grand site d’offres d’emploi au monde, avec environ , disponibles à un instant donné, et une présence dans . Autrement dit, c’est à la fois l’une des mines d’or les plus riches en données sur le marché de l’emploi — et l’une des plus pénibles à scraper. Le scraper open source JobFunnel (des milliers d’étoiles sur GitHub) a d’ailleurs été en décembre 2025 après des années de lutte perdue contre les protections anti-bot. Selon le mainteneur lui-même : "Tous les utilisateurs peuvent extraire quelques offres, mais se heurtent rapidement à un captcha, et l’extraction échoue, sans renvoyer d’offres." Un autre contributeur a signalé avoir reçu un CAPTCHA . Bref, ce n’est clairement pas une cible de scraping facile. Dans ce guide, je vais passer en revue toutes les méthodes concrètes pour extraire Indeed avec Python, vous montrer comment tenir face au mur des 403, et — pour ceux qui préfèrent éviter le débogage — présenter une alternative sans code avec .

Que signifie extraire Indeed avec Python ?

Le web scraping, dans son principe, consiste à extraire automatiquement des données structurées depuis des pages web. Quand on parle d’extraire Indeed avec Python, cela veut dire écrire un script qui visite les pages de résultats et les fiches d’offres d’Indeed, lit le HTML sous-jacent (ou les données intégrées), puis récupère des champs comme le poste, l’entreprise, la localisation, le salaire et la description dans un format exploitable — CSV, base de données, Google Sheet.

Les bibliothèques Python typiques sont Requests (pour les appels HTTP), BeautifulSoup (pour parser le HTML) et Selenium ou Playwright (pour automatiser le navigateur). Mais Indeed n’est pas un simple site statique. C’est un hybride : du HTML rendu côté serveur avec un bloc d’état JSON intégré, protégé par Cloudflare Bot Management. Ça veut dire que votre scraper doit gérer du contenu rendu en JavaScript, des noms de classes CSS qui changent, et des protections anti-bot agressives — avant même d’analyser un seul intitulé de poste.

Il n’existe pas non plus d’API Indeed officielle, gratuite et en lecture seule en 2026. L’ancienne Publisher Jobs API a été abandonnée vers 2020, et ce qu’il reste est réservé aux employeurs (Job Sync, Sponsored Jobs). Le scraping ou le recours à un fournisseur de données tiers sont donc les deux seules options vraiment réalistes.

Pourquoi extraire les données d’offres Indeed ?

Le cas d’usage est simple : parcourir manuellement des milliers d’annonces est irréaliste, et les données contenues dans ces annonces ont une vraie valeur.

indeed_stats_dca2a43cec.png

Cas d’usageQui en bénéficieExemple
Génération de leadsÉquipes commerciales et recrutementCréer des listes d’entreprises qui recrutent avec leurs coordonnées
Étude du marché de l’emploiAnalystes, équipes RHIdentifier les compétences recherchées et les repères salariaux par région
Veille concurrentielleEmployeurs, cabinets de recrutementSuivre les recrutements et les niveaux de rémunération des concurrents
Automatisation de la recherche d’emploiCandidatsRassembler les annonces correspondant à vos critères dans plusieurs zones
Données d’entraînement pour modèles MLData scientistsConstruire des modèles de prédiction salariale à partir de données historiques

Les propres recherches d’Indeed Hiring Lab que les données d’offres suivent de près les chiffres BLS JOLTS et peuvent servir de proxy quasi temps réel pour l’état du marché du travail américain. Des hedge funds utilisent le rythme de publication des offres comme signal de données alternatives. Les équipes RH s’appuient sur les fourchettes salariales extraites pour comparer les rémunérations. Et les recruteurs établissent des listes de prospects à partir des entreprises qui embauchent activement.

Un point pratique : les données salariales sur Indeed s’améliorent, mais restent incomplètes. À la mi-2025, environ incluent une information de salaire, mais seulement environ donnent un montant exact — le reste affiche des fourchettes. Toute analyse salariale fondée sur Indeed doit donc tenir compte de cette dispersion.

Choisir votre méthode pour extraire Indeed avec Python

Il n’existe pas une seule bonne manière d’extraire Indeed. La meilleure approche dépend de votre niveau, du volume de données souhaité et du niveau de maintenance que vous acceptez. J’ai testé les quatre grandes approches, et voici leur comparaison :

CritèreBS4 + RequestsSeleniumJSON caché (window.mosaic)Sans code (Thunderbit)
DifficultéDébutantIntermédiaireIntermédiaire-avancéAucune (2 clics)
VitesseRapideLente (rendu navigateur)RapideRapide (scraping cloud)
Contenu rendu en JSNonOuiOui (données intégrées)Oui
Résistance anti-botFaibleMoyenne (détectable)Moyenne à élevéeÉlevée (gérée automatiquement)
Maintenance si le HTML changeÉlevée (sélecteurs fragiles)ÉlevéeMoyenne (structure JSON plus stable)Aucune (l’IA s’adapte)
Idéal pourPrototypes rapidesPages dynamiques, contenu derrière connexionDonnées structurées en masseNon-développeurs, résultats rapides

Ce guide couvre chaque méthode. Si vous êtes développeur Python, vous voudrez lire les sections BS4, JSON caché et Selenium. Si vous n’êtes pas codeur — ou si vous en avez ras-le-bol de déboguer des 403 — sautez directement à la partie Thunderbit.

Avant de commencer

  • Niveau : Débutant à intermédiaire (sections Python) ; aucun (section Thunderbit)
  • Temps nécessaire : environ 20 à 60 minutes pour la configuration Python et le premier scraping ; environ 2 minutes avec Thunderbit
  • Ce qu’il vous faut : Python 3.9+, un éditeur de code, le navigateur Chrome, et (pour la solution sans code) l’

Préparer votre environnement Python pour le scraping d’Indeed

Avant d’écrire le moindre code, préparez votre environnement.

Installer les bibliothèques requises

Créez un environnement virtuel et installez les paquets nécessaires :

1python -m venv indeed_env
2source indeed_env/bin/activate  # Sous Windows : indeed_env\Scripts\activate
3# Pour l’approche HTTP + parsing
4pip install requests beautifulsoup4 lxml httpx
5# Pour l’approche JSON caché (recommandée)
6pip install curl_cffi parsel tenacity
7# Pour l’automatisation du navigateur
8pip install selenium

Quelques remarques :

  • curl_cffi est devenu le standard 2026 pour scraper les sites protégés par Cloudflare. Il imite les empreintes TLS d’un vrai navigateur, ce que requests et httpx ne savent pas faire. On verra plus loin pourquoi c’est crucial dans la partie anti-bot.
  • Selenium 4.6+ inclut Selenium Manager, donc vous n’avez plus besoin de télécharger ChromeDriver manuellement — la gestion du binaire du navigateur est automatisée.
  • Utilisez lxml comme moteur de parsing pour BeautifulSoup. Il est environ que le html.parser de la bibliothèque standard.

Créer la structure du projet

Gardez cela simple :

1indeed_scraper/
2├── scraper.py
3├── output/
4└── requirements.txt

Tous les exemples de code ci-dessous s’appuient sur scraper.py.

Comment extraire Indeed avec Python en utilisant BeautifulSoup

C’est l’approche la plus accessible : utiliser requests pour récupérer la page et BeautifulSoup pour parser le HTML. C’est la plus rapide à mettre en place, mais aussi la plus fragile sur Indeed.

Étape 1 : construire l’URL de recherche Indeed

Les URL de recherche d’Indeed suivent un schéma prévisible :

1https://www.indeed.com/jobs?q=<query>&l=<location>&start=<offset>

Par exemple, pour chercher "data analyst" à "Austin, TX" à partir de la première page :

1from urllib.parse import urlencode
2params = {
3    "q": "data analyst",
4    "l": "Austin, TX",
5    "start": 0,
6}
7url = f"https://www.indeed.com/jobs?{urlencode(params)}"
8print(url)
9# https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX&start=0

Indeed pagine par tranches de 10, avec un plafond dur de 1 000 résultats (start <= 990). Toute valeur au-delà de 990 renvoie silencieusement la même page.

Étape 2 : envoyer une requête HTTP avec des en-têtes adaptés

Indeed bloque immédiatement les requêtes avec les User-Agent Python par défaut. Il faut des en-têtes réalistes :

1import requests
2headers = {
3    "User-Agent": (
4        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
5        "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
6    ),
7    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
8    "Accept-Language": "en-US,en;q=0.9",
9    "Accept-Encoding": "gzip, deflate, br",
10    "Referer": "https://www.indeed.com/",
11}
12response = requests.get(url, headers=headers, timeout=30)
13print(response.status_code)

Si vous obtenez un 200, vous êtes passé — pour l’instant. Si vous obtenez un 403, Cloudflare vous a repéré. (On voit plus bas comment s’en sortir.)

Étape 3 : parser les annonces depuis le HTML

Utilisez BeautifulSoup pour sélectionner les éléments des cartes d’offres. Visez les attributs data-testid — ils sont plus stables que les noms de classes CSS aléatoires d’Indeed :

1from bs4 import BeautifulSoup
2soup = BeautifulSoup(response.text, "lxml")
3cards = soup.find_all("div", attrs={"data-testid": "slider_item"})
4jobs = []
5for card in cards:
6    title_el = card.find("h2", class_="jobTitle")
7    title = title_el.get_text(strip=True) if title_el else None
8    company = card.find(attrs={"data-testid": "company-name"})
9    location = card.find(attrs={"data-testid": "text-location"})
10    link = title_el.find("a")["href"] if title_el and title_el.find("a") else None
11    jobs.append({
12        "title": title,
13        "company": company.get_text(strip=True) if company else None,
14        "location": location.get_text(strip=True) if location else None,
15        "url": f"https://www.indeed.com{link}" if link else None,
16    })
17print(f"{len(jobs)} offres trouvées")

Étape 4 : gérer la pagination

Bouclez sur les pages en incrémentant le paramètre start :

1import time, random
2all_jobs = []
3for page in range(0, 50, 10):  # 5 premières pages
4    params["start"] = page
5    url = f"https://www.indeed.com/jobs?{urlencode(params)}"
6    response = requests.get(url, headers=headers, timeout=30)
7    # ... parser comme ci-dessus ...
8    all_jobs.extend(jobs)
9    time.sleep(random.uniform(3, 6))

Limites de cette approche

Soyons clairs : BS4 + Requests est la méthode la plus faible pour Indeed en 2026. requests utilise la bibliothèque TLS standard de Python, qui produit une identifiée instantanément par Cloudflare comme "non navigateur". De plus, il ne prend pas en charge HTTP/2, alors qu’Indeed le sert. Vous serez probablement bloqué après quelques pages. Et les sélecteurs CSS ? Indeed fait tourner les noms de classes comme css-1m4cuuf et jobsearch-JobComponent-embeddedBody-1n0gh5s — tout sélecteur qui les cible est une bombe à retardement.

Réservez cette méthode au prototypage rapide sur une seule page. Pour tout ce qui est plus sérieux, utilisez l’approche JSON caché.

Comment extraire Indeed avec Python en utilisant les données JSON cachées

C’est la méthode que je recommande à la plupart des développeurs Python. Au lieu d’analyser des éléments HTML fragiles, vous récupérez des données structurées à partir d’une variable JavaScript intégrée au code source d’Indeed : window.mosaic.providerData["mosaic-provider-jobcards"].

Tous les champs utiles — intitulé, entreprise, localisation, salaire, clé d’annonce, date de publication, indicateur remote — se trouvent déjà dans ce bloc JSON. Aucune exécution JavaScript n’est nécessaire. Le schéma est , ce qui le rend bien plus robuste que les sélecteurs DOM.

Étape 1 : récupérer le HTML de la page

Utilisez curl_cffi plutôt que requests — il imite les empreintes TLS d’un vrai navigateur, ce qui est essentiel pour survivre à Cloudflare :

1from curl_cffi import requests as cffi_requests
2response = cffi_requests.get(
3    "https://www.indeed.com/jobs?q=python+developer&l=Remote&start=0",
4    impersonate="chrome124",
5    headers={
6        "Accept-Language": "en-US,en;q=0.9",
7        "Referer": "https://www.indeed.com/",
8    },
9    timeout=30,
10)
11print(response.status_code, len(response.text))

Pourquoi curl_cffi ? C’est une interface Python autour de curl-impersonate, qui reproduit exactement le ClientHello TLS, le frame HTTP/2 SETTINGS et l’ordre des en-têtes d’un vrai navigateur. C’est le seul client HTTP Python activement maintenu qui contourne en un seul appel. Les cibles d’imitation disponibles incluent chrome120, chrome124, chrome131, ainsi que des variantes Safari et Edge.

Étape 2 : extraire le JSON avec une expression régulière

Le bloc JSON est intégré dans une balise <script>. Extrayez-le avec une regex :

1import re, json
2MOSAIC_RE = re.compile(
3    r'window\.mosaic\.providerData\["mosaic-provider-jobcards"\]=(\{.+?\});',
4    re.DOTALL,
5)
6match = MOSAIC_RE.search(response.text)
7if match:
8    data = json.loads(match.group(1))
9    results = data["metaData"]["mosaicProviderJobCardsModel"]["results"]
10    print(f"{len(results)} offres trouvées dans le JSON caché")
11else:
12    print("JSON caché introuvable — blocage possible ou changement de page")

Étape 3 : extraire les champs des offres depuis le JSON

Chaque élément de results contient plus d’informations que ce qui est visible à l’écran :

1jobs = []
2for job in results:
3    jobs.append({
4        "jobkey": job["jobkey"],
5        "title": job["title"],
6        "company": job.get("company"),
7        "location": job.get("formattedLocation"),
8        "remote": job.get("remoteLocation"),
9        "salary": (job.get("salarySnippet") or {}).get("text"),
10        "posted": job.get("formattedRelativeTime"),
11        "job_type": job.get("jobTypes"),
12        "easy_apply": job.get("indeedApplyEnabled"),
13        "url": f"https://www.indeed.com/viewjob?jk={job['jobkey']}",
14    })

Le JSON inclut souvent des estimations salariales, des attributs de taxonomie (tags de compétences) et des notes d’entreprise qui ne sont pas toujours visibles dans le HTML rendu.

Étape 4 : extraire plusieurs pages

Utilisez tierSummaries dans le JSON pour connaître le volume total de résultats, puis bouclez :

1import time, random
2all_jobs = []
3for start in range(0, 50, 10):  # 5 premières pages
4    url = f"https://www.indeed.com/jobs?q=python+developer&l=Remote&start={start}&sort=date"
5    response = cffi_requests.get(
6        url,
7        impersonate="chrome124",
8        headers={"Accept-Language": "en-US,en;q=0.9", "Referer": "https://www.indeed.com/"},
9        timeout=30,
10    )
11    match = MOSAIC_RE.search(response.text)
12    if match:
13        data = json.loads(match.group(1))
14        results = data["metaData"]["mosaicProviderJobCardsModel"]["results"]
15        all_jobs.extend([{
16            "jobkey": j["jobkey"],
17            "title": j["title"],
18            "company": j.get("company"),
19            "location": j.get("formattedLocation"),
20            "salary": (j.get("salarySnippet") or {}).get("text"),
21            "url": f"https://www.indeed.com/viewjob?jk={j['jobkey']}",
22        } for j in results])
23    time.sleep(random.uniform(3, 7))
24print(f"Total : {len(all_jobs)} offres extraites")

Pourquoi le JSON caché est plus robuste

La structure window.mosaic.providerData change moins souvent que les noms des classes CSS. Vous obtenez des données propres et structurées sans avoir à parser un HTML brouillon. Cela dit, vous avez toujours besoin de protections anti-bot (en-têtes, délais, proxies) — on en parle juste après.

Comment extraire Indeed avec Python en utilisant Selenium

Selenium est l’approche d’automatisation du navigateur. Elle est utile lorsque vous devez interagir avec la page — cliquer sur les panneaux de détail d’une offre, gérer du contenu derrière connexion, ou extraire des descriptions chargées dynamiquement qui ne figurent pas dans le HTML initial.

Quand utiliser Selenium plutôt qu’un client HTTP ?

  • Indeed charge une partie du contenu dynamiquement (descriptions complètes dans le panneau latéral droit)
  • Vous devez extraire des pages qui nécessitent une session ou une connexion
  • Vous faites du scraping à petite échelle où la vitesse n’est pas critique

Aperçu rapide

1from selenium import webdriver
2from selenium.webdriver.common.by import By
3from selenium.webdriver.chrome.options import Options
4import time
5options = Options()
6options.add_argument("--disable-blink-features=AutomationControlled")
7# options.add_argument("--headless=new")  # Le mode headless est plus détectable — utilisez-le avec prudence
8driver = webdriver.Chrome(options=options)
9driver.get("https://www.indeed.com/jobs?q=data+engineer&l=New+York")
10time.sleep(3)
11cards = driver.find_elements(By.CSS_SELECTOR, "[data-testid='slider_item']")
12for card in cards:
13    try:
14        title = card.find_element(By.CSS_SELECTOR, "h2.jobTitle").text
15        company = card.find_element(By.CSS_SELECTOR, "[data-testid='company-name']").text
16        location = card.find_element(By.CSS_SELECTOR, "[data-testid='text-location']").text
17        print(f"{title} | {company} | {location}")
18    except Exception:
19        continue
20driver.quit()

Limites

Selenium est lent — chaque page nécessite un rendu complet du navigateur. Chrome en mode headless est (Cloudflare vérifie navigator.webdriver, les chaînes de vendeur WebGL, le nombre de plugins, etc.). Même undetected-chromedriver ne fait que retarder la détection ; il ne la supprime pas définitivement. Et comme avec BS4, vos sélecteurs casseront quand Indeed mettra à jour son interface.

Pour la plupart des cas, l’approche JSON caché vous donne les mêmes données, plus vite et avec moins de maintenance. Gardez Selenium pour les cas particuliers où vous avez vraiment besoin d’un navigateur.

Comment éviter les erreurs 403 lors du scraping d’Indeed avec Python

C’est la section la plus importante. Si vous êtes arrivé ici après une recherche Google frustrante, vous êtes au bon endroit.

indeed_antibot_374d080ff4.png

Pourquoi Indeed bloque votre scraper

Indeed utilise — pas DataDome, pas PerimeterX. Les en-têtes de réponse le confirment : server: cloudflare, cf-ray et le cookie de bot management __cf_bm. Cloudflare inspecte votre empreinte TLS (JA3/JA4), l’ordre des en-têtes HTTP/2, vos schémas de requêtes et des signaux de comportement navigateur. Si l’un de ces éléments semble non humain, vous obtenez un 403, un 429, un 503 ou — le cas le plus vicieux — un 200 OK avec une page de challenge Turnstile à la place des vraies offres.

Faire tourner les User-Agent et les en-têtes de requête

Un User-Agent unique et statique est le moyen le plus rapide de se faire bloquer. Faites tourner une liste de chaînes réalistes et récentes. Important : les champs mineurs de version de Chrome sont depuis la réduction du User-Agent — n’inventez pas de versions mineures non nulles, sinon les systèmes anti-bot vous détecteront.

1import random
2USER_AGENTS = [
3    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
4    "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
5    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
6    "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
7    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
8    "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.3800.97",
9    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
10    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 "
11    "(KHTML, like Gecko) Version/17.4 Safari/605.1.15",
12]
13headers = {
14    "User-Agent": random.choice(USER_AGENTS),
15    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
16    "Accept-Language": "en-US,en;q=0.9",
17    "Accept-Encoding": "gzip, deflate, br, zstd",
18    "Referer": "https://www.indeed.com/",
19    "Sec-Fetch-Dest": "document",
20    "Sec-Fetch-Mode": "navigate",
21    "Sec-Fetch-Site": "same-origin",
22}

Assurez-vous aussi que vos Client Hints sec-ch-ua correspondent à la version du User-Agent. Un sec-ch-ua: "Chrome";v="131" associé à un User-Agent qui prétend être Chrome 145 est un drapeau rouge immédiat.

Ajouter des délais aléatoires entre les requêtes

Des intervalles fixes déclenchent les systèmes de détection de patterns. Utilisez un peu de variabilité :

1import time, random
2# Entre chaque requête
3time.sleep(random.uniform(3, 6))
4# En cas de nouvelle tentative après un blocage
5def backoff_sleep(attempt):
6    base = 4
7    sleep_time = base * (2 ** attempt) + random.uniform(0, 2)
8    time.sleep(min(sleep_time, 60))

Le consensus terrain issu de et recommande 3 à 6 secondes entre les requêtes par IP, avec un plafond d’environ 100 requêtes par IP et par session avant rotation.

Utiliser la rotation de proxies

C’est le facteur le plus déterminant. Les proxies de datacenter provenant des plages AWS/GCP atteignent environ 5 à 15 % de réussite sur des cibles protégées par Cloudflare Enterprise — autrement dit, ils sont quasiment inutiles sur Indeed. Les proxies résidentiels, associés à une empreinte TLS correcte, peuvent monter à 80 à 95 % de réussite.

1PROXIES = [
2    "http://user:pass@us.residential.example:7777",
3    "http://user:pass@us.residential.example:7778",
4    "http://user:pass@us.residential.example:7779",
5]
6proxy = random.choice(PROXIES)
7response = cffi_requests.get(
8    url,
9    impersonate="chrome124",
10    headers=headers,
11    proxies={"https": proxy},
12    timeout=30,
13)

En 2026, le prix des proxies résidentiels tourne autour de , selon le fournisseur et l’engagement. Pour Indeed, commencez avec un petit pool puis augmentez progressivement si nécessaire.

Gérer proprement les codes 403, 429 et 503

Ne vous contentez pas de relancer aveuglément. Chaque code signifie quelque chose :

1def fetch_with_retry(url, proxy_pool, max_retries=5):
2    for attempt in range(max_retries):
3        proxy = random.choice(proxy_pool)
4        headers["User-Agent"] = random.choice(USER_AGENTS)
5        try:
6            r = cffi_requests.get(
7                url,
8                impersonate=random.choice(["chrome124", "chrome120", "edge101"]),
9                headers=headers,
10                proxies={"https": proxy},
11                timeout=30,
12            )
13            # Vérifier le cas vicieux du "200 avec challenge"
14            if r.status_code == 200 and "cf-turnstile" not in r.text and "Just a moment" not in r.text:
15                return r
16            if r.status_code == 403:
17                print(f"403 — bloqué. Changement de proxy, tentative {attempt + 1}")
18            elif r.status_code == 429:
19                print(f"429 — limitation de débit. On ralentit.")
20            elif r.status_code == 503:
21                print(f"503 — serveur surchargé ou challenge JS.")
22            backoff_sleep(attempt)
23        except Exception as e:
24            print(f"Erreur de requête : {e}")
25            backoff_sleep(attempt)
26    raise RuntimeError(f"Échec après {max_retries} tentatives : {url}")

Le cas du 200 avec challenge est le plus piégeux. Vérifiez toujours la présence de cf-turnstile ou de Just a moment dans le corps de réponse avant de considérer un 200 comme un succès.

L’alternative la plus simple : laisser Thunderbit gérer l’anti-bot

Pour ceux qui ne veulent pas construire et maintenir des pools de proxies, des rotations d’en-têtes et l’imitation des empreintes TLS, le scraping cloud de gère automatiquement les CAPTCHA, la rotation de proxies et les protections anti-bot. Pas de configuration de proxy, pas de paramétrage de curl_cffi, pas de bibliothèques pour résoudre les CAPTCHA. C’est la solution la plus simple quand vous avez juste besoin des données.

Pourquoi votre scraper Indeed casse sans cesse (et comment y remédier)

Le mur des 403 est la douleur aiguë. La douleur chronique, c’est la maintenance — des scrapers qui fonctionnent aujourd’hui mais cassent la semaine suivante, en renvoyant silencieusement des données vides ou obsolètes.

Comment Indeed casse vos sélecteurs

Indeed fait tourner agressivement les noms de classes CSS. Le guide de Bright Data : des classes comme css-1m4cuuf et css-1rqpxry « semblent générées aléatoirement — probablement à la compilation ». Les tests A/B font que différentes sessions voient des mises en page différentes. Et la structure du DOM change sans préavis.

L’épisode JobFunnel est révélateur. Un contributeur a rapporté : "CaptchaBuster a réussi à atténuer le captcha, et la raison pour laquelle le scraping échouait encore [était] des sélecteurs BeautifulSoup obsolètes." Le scraper n’était pas bloqué — il analysait juste les mauvais éléments.

Stratégie : privilégier le JSON caché au parsing DOM

Le bloc window.mosaic.providerData est stable sur le plan du schéma depuis au moins 2023. Le chemin metaData.mosaicProviderJobCardsModel.results[] en 2026. Les sélecteurs DOM cassent chaque mois. L’extraction JSON casse au pire une fois par an — si elle casse.

Stratégie : utiliser des attributs de données plutôt que les noms de classes

Quand vous devez toucher au DOM, ciblez des attributs fonctionnels :

SélecteurRôle
[data-testid="slider_item"]Conteneur de chaque carte d’offre
[data-testid="job-title"] ou h2.jobTitle > aLien du titre de l’offre
[data-testid="company-name"]Nom de l’employeur
[data-testid="text-location"]Texte de la localisation
data-jk="<jobkey>" sur chaque carteL’ancrage le plus stable — inchangé depuis 2019

Ajouter des assertions pour détecter des sélecteurs obsolètes

Ne laissez jamais votre scraper s’exécuter silencieusement avec zéro résultat. Ajoutez une vérification après chaque récupération :

1results = parse_hidden_json(html)
2assert len(results) &gt; 0, (
3    f"Indeed a renvoyé un ensemble vide pour start={start} — "
4    "blocage possible, CAPTCHA ou dérive des sélecteurs. "
5    f"500 premiers caractères de la réponse : {html[:500]}"
6)

Journalisez les 500 à 2 000 premiers caractères de la réponse brute en cas d’échec. Vous pourrez ainsi identifier immédiatement s’il s’agit d’un challenge Turnstile, d’un mur de connexion ou d’un changement de schéma. Exécutez un test de fumée quotidien au niveau CI sur une requête fixe (par exemple q=python&l=remote) qui vérifie qu’il y a des résultats non nuls.

L’alternative IA : des scrapers qui ne cassent jamais

L’IA de Thunderbit relit la structure de la page à chaque exécution — elle ne dépend ni de sélecteurs codés en dur ni de motifs regex fragiles. Quand Indeed modifie son HTML, Thunderbit s’adapte automatiquement. Ça répond directement au problème de maintenance que les utilisateurs citent sans arrêt comme leur plus grosse frustration. Si vous avez déjà reçu un message Slack du genre « le scraper renvoie encore des lignes vides », vous savez à quel point c’est précieux de ne pas avoir à corriger ça.

Extraire Indeed sans écrire de Python : l’alternative sans code

Tous les guides concurrents partent du principe que vous allez écrire du code Python. Mais les retours des forums racontent autre chose. Des utilisateurs disent des choses comme "c’est tellement difficile avec les bugs et erreurs constants" et certains suggèrent même d’embaucher quelqu’un sur Fiverr juste pour récupérer les données. Si ça vous parle, cette section est votre porte de sortie.

Comment extraire Indeed avec Thunderbit (pas à pas)

Étape 1 : Installez l’ depuis le Chrome Web Store. Le démarrage est gratuit.

Étape 2 : Ouvrez une page de résultats Indeed dans votre navigateur — par exemple https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX.

Étape 3 : Cliquez sur l’icône Thunderbit dans la barre d’outils, puis sur "AI Suggest Fields." L’IA de Thunderbit analyse la page et détecte automatiquement des colonnes comme Job Title, Company, Location, Salary, Job URL et Posted Date. Vous pouvez vérifier et ajuster les champs proposés — supprimer ceux dont vous n’avez pas besoin, ou ajouter des champs personnalisés en décrivant ce que vous voulez en langage naturel.

Étape 4 : Cliquez sur "Scrape." Thunderbit extrait les données de la page et les affiche dans un tableau structuré. Vous devriez voir des lignes d’offres avec les champs configurés.

Enrichir avec l’extraction des sous-pages

Après avoir extrait la page de listes, cliquez sur "Scrape Subpages" pour que Thunderbit visite chaque fiche d’offre individuellement. Il récupère les descriptions complètes, qualifications, avantages et liens de candidature — sans configuration supplémentaire. C’est l’équivalent d’écrire un second scraper Python pour visiter chaque URL /viewjob?jk=<jobkey>, sauf qu’ici cela prend un clic.

Gérer la pagination automatiquement

Thunderbit gère automatiquement la pagination par clic d’Indeed. Pas besoin de construire manuellement des URL avec offset ni d’écrire des boucles de pagination. Il clique de page en page et agrège les résultats.

Exporter vers vos outils préférés

Exportez les données extraites vers CSV, Excel, Google Sheets, Airtable ou Notion — . Pas besoin d’écrire du code csv.writer() ou pandas.to_csv().

Quand utiliser Python ou Thunderbit ?

ScénarioOutil recommandé
Pipelines de données personnalisés, automatisation planifiée via cron/AirflowPython
Intégration dans une base de code existantePython
Logique d’analyse très personnaliséePython
Étude ponctuelle ou analyse de marchéThunderbit
Une équipe non technique a besoin des donnéesThunderbit
Obtenir les données immédiatement sans déboguer les 403Thunderbit
Enrichissement des sous-pages sans configurationThunderbit

Comparaison du temps nécessaire : configuration Python + débogage anti-bot = de plusieurs heures à plusieurs jours (surtout la première fois). Thunderbit = moins de 2 minutes pour les mêmes données. Je ne dis pas que Python est faux — je dis que tout dépend de ce dont vous avez besoin.

Le scraping d’Indeed est-il légal ? Ce qu’il faut savoir

Aucun des guides les mieux classés sur le scraping d’Indeed n’aborde la légalité, ce qui est surprenant vu la fréquence de la question "Le scraping d’Indeed est-il légal ?" dans les forums. Ceci ne constitue pas un avis juridique, mais voici le contexte.

Les conditions d’utilisation d’Indeed

Les CGU d’Indeed () ne contiennent pas de clause générale interdisant le scraping. La seule interdiction explicite liée à l’automatisation se trouve à la section A.3.5, qui interdit "l’utilisation de toute automatisation, script ou bot pour automatiser le processus Indeed Apply." Cela vise spécifiquement le flux de candidature, pas la lecture passive des offres publiques. Le principal outil d’Indeed est technique — challenges Cloudflare, bannissement d’IP, empreinte d’appareil — pas judiciaire.

Jurisprudence pertinente

L’affaire américaine la plus citée est hiQ Labs v. LinkedIn. La Cour du 9e circuit a que l’extraction de données publiquement accessibles « ne viole probablement pas le CFAA » (Computer Fraud and Abuse Act). Cependant, hiQ a ensuite été jugée parce que ses employés avaient créé de faux profils LinkedIn et accepté les CGU.

Plus récemment, Meta v. Bright Data (N.D. Cal., janvier 2024) a donné une décision encore plus claire. Le juge Chen a que les conditions d’utilisation de Facebook et Instagram « n’interdisent pas l’extraction de données publiques lorsque l’utilisateur est déconnecté ». Meta a volontairement retiré les autres griefs le mois suivant.

Le robots.txt d’Indeed

Le d’Indeed interdit largement /jobs/ et /job/ pour le User-agent: * par défaut, mais autorise explicitement Googlebot et Bingbot à accéder à /viewjob?, c’est-à-dire aux pages de détail d’une offre. Les robots d’entraînement IA (GPTBot, CCBot, anthropic-ai) sont fortement restreints. Le robots.txt n’a pas de valeur juridique contraignante aux États-Unis, mais le respecter reste une bonne pratique et une preuve de bonne foi.

Bonnes pratiques pour un scraping responsable

  • N’extrayez que des données publiques — ne vous connectez jamais, ne créez jamais de faux comptes
  • Respectez les limites de débit : 1 requête toutes les 3 à 6 secondes par IP, avec une concurrence faible
  • Ne republiez pas les données extraites comme si c’était votre propre site d’offres d’emploi
  • Utilisez les données pour des recherches personnelles ou internes, pas pour les revendre sans autorisation
  • Supprimez ou hachez les données personnelles inutiles ; fixez une limite de conservation pour les données proches de données personnelles
  • Si vous opérez à grande échelle ou dans l’UE / le Royaume-Uni, consultez un avocat — les obligations de transparence de l’article 14 du RGPD s’appliquent aux données personnelles extraites

Le niveau de risque est progressif : l’automatisation de recherche d’emploi à usage personnel est en bas de l’échelle. La revente commerciale à grande échelle des données d’Indeed est en haut.

Conclusion et points clés à retenir

Extraire Indeed avec Python est faisable, mais ce n’est pas un projet de week-end qu’on lance et qu’on oublie. La protection Cloudflare d’Indeed, les sélecteurs qui changent et les mécanismes anti-bot agressifs signifient que vous devez aborder le sujet avec les bons outils et les bonnes attentes.

Voici ce qu’il faut retenir :

  • Indeed est l’une des sources les plus riches de données sur le marché de l’emploi sur le web — 350 millions de visiteurs mensuels, 130 millions d’annonces — mais il résiste fortement aux scrapers.
  • L’extraction du JSON caché (window.mosaic.providerData) est l’approche Python la plus robuste. Le schéma est stable depuis des années, alors que les sélecteurs CSS cassent chaque mois.
  • curl_cffi avec imitation de navigateur est le client HTTP standard 2026 pour les sites protégés par Cloudflare. requests et httpx classiques sont bloqués rien qu’à cause de l’empreinte TLS.
  • Utilisez toujours des en-têtes rotatifs, des délais aléatoires et des proxies résidentiels pour éviter les erreurs 403. Les proxies de datacenter sont quasiment inutiles contre Cloudflare Enterprise.
  • Ajoutez des assertions pour savoir immédiatement quand vos sélecteurs cassent ou quand vous recevez une page de challenge à la place des données.
  • Pour les non-techniciens ou toute personne qui veut des résultats rapidement, propose une solution sans code, alimentée par l’IA, qui s’adapte automatiquement aux changements du site — sans proxies, sans débogage, sans maintenance.

Si vous voulez tester la solution sans code, afin de l’essayer sur Indeed sans engagement. Et si vous partez sur Python, les exemples ci-dessus constituent une base solide — mais souvenez-vous que la résistance anti-bot doit être une priorité, pas une réflexion après coup.

Pour aller plus loin sur les approches et outils de web scraping, consultez nos guides sur , et . Vous pouvez aussi regarder des tutoriels sur la .

Essayez Thunderbit pour extraire plus vite les données Indeed

FAQ

Quelles bibliothèques Python sont les meilleures pour scraper Indeed ?

Pour les requêtes HTTP, curl_cffi est le meilleur choix en 2026 — il imite les empreintes TLS d’un vrai navigateur, ce qui est essentiel pour contourner Cloudflare. httpx avec HTTP/2 reste un bon plan B pour les cibles moins protégées. Pour le parsing HTML, BeautifulSoup4 avec lxml demeure la référence. Pour l’automatisation de navigateur, Playwright (avec playwright-stealth) ou undetected-chromedriver fonctionnent, même si les deux sont de plus en plus détectables. L’approche par regex sur le JSON caché (window.mosaic.providerData) évite quasiment tout parsing lourd.

Pourquoi est-ce que je reçois sans cesse des erreurs 403 en scrapant Indeed ?

Indeed utilise Cloudflare Bot Management, qui examine votre empreinte TLS (JA3/JA4), l’ordre des en-têtes HTTP/2, vos schémas de requêtes et le comportement du navigateur. Si vous utilisez requests tel quel, votre empreinte TLS vous identifie immédiatement comme un script Python — le 403 arrive avant même que les en-têtes soient lus. Corrigez cela en passant à curl_cffi avec imitation de navigateur, en faisant tourner des User-Agent réalistes, en ajoutant des délais aléatoires (3 à 6 secondes) et en utilisant des proxies résidentiels. Vérifiez aussi le cas du « 200 avec challenge Turnstile » — inspectez le corps de réponse à la recherche de cf-turnstile.

Peut-on extraire Indeed sans coder ?

Oui. Des outils comme permettent d’extraire les offres Indeed en quelques clics — installez l’extension Chrome, ouvrez une page de recherche Indeed, cliquez sur "AI Suggest Fields", puis sur "Scrape." L’IA de Thunderbit détecte automatiquement les champs comme le poste, l’entreprise, la localisation et le salaire. Elle gère automatiquement la pagination, l’enrichissement des sous-pages (descriptions complètes) et les protections anti-bot. Exportez gratuitement vers CSV, Google Sheets, Airtable ou Notion.

À quelle fréquence Indeed change-t-il sa structure HTML ?

Indeed modifie régulièrement les noms de classes CSS (par exemple css-1m4cuuf, des chaînes hachées générées aléatoirement) et restructure les éléments DOM sans prévenir. Les tests A/B font que différents utilisateurs peuvent voir des mises en page différentes en même temps. L’approche JSON caché (window.mosaic.providerData) est nettement plus stable — le schéma est cohérent depuis au moins 2023. Quand vous devez utiliser des sélecteurs DOM, ciblez les attributs data-testid et data-jk (job key) plutôt que les classes CSS.

Est-il légal de scraper Indeed ?

L’extraction, sans connexion, de pages Indeed publiques est peu susceptible d’entraîner une responsabilité au titre du CFAA aux États-Unis, au vu de la décision hiQ v. LinkedIn (2022) de la Cour du 9e circuit et de l’affaire Meta v. Bright Data (2024). Les CGU d’Indeed interdisent spécifiquement l’automatisation du processus de candidature, pas la lecture passive des annonces publiques. Cela dit, scrappez toujours de manière responsable : ne vous connectez pas, ne créez pas de faux comptes, respectez les limites de débit, ne republiez pas les données comme votre propre site d’offres d’emploi, et traitez avec soin toute donnée personnelle (noms de recruteurs, emails) conformément au RGPD / CCPA. Pour une exploitation commerciale à grande échelle, consultez un avocat.

En savoir plus

Fawad Khan
Fawad Khan
Fawad writes for a living, and honestly, he kind of loves it. He's spent years figuring out what makes a line of copy stick — and what makes readers scroll past. Ask him about marketing, and he'll talk for hours. Ask him about carbonara, and he'll talk longer.
Table des matières

Essaie Thunderbit

Extrayez des leads et d’autres données en seulement 2 clics. Propulsé par l’IA.

Obtenir Thunderbit C’est gratuit
Extraire des données grâce à l’IA
Transfère facilement les données vers Google Sheets, Airtable ou Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week