Les offres d'emploi publiques de LinkedIn sont une source riche de données d'embauche : intitulés de postes, entreprises, localisations, dates de publication et les facettes de recherche qui indiquent l'évolution de la demande. Le problème, c'est d'accéder à ces données par voie programmatique. LinkedIn rend ses pages côté client et challenge agressivement le trafic automatisé, donc une simple requête HTTP vous retourne une coquille vide plutôt que des offres d'emploi. Ce guide vous montre comment scraper les offres d'emploi publiques LinkedIn en Python : un petit script exécutable qui récupère la page rendue via la Crawling API, extrait les champs souhaités et les écrit sur disque.
Pour rester honnêtes, tout ce tutoriel se limite aux données publiques : les offres d'emploi que chacun peut voir sur les pages de recherche d'emploi publiques de LinkedIn sans se connecter. Il ne touche pas aux comptes, au contenu protégé par connexion ni aux profils personnels. La section juridique ci-dessous trace clairement cette limite, et ce n'est pas un simple boilerplate, alors lisez-la avant de pointer ce script sur un volume réel.
Est-il légal de scraper LinkedIn ?
Lisez cette section en premier, car LinkedIn est l'une des cibles les plus sensibles du web et la réponse honnête est "ça dépend, et la portée est déterminante." Ce guide se limite délibérément aux offres d'emploi publiques et non authentifiées : les pages que LinkedIn sert à tout le monde, connecté ou non, sur sa surface de recherche d'emploi publique. Dans ce cadre, quelques règles sont non négociables :
- Ne scraper pas derrière l'authentification. Si une page nécessite une connexion pour être vue, elle est hors du périmètre de ce guide. Pas de cookies de session, pas de réutilisation de credentials, aucun contournement d'authentification d'aucune sorte.
- Ne collectez pas de données personnelles ni de profils personnels. Les noms, coordonnées, graphes de connexion et pages de profil individuels sont exclus. Ce tutoriel collecte des métadonnées d'offres d'emploi, pas des données sur des personnes identifiables.
- Ne violez pas le Contrat Utilisateur de LinkedIn. Lisez les Conditions d'utilisation de LinkedIn et son robots.txt avant de commencer, et respectez ce qu'ils indiquent. Respectez les attentes de débit déclarées et maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger les serveurs.
Sur le contexte juridique : dans l'affaire hiQ Labs c. LinkedIn, les tribunaux américains ont traité du scraping de données publiquement accessibles et, à un niveau général, ont conclu que l'accès aux pages publiques ne violait pas en soi le Computer Fraud and Abuse Act. Cette affaire est souvent citée comme couverture pour scraper LinkedIn, mais ce n'est pas une autorisation générale. Les conditions contractuelles (le Contrat Utilisateur), la législation sur la vie privée et l'usage que vous faites des données s'appliquent tous, et le paysage continue d'évoluer. Traitez la ligne des données publiques comme le plancher, pas comme une faille.
Tout dans ce guide collecte des métadonnées d'offres d'emploi publiques : intitulés de postes, entreprises, localisations et dates de publication que chacun peut voir sans compte. Il ne couvre pas les données protégées par connexion, les profils personnels, les données de connexion, la messagerie ni aucun contournement d'authentification. Si votre projet nécessite plus que les annonces publiques, la bonne démarche est un partenariat officiel ou un accord de données avec LinkedIn, pas un scraper plus sophistiqué.
Pourquoi une simple requête échoue sur LinkedIn
Faites une requête à une URL de recherche d'emploi LinkedIn avec un client HTTP basique et vous obtenez une réponse 200 avec presque aucune donnée d'emploi dans le corps. Deux choses jouent contre vous. Premièrement, LinkedIn rend ses annonces dans le navigateur avec JavaScript, donc le HTML initial est une coquille qui se remplit seulement une fois les scripts de la page exécutés. Deuxièmement, LinkedIn signale rapidement le trafic automatisé : les adresses IP de datacenter et les modèles de requêtes qui ne ressemblent pas à un vrai visiteur sont challengés ou bloqués avant d'atteindre le contenu rendu.
Un scraper LinkedIn fonctionnel nécessite donc deux choses dans une seule requête : un navigateur qui rende la page, et une IP que la plateforme perçoit comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur headless plus un pool de proxies résidentiels rotatifs, mais maintenir cet ensemble en bon état représente l'essentiel du travail. La Crawling API réunit 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 retourne le HTML final.
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. LinkedIn 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.
Comprendre la cible : l'URL de recherche d'emploi publique de LinkedIn
La recherche d'emploi publique de LinkedIn se trouve à une URL prévisible dont les paramètres de requête correspondent directement au formulaire de recherche, vous permettant ainsi de construire n'importe quelle recherche par programmation sans piloter l'interface. Voici un exemple concret : les postes de développeur Python à Londres.
https://www.linkedin.com/jobs/search?keywords=Python%20Developer&location=London
Les paramètres qui comptent :
- keywords le poste ou la compétence que vous recherchez, encodé en URL.
- location la ville, la région ou le pays dans lequel chercher.
Construisez l'URL avec les paramètres qui vous intéressent et vous avez une cible reproductible. Variez les keywords et la location dans une boucle et vous avez un job de suivi des tendances d'embauche qui surveille la demande publique au fil du temps.
Configurer votre environnement
Vous avez besoin de Python 3.8 ou version ultérieure. Vérifiez votre version, créez un environnement virtuel pour isoler les dépendances du projet, puis installez les bibliothèques.
python --version python -m venv linkedin_env source linkedin_env/bin/activate pip install crawlbase beautifulsoup4
Sur Windows, activez l'environnement avec linkedin_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 en extraire les champs. Vous avez également besoin d'un compte Crawlbase et d'un token JS, que vous obtenez depuis le tableau de bord après vous être inscrit. Placez-le dans le code partout où vous voyez YOUR_CRAWLBASE_JS_TOKEN.
Récupérer la page de recherche d'emploi rendue
Commencez par obtenir la page finale. Vous passez deux options importantes pour un site comme LinkedIn : ajax_wait indique à l'API d'attendre que le contenu asynchrone se charge, et page_wait maintient un délai fixe en millisecondes après le chargement pour que les annonces à rendu tardif aient le temps d'apparaître. Cinq secondes est un bon point de départ ; augmentez si les résultats reviennent maigres.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) options = {"ajax_wait": "true", "page_wait": 5000} jobs_url = "https://www.linkedin.com/jobs/search?keywords=Python%20Developer&location=London" def fetch_jobs_html(url): response = api.get(url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print("Failed to fetch the page. Status code:", response["status_code"]) return None html = fetch_jobs_html(jobs_url) print(html[:500])
Exécutez-le et vous devriez voir le vrai balisage avec des cartes d'offres d'emploi, pas la coquille vide qu'une simple requête retourne. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.
LinkedIn nécessite 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 adresses IP résidentielles côté serveur, et vous livre le HTML final, vous épargnant ainsi la gestion d'une flotte headless et d'un pool de proxies. Pointez-la vers une recherche d'emploi publique sur le niveau gratuit d'abord.
Analyser les offres d'emploi
Avec le HTML en main, chargez-le dans BeautifulSoup et parcourez les cartes d'offres d'emploi. Chaque carte sur la page de recherche publique contient les champs souhaités : intitulé du poste, nom de l'entreprise, localisation et date de publication. Inspectez la page en direct dans les outils de développement de votre navigateur pour trouver les sélecteurs actuels, puis mappez chaque champ à l'un d'eux.
from bs4 import BeautifulSoup def extract_jobs(html): soup = BeautifulSoup(html, "html.parser") jobs = [] for card in soup.select("div.base-card"): title = card.select_one("h3.base-search-card__title") company = card.select_one("h4.base-search-card__subtitle") location = card.select_one("span.job-search-card__location") posted = card.select_one("time") jobs.append({ "title": title.get_text(strip=True) if title else "", "company": company.get_text(strip=True) if company else "", "location": location.get_text(strip=True) if location else "", "posted": posted["datetime"] if posted else "", }) return jobs
Les noms de classes de LinkedIn changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas un contrat. Quand l'extraction retourne des chaînes vides, inspectez à nouveau une page de recherche d'emploi publique en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. C'est une maintenance normale pour tout scraper en production, pas le signe que quelque chose est cassé.
Reliez la récupération et l'analyse dans une fonction main pour avoir un script exécutable unique.
def main(): html = fetch_jobs_html(jobs_url) if not html: return jobs = extract_jobs(html) for job in jobs: print(job) if __name__ == "__main__": main()
À quoi ressemble le résultat
Exécutez le script complet et vous obtenez une liste d'objets d'offres d'emploi structurés. Un exemple réduit :
[ { "title": "Senior Python Developer", "company": "Monzo Bank", "location": "London, England, United Kingdom", "posted": "2026-01-14" }, { "title": "Python Backend Engineer", "company": "Deliveroo", "location": "London, England, United Kingdom", "posted": "2026-01-12" } ]
Gérer la pagination
La recherche d'emploi publique affiche un premier lot d'annonces et en révèle davantage au fur et à mesure du défilement ou de la navigation dans les pages. La façon la plus propre de les parcourir est le paramètre de requête start, qui décale les résultats : start=0 est la première page, start=25 la suivante, et ainsi de suite. Bouclez sur le décalage, récupérez chaque page via la Crawling API et collectez les lignes.
all_jobs = [] base = "https://www.linkedin.com/jobs/search?keywords=Python%20Developer&location=London" for start in range(0, 75, 25): page_url = f"{base}&start={start}" html = fetch_jobs_html(page_url) if not html: break all_jobs.extend(extract_jobs(html)) print(f"Collected {len(all_jobs)} listings")
Gardez le nombre de pages modeste et cadencez la boucle. Parcourir trois pages pour échantillonner une recherche est très différent de balayer des milliers de décalages dans une boucle serrée, et ce second modèle est exactement ce qui fait throttler un scraper.
Sauvegarder les résultats en CSV
Afficher dans la console est bien pendant le développement, mais vous voulez les données sur disque. Le module intégré csv de Python mappe chaque clé d'objet à une colonne et écrit vos lignes en quelques lignes, sans dépendance supplémentaire.
import csv def save_to_csv(jobs, path="linkedin_jobs.csv"): fields = ["title", "company", "location", "posted"] with open(path, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(jobs) print(f"Saved {path}")
Appelez save_to_csv(all_jobs) après la boucle de pagination et chaque exécution écrit un linkedin_jobs.csv soigné que vous pouvez ouvrir dans n'importe quel tableur ou charger dans un pipeline. Si vous préférez interroger les données avec SQL, écrivez les mêmes lignes dans une table SQLite ; l'analyse reste identique.
Rester débloqué
Même avec le rendu géré, LinkedIn 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. Marteler la même recherche dans une boucle serrée est la façon la plus rapide d'être throttlé. Espacez les requêtes et variez vos keywords et localisation.
- Misez sur la rotation. Un pool de proxies résidentiels répartit les requêtes sur de nombreuses IP d'utilisateurs réels pour qu'aucune adresse individuelle ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous préférez router votre propre trafic, le Smart AI Proxy vous donne la même rotation d'IP résidentielles comme point de terminaison proxy drop-in.
- 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 les codes d'erreur de statut de proxy comme un signal, pas du bruit, et reculez quand vous les voyez apparaître.
Pour le guide complet, consultez comment scraper des sites web sans se faire bloquer.
Points clés
- Restez sur les données d'offres d'emploi publiques. Ce guide collecte uniquement les métadonnées d'offres d'emploi publiques. Pas de pages protégées par connexion, pas de profils personnels, pas de contournement d'authentification ; respectez le Contrat Utilisateur et le robots.txt de LinkedIn.
- LinkedIn est rendu côté client. Une simple requête retourne une coquille vide, donc vous devez rendre la page 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_waitetpage_waitcontrôlent le temps d'attente pour le contenu. -
La pagination est un décalage. Incrémentez le paramètre
startpar pas de 25 pour parcourir les pages supplémentaires de résultats publics. - Rotation et cadence pour rester débloqué. La Crawling API fait tourner les IP pour vous ; le Smart AI Proxy est l'option drop-in si vous routez votre propre trafic.
Foire aux questions
Est-il légal de scraper LinkedIn ?
Cela dépend de ce que vous scrappez et de l'usage que vous en faites. Ce guide se limite strictement aux offres d'emploi publiques et non authentifiées et évite les profils personnels, le contenu protégé par connexion et le contournement d'authentification. Même là, le Contrat Utilisateur de LinkedIn, le robots.txt et les lois sur la vie privée applicables s'appliquent tous. L'affaire hiQ c. LinkedIn a traité l'accès aux données publiques à un niveau général, mais ce n'est pas une autorisation générale. Lisez les conditions de LinkedIn d'abord et limitez votre portée aux données publiques.
Pourquoi une simple requête ne retourne-t-elle pas de données d'emploi de LinkedIn ?
Parce que LinkedIn rend ses annonces côté client avec JavaScript. Le HTML initial est une coquille qui se remplit seulement après que les scripts de la page s'exécutent dans un navigateur, donc une requête HTTP brute retourne le statut 200 avec les champs d'emploi vides. Pour obtenir de vraies données, vous devez d'abord rendre la page, ce que le token JS de la Crawling API gère pour vous.
Ai-je besoin du token normal ou du token JS pour LinkedIn ?
Le token JS. Le token normal récupère du HTML statique, qui sur LinkedIn 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 annonces sont présentes quand BeautifulSoup les analyse.
Mes sélecteurs retournent des chaînes vides. Qu'est-ce qui a changé ?
Presque certainement le balisage de LinkedIn. Ses noms de classes changent sans préavis, donc des sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Inspectez à nouveau une page de recherche d'emploi publique 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 éviter d'être bloqué lors du scraping de LinkedIn ?
Maintenez votre taux de requêtes par IP bas, variez vos keywords et localisation au lieu de boucler sur la même URL, 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, le Smart AI Proxy vous donne cette rotation comme point de terminaison drop-in. Surveillez les codes de statut et reculez quand des challenges apparaissent.
Puis-je scraper des profils LinkedIn ou des messages avec cela ?
Non, et vous ne devriez pas essayer. Ce guide se limite aux offres d'emploi publiques délibérément. Les profils personnels, les données de connexion et la messagerie se trouvent derrière une authentification et impliquent des données personnelles, ce qui est hors du périmètre ici. Si votre projet nécessite plus que les offres d'emploi publiques, poursuivez un partenariat officiel LinkedIn ou un accord de données plutôt que de scraper des pages authentifiées.
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.
