Web scraping avec Python
Atelier LINUQ pratique : extraction de données météo depuis Environnement Canada avec RoboBrowser, et collecte de données économiques via l’API du Bureau of Labor Statistics américain. Deux approches complémentaires du scraping en Python.
Le 9 décembre 2017, j’ai animé un atelier web scraping avec Python au LINUQ. L’objectif : montrer deux approches complémentaires pour extraire des données sur le web avec Python, en utilisant Jupyter Notebooks.
🌘 Deux approches, deux bibliothèques
Le web scraping, c’est l’art d’extraire des données depuis des pages web de façon automatisée. Selon la source, tu peux soit simuler un navigateur avec RoboBrowser, soit interroger une API directement avec requests.
graph TD
A[Source de données web] --> B{Source offre-t-elle une API?}
B -->|Oui| C[requests + JSON]
B -->|Non| D[RoboBrowser<br>requests + BeautifulSoup]
C --> E[Données structurées fiables]
D --> F[Interagir avec formulaires<br>Parser le HTML]
E --> G[Analyse]
F --> G
On a testé les deux.
🌘 Scraping météo avec RoboBrowser
Premier exemple : récupérer les prévisions météo d’Environnement Canada pour la ville de Québec.
import re
from robobrowser import RoboBrowser
browser = RoboBrowser(history=True, parser="lxml")
browser.open('https://meteo.gc.ca/')
On ouvre la page et on repère le formulaire de sélection de ville :
form = browser.get_form(id="cityjump")
form
Ce qui affiche le formulaire vide :
<RoboForm city=, lang=f, unit=>
On remplit le champ city avec « Québec » :
form['city'].value = 'Québec'
browser.submit_form(form)
Une fois la page de résultats chargée, on extrait les informations avec des sélecteurs CSS. Le site utilise des classes Bootstrap, ce qui facilite le ciblage :
location_xml = browser. \
select('.col-sm-10')[0]. \
select('dl.mrgn-bttm-0')[0]. \
select('dd.mrgn-bttm-0')
Pour extraire les températures jour par jour, on crée une fonction. RoboBrowser ne gère pas le sélecteur nth-child(), mais on peut utiliser les listes Python pour reproduire le même comportement :
def temperature_xml(jour):
return browser. \
select('div.div-column')[jour]. \
select('div')[1]. \
select('p.mrgn-bttm-0')[0]. \
select('span')[0]
Finalement, on assemble le tout dans un dictionnaire Python :
{
'city': location_xml[0].text,
'date': location_xml[1].text,
'temperature': [temperature_xml(i).text for i in range(0, 7)]
}
Résultat :
{
'city': 'Aéroport int. Lesage de Québec',
'date': '13h00 HNE le samedi 9 décembre 2017',
'temperature': ['-1°C', '-2°C', '-13°C', '-4°C', '-8°C', '-11°C', '-11°C']
}
Une belle journée d’hiver à Québec !
Voici le flux de l’interaction avec RoboBrowser :
sequenceDiagram
participant Script
participant Site as meteo.gc.ca
Script->>Site: browser.open()
Script->>Site: get_form(cityjump)
Script->>Site: form.city = "Québec"
Script->>Site: submit_form(form)
Site->>Script: Page de résultats HTML
Script->>Script: CSS selectors (.col-sm-10, dl, dd)
Script->>Script: temperature_xml(jour) en boucle
Script->>Script: Dictionnaire final {city, date, temperature}
🌘 Données économiques avec l’API du BLS
Deuxième exemple : interroger l’API publique du Bureau of Labor Statistics des États-Unis pour récupérer des séries de données sur l’inflation.
import requests
import json
import prettytable
headers = {'Content-type': 'application/json'}
data = json.dumps({
"seriesid": ['CUUR0000SA0', 'SUUR0000SA0'],
"startyear": "2011",
"endyear": "2014"
})
p = requests.post(
'https://api.bls.gov/publicAPI/v2/timeseries/data/',
data=data,
headers=headers
)
La réponse est en JSON. On la parse et on inspecte les en-têtes HTTP :
p.headers
json_data = json.loads(p.text)
On extrait les données et on les formate avec PrettyTable pour produire des fichiers texte lisibles :
for series in json_data['Results']['series']:
x = prettytable.PrettyTable(["series id", "year", "period", "value", "footnotes"])
seriesId = series['seriesID']
for item in series['data']:
year = item['year']
period = item['period']
value = item['value']
footnotes = ""
for footnote in item['footnotes']:
if footnote:
footnotes = footnotes + footnote['text'] + ','
if 'M01' <= period <= 'M12':
x.add_row([seriesId, year, period, value, footnotes[0:-1]])
output = open(seriesId + '.txt', 'w')
output.write(x.get_string())
output.close()
Ce script génère deux fichiers texte contenant les séries chronologiques des indices des prix à la consommation (CPI) et des salaires, prêts à être analysés.
L’appel à l’API du BLS suit un flux plus direct :
sequenceDiagram
participant Script
participant API as api.bls.gov
Script->>API: POST /publicAPI/v2/timeseries/data/
Note over Script,API: JSON: seriesid, startyear, endyear
API->>Script: Réponse JSON
Script->>Script: json.loads() → séries
Script->>Script: PrettyTable → format tabulaire
Script->>Script: Écriture dans .txt
🌘 Ce qu’il faut retenir
- RoboBrowser est idéal quand tu dois interagir avec des formulaires et naviguer entre les pages. Il combine la simplicité de requests avec la puissance de BeautifulSoup.
- L’API BLS montre qu’avant de scrapper, vérifie toujours si la source offre une API — c’est plus fiable et tu n’as pas à parser du HTML.
Les notebooks complets sont dans le dossier de l’atelier.
Tu as déjà fait du web scraping en Python ? Quelle bibliothèque utilises-tu ?