Cet article fait partie de la série « Un projet Data Science de bout en bout ». J’y présente les travaux réalisés dans le cadre de la formation Big Data pour l’entreprise numérique proposée par Centrale Supelec Exced.

Ce projet a pour objectif d’analyser les offres d’emploi Ressources Humaines afin d’identifier automatiquement des tendances. Cet article présente comment j’ai automatisé la récolte des offres d’emploi sur LinkedIn.

Avertissement : cet article contient du code pouvant heurter la sensibilité des plus jeunes. L’ensemble du code est disponible ici.

Avertissement 2 : cet article est rédigé à des fins pédagogiques.

Un script de web scraping basique demande l’envoi d’une requête http et le téléchargement du contenu de la réponse. Néanmoins, LinkedIn, propriété de Microsoft, est un acteur majeur du web. Ils ont mis en place des sécurités qui bloquent cette démarche.

Nous avons réalisé deux scripts de web scraping :

  • Un premier, pour obtenir la liste des offres d’emploi RH
  • Un second, pour obtenir le détail des offres d’emploi.

Afin de lister les offres d’emploi RH, il faut commencer par obtenir l’url correspondant sur LinkedIn. Pour cela, nous avons ouvert la page des offres d’emploi et avons défini les critères directement dans la page :

Keywords = « Ressources Humaines » ; Location = « France »

Nous avons ainsi obtenu l’url suivant :

https://www.linkedin.com/jobs/search?keywords=Ressources%20humaines&location=France

La première étape consiste donc à ouvrir la page web dans un environnement contrôlé par python. Pour cela, nous avons utilisé le webdriver de Selenium.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

url_rh = "https://www.linkedin.com/jobs/search?keywords=Ressources%20humaines&location=France"

wd = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
wd.get(url_rh)

La page des résultats s’ouvre dans une fenêtre Chrome. Cependant, par défaut, LinkedIn n’affiche que 25 résultats. Pour obtenir les 1000 résultats possiblement affichés par LinkedIn dans une navigation normale, il faut dérouler les résultats.

Encore une fois, il est possible d’automatiser cette opération par une succession de scroll et de clique .

t_scroll = 5

i = 0
while i <= int(44): 
    wd.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    i = i + 1
    print(i)
    try:
        wd.find_element(By.XPATH,"/html/body/div[3]/div/main/section[2]/button").click()
        time.sleep(t_scroll)
    except:
        pass
        time.sleep(t_scroll)

jobs_lists = wd.find_element(By.CLASS_NAME,"jobs-search__results-list")
jobs = jobs_lists.find_elements(By.TAG_NAME,"li") # return a list

Nous mettons en place des durées d’attente afin de simuler un comportement humain et passer outre les contrôles de LinkedIn.

Voici en live le comportement du script sur l’ordinateur qui l’exécute. C’est satisfaisant n’est-ce pas ?

Après environ 15 minutes, nous disposons d’une page affichant environ 1000 offres d’emploi. Il est désormais possible de collecter toutes les données.

Pour cela, nous exécutons sur l’ensemble des résultats présents sur la page une extraction des données recherchées. Nous avons constaté que LinkedIn présentait deux structures de page html pour les offres d’emploi. Nous avons utilisé la fonction try except afin d’exécuter le bon code selon la structure affichée.

link=[]
id_track=[]

while i <= len(jobs) :

    # ID search - sert à identifier la recherche. A priori la date devrait suffire
    #xpath_idsearch = "/html/body/div[3]/div/main/section[2]/ul/li[{}]/div".format(i)               
    #data_idsearch = wd.find_element_by_xpath(xpath_idsearch)
    #id_search0 = data_idsearch.get_attribute("data-search-id")
    #id_search.append(id_search0)
    

   try :
        # Lien de l'offre
        xpath_link = "/html/body/div[3]/div/main/section[2]/ul/li[{}]/div/a".format(i)
        data_link = wd.find_elements(By.XPATH,xpath_link)
        link0 = data_link[0].get_attribute("href")
        link.append(link0)

    except :
        # Lien de l'offre
        xpath_link = "/html/body/div[3]/div/main/section[2]/ul/li[{}]/a".format(i)
        data_link = wd.find_elements(By.XPATH,xpath_link)
        link0 = data_link[0].get_attribute("href")
        link.append(link0)

    i=i+1


df = pd.DataFrame(list(zip(id_track, link)),columns=["id_track","Link"])

df["date de recherche"]= today

name_file = "extractions/"+str(today) + " - linkedin jobs - synthétique.xlsx"
df.to_excel(name_file)

Le code ci-dessous extrait la donnée « Link ». Nous avons réalisé l’extraction pour les données suivantes *:

NomDescription
LinkLe lien pour consulter l’offre d’emploi
CompanyLe nom de l’entreprise qui recrute
Job_nameL’intitulé de l’offre d’emploi
Id_searchL’identifiant de la recherche réalisée
Id_trackL’identifiant attribué par LinkedIn à l’offre d’emploi
LocationLe lieu d’exercice de l’emploi

Nous avons des éléments génériques sur les offres d’emploi collectées. Il est désormais temps de collecter le contenu des offres d’emploi.

Dans le script précédent, nous avons collecté les liens vers les offres d’emploi. L’objectif de ce script sera donc de visiter chacune des pages d’offre d’emploi afin de télécharger l’intégralité du contenu de l’offre d’emploi.

Voici le script que nous avons développé :

description =[]

i=0

# Itération sur la liste des liens obtenus à partir de la recherche
for elements in df["Link"] :
    print(i)
    i=i+1

    # Ouverture d'une instance WebDriver
    wd1 = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
    wd1.get(elements)
    
    #Pause
    time.sleep(3)


    # Récupération de la desription générale du poste
    try :
        xpath_description = "/html/body/main/section[1]/div/div[1]/section[1]/div/div/section/div"
        data_description = wd1.find_element(By.XPATH,xpath_description)
        description0 = data_description.get_attribute("innerText")
        
 
    except :
        description0=""
    
    description.append(description0)


df1 = pd.DataFrame(list(zip(id_track,description)),columns=["id_track","Description"])

name_file_detail = "extractions/"+str(today) + " - linkedin jobs - détail.xlsx"
df1.to_excel(name_file_detail)

Encore une fois, voici une vidéo pour observer le script s’exécuter sur l’ordinateur. C’est toujours aussi satisfaisant !

Le web scraping est bien pratique pour collecter des données. Néanmoins, il existe un cadre juridique qu’il faut avoir en tête afin de respecter les propriétaires des bases de données.

La loi donne au producteur d’une base de données le droit d’interdire certaines formes d’extractions et d’utilisation de la base de données si :

  • L’extraction des données « par transfert permanent ou temporaire de la totalité ou d’une partie qualitativement ou quantitativement substantielle du contenu d’une base de données sur un autre support, par tout moyen et sous toute forme que ce soit » (CPI, art. L. 342-1, al. 1).
  • La réutilisation des données « par la mise à la disposition du public de la totalité ou d’une partie qualitativement ou quantitativement substantielle du contenu de la base, quelle qu’en soit la forme. » (CPI, art. L. 342-1, al. 2)

Néanmoins, le producteur d’une base de données ne peut interdire l’extraction d’une base de données pour :

  • Certaines utilisations à des fins pédagogiques ou de recherche ne donnant lieu à aucune exploitation commerciale (CPI art. L. 342-3, 4°). C’est le cas, puisque notre travail s’inscrit dans la démarche du mémoire. Nous avons effectivement l’intention d’exploiter ces travaux dans un cadre professionnel mais sans monétisation. La question méritera d’être approfondie.
  • Certaines reproductions provisoires présentant un caractère transitoire ou accessoire, mais lorsqu’elles sont une partie intégrante et essentielle d’un procédé technique. C’est le cas ici puisque nous avons l’intention d’appliquer d’importantes transformations sur la base de données avant de l’exploiter.

J’ai réalisé ces travaux dans le cadre de la formation « Big data pour l’entreprise Numérique » à Centrale Supelec Exced. Nous entrons donc bien dans les finalités pédagogiques. C’est à cette même fin que le présent article est rédigé.

Par ailleurs, la suite des travaux présente comment les données collectées ont été intégrées dans un « procédé technique » de transformation.

Cliquez ici pour découvrir la suite du projet Data Science de bout en bout.

2 commentaires sur “Webscraping des offres d’emploi sur LinkedIn”

  1. Avatar

    Je vous remercie infiniment. Vous venez de m’aider à trouver la solution que je cherchais depuis des semaines sur comment scraper Linkedin. Je ne parle pas au niveau du code, mais simplement pour le lien. Car celui-ci va me permettre de pouvoir faire ce que je souhaitais.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *