Meilleur web scraping en Python avec Selenium, Beautiful Soup et Pandas

Raclage Web

En utilisant le langage de programmation Python, il est possible de «récupérer» des données du Web de manière rapide et efficace.

Le web scraping est défini comme:

un outil pour transformer les données non structurées sur le Web en données structurées lisibles par machine qui sont prêtes pour l'analyse. (la source)

Le scraping Web est un outil précieux dans les compétences du data scientist.

Maintenant, quoi gratter?

Données accessibles au public

Le site Web KanView soutient la «Transparence au sein du gouvernement». C'est aussi le slogan du site. Le site fournit des données de paie pour l'État du Kansas. Et c'est génial!

Pourtant, comme de nombreux sites Web gouvernementaux, il enfouit les données dans des liens et des tableaux détaillés. Cela nécessite souvent une «navigation optimale» pour trouver les données spécifiques que vous recherchez. Je voulais utiliser les données publiques fournies pour les universités du Kansas dans un projet de recherche. Récupérer les données avec Python et les enregistrer au format JSON était ce que je devais faire pour commencer.

Les liens JavaScript augmentent la complexité

Le scraping Web avec Python ne nécessite souvent que l'utilisation du module Beautiful Soup pour atteindre l'objectif. Beautiful Soup est une bibliothèque Python populaire qui facilite la mise en œuvre du web scraping en parcourant le DOM (document object model).

Cependant, le site Web KanView utilise des liens JavaScript. Par conséquent, les exemples utilisant Python et Beautiful Soup ne fonctionneront pas sans quelques ajouts supplémentaires.

Le sélénium à la rescousse

Le package Selenium est utilisé pour automatiser l'interaction du navigateur Web à partir de Python. Avec Selenium, la programmation d'un script Python pour automatiser un navigateur Web est possible. Ensuite, ces liens JavaScript embêtants ne sont plus un problème.

from selenium import webdriver from selenium.webdriver.common.keys import Keys from bs4 import BeautifulSoup import re import pandas as pd import os

Selenium va maintenant démarrer une session de navigateur. Pour que Selenium fonctionne, il doit accéder au pilote du navigateur. Par défaut, il cherchera dans le même répertoire que le script Python. Liens vers les pilotes Chrome, Firefox, Edge et Safari disponibles ici. L'exemple de code ci-dessous utilise Firefox:

#launch url url = "//kanview.ks.gov/PayRates/PayRates_Agency.aspx" # create a new Firefox session driver = webdriver.Firefox() driver.implicitly_wait(30) driver.get(url) python_button = driver.find_element_by_id('MainContent_uxLevel1_Agencies_uxAgencyBtn_33') #FHSU python_button.click() #click fhsu link

Ce qui python_button.click()précède indique à Selenium de cliquer sur le lien JavaScript sur la page. Après être arrivé à la page des titres de poste, Selenium transfère la source de la page à Beautiful Soup.

Transition vers une belle soupe

Beautiful Soup reste le meilleur moyen de parcourir le DOM et de gratter les données. Après avoir défini une liste vide et une variable de compteur, il est temps de demander à Beautiful Soup de saisir tous les liens de la page qui correspondent à une expression régulière:

#Selenium hands the page source to Beautiful Soup soup_level1=BeautifulSoup(driver.page_source, 'lxml') datalist = [] #empty list x = 0 #counter for link in soup_level1.find_all('a', id=re.compile("^MainContent_uxLevel2_JobTitles_uxJobTitleBtn_")): ##code to execute in for loop goes here

Vous pouvez voir dans l'exemple ci-dessus que Beautiful Soup récupérera un lien JavaScript pour chaque titre de poste au sein de l'agence d'État. Maintenant, dans le bloc de code de la boucle for / in, Selenium cliquera sur chaque lien JavaScript. Beautiful Soup récupérera alors le tableau de chaque page.

#Beautiful Soup grabs all Job Title links for link in soup_level1.find_all('a', id=re.compile("^MainContent_uxLevel2_JobTitles_uxJobTitleBtn_")): #Selenium visits each Job Title page python_button = driver.find_element_by_id('MainContent_uxLevel2_JobTitles_uxJobTitleBtn_' + str(x)) python_button.click() #click link #Selenium hands of the source of the specific job page to Beautiful Soup soup_level2=BeautifulSoup(driver.page_source, 'lxml') #Beautiful Soup grabs the HTML table on the page table = soup_level2.find_all('table')[0] #Giving the HTML table to pandas to put in a dataframe object df = pd.read_html(str(table),header=0) #Store the dataframe in a list datalist.append(df[0]) #Ask Selenium to click the back button driver.execute_script("window.history.go(-1)") #increment the counter variable before starting the loop over x += 1

pandas: bibliothèque d'analyse de données Python

Beautiful Soup transmet les découvertes aux pandas. Pandas utilise sa read_htmlfonction pour lire les données de la table HTML dans une trame de données. Le dataframe est ajouté à la liste vide précédemment définie.

Avant que le bloc de code de la boucle ne soit terminé, Selenium doit cliquer sur le bouton Précédent dans le navigateur. C'est ainsi que le lien suivant de la boucle sera disponible pour cliquer sur la page de liste des emplois.

Lorsque la boucle for / in est terminée, Selenium a visité chaque lien de titre de poste. Beautiful Soup a récupéré le tableau de chaque page. Pandas a stocké les données de chaque table dans une trame de données. Chaque dataframe est un élément de la liste de données. Les trames de données de table individuelles doivent maintenant fusionner en une seule trame de données volumineuse. Les données seront ensuite converties au format JSON avec pandas.Dataframe.to_json:

#loop has completed #end the Selenium browser session driver.quit() #combine all pandas dataframes in the list into one big dataframe result = pd.concat([pd.DataFrame(datalist[i]) for i in range(len(datalist))],ignore_index=True) #convert the pandas dataframe to JSON json_records = result.to_json(orient='records')

Maintenant, Python crée le fichier de données JSON. Il est prêt à l'emploi!

#get current working directory path = os.getcwd() #open, write, and close the file f = open(path + "\\fhsu_payroll_data.json","w") #FHSU f.write(json_records) f.close()

Le processus automatisé est rapide

Le processus de raclage Web automatisé décrit ci-dessus se termine rapidement. Selenium ouvre une fenêtre de navigateur que vous pouvez voir fonctionner. Cela me permet de vous montrer une vidéo de capture d'écran de la vitesse du processus. Vous voyez à quelle vitesse le script suit un lien, saisit les données, revient en arrière et clique sur le lien suivant. Cela permet de récupérer les données de centaines de liens en quelques minutes à un chiffre.

Le code Python complet

Voici le code Python complet. J'ai inclus une importation pour tabuler. Il nécessite une ligne de code supplémentaire qui utilisera tabulate pour imprimer les données sur votre interface de ligne de commande:

from selenium import webdriver from selenium.webdriver.common.keys import Keys from bs4 import BeautifulSoup import re import pandas as pd from tabulate import tabulate import os #launch url url = "//kanview.ks.gov/PayRates/PayRates_Agency.aspx" # create a new Firefox session driver = webdriver.Firefox() driver.implicitly_wait(30) driver.get(url) #After opening the url above, Selenium clicks the specific agency link python_button = driver.find_element_by_id('MainContent_uxLevel1_Agencies_uxAgencyBtn_33') #FHSU python_button.click() #click fhsu link #Selenium hands the page source to Beautiful Soup soup_level1=BeautifulSoup(driver.page_source, 'lxml') datalist = [] #empty list x = 0 #counter #Beautiful Soup finds all Job Title links on the agency page and the loop begins for link in soup_level1.find_all('a', id=re.compile("^MainContent_uxLevel2_JobTitles_uxJobTitleBtn_")): #Selenium visits each Job Title page python_button = driver.find_element_by_id('MainContent_uxLevel2_JobTitles_uxJobTitleBtn_' + str(x)) python_button.click() #click link #Selenium hands of the source of the specific job page to Beautiful Soup soup_level2=BeautifulSoup(driver.page_source, 'lxml') #Beautiful Soup grabs the HTML table on the page table = soup_level2.find_all('table')[0] #Giving the HTML table to pandas to put in a dataframe object df = pd.read_html(str(table),header=0) #Store the dataframe in a list datalist.append(df[0]) #Ask Selenium to click the back button driver.execute_script("window.history.go(-1)") #increment the counter variable before starting the loop over x += 1 #end loop block #loop has completed #end the Selenium browser session driver.quit() #combine all pandas dataframes in the list into one big dataframe result = pd.concat([pd.DataFrame(datalist[i]) for i in range(len(datalist))],ignore_index=True) #convert the pandas dataframe to JSON json_records = result.to_json(orient='records') #pretty print to CLI with tabulate #converts to an ascii table print(tabulate(result, headers=["Employee Name","Job Title","Overtime Pay","Total Gross Pay"],tablefmt='psql')) #get current working directory path = os.getcwd() #open, write, and close the file f = open(path + "\\fhsu_payroll_data.json","w") #FHSU f.write(json_records) f.close()

Conclusion

Web scraping with Python and Beautiful Soup is an excellent tool to have within your skillset. Use web scraping when the data you need to work with is available to the public, but not necessarily conveniently available. When JavaScript provides or “hides” content, browser automation with Selenium will insure your code “sees” what you (as a user) should see. And finally, when you are scraping tables full of data, pandas is the Python data analysis library that will handle it all.

Reference:

The following article was a helpful reference for this project:

//pythonprogramminglanguage.com/web-scraping-with-pandas-and-beautifulsoup/

Reach out to me any time on LinkedIn or Twitter. And if you liked this article, give it a few claps. I will sincerely appreciate it.

//www.linkedin.com/in/davidagray/

Dave Gray (@yesdavidgray) | Twitter

The latest Tweets from Dave Gray (@yesdavidgray). Instructor @FHSUInformatics * Developer * Musician * Entrepreneur *…

twitter.com