Cours
À l'ère où Internet regorge de données et où celles-ci sont souvent qualifiées de nouvel or, le web scraping s'impose comme une pratique essentielle et très concrète pour de nombreux cas d'usage. Le web scraping consiste à extraire des informations depuis un site web. On parle aussi d'extraction de données web ou de collecte web. Copier-coller du texte d'un site vers votre ordinateur est une forme de scraping, mais manuelle. En général, le web scraping désigne l'extraction automatisée de données à l'aide de robots d'indexation. Ces robots sont des scripts qui se connectent au Web via le protocole HTTP et permettent de récupérer des données de façon automatisée.
Que vous soyez data scientist, ingénieur ou analyste manipulant de grands volumes de données, savoir extraire des données du web est une compétence précieuse. Si vous trouvez des données en ligne sans option de téléchargement direct, le scraping avec Python vous permettra d'extraire ces données dans un format exploitable puis de les importer pour divers usages.
Quelques applications concrètes du web scraping :
- Collecter des CV de candidats possédant une compétence précise,
- Extraire des tweets contenant des hashtags spécifiques,
- Générer des leads en marketing,
- Récupérer des fiches produits et des avis sur des sites e-commerce comme Amazon, sujet de ce tutoriel
Au-delà de ces exemples, le web scraping est largement utilisé en traitement du langage naturel pour extraire des textes et entraîner des modèles d'apprentissage profond.
Avant de continuer, gardez à l'esprit que des sites comme Amazon peuvent mettre à jour leurs conditions d'utilisation ou ajouter des restrictions techniques affectant l'accès ou la réutilisation de leur contenu. Il vous revient de consulter les conditions d'utilisation du site et le fichier robots.txt afin de vérifier que votre cas d'usage est conforme. Les politiques peuvent évoluer et les permissions varier.
Défis potentiels du web scraping
-
L'un des défis majeurs lors de l'extraction d'informations est la diversité des structures de sites. Les gabarits diffèrent et restent souvent uniques : généraliser un scraper pour couvrir plusieurs sites peut s'avérer complexe.
-
Un autre défi concerne la pérennité. Les développeurs web mettent leurs sites à jour en continu : vous ne pouvez pas compter indéfiniment sur un seul scraper. Même des modifications mineures peuvent perturber la récupération des données.
Pour y faire face, plusieurs approches existent. L'une d'elles consiste à appliquer une démarche d'intégration et déploiement continus (CI/CD) et une maintenance régulière, car les sites évoluent dynamiquement.
Une approche plus robuste consiste à utiliser les interfaces de programmation (API) proposées par de nombreux sites et plateformes. Par exemple, Facebook et Twitter offrent des API destinées aux développeurs souhaitant expérimenter avec leurs données ou extraire, disons, des informations liées aux amis et amis communs pour tracer un graphe de relations. Les données issues d'API sont généralement au format JSON ou XML, quand le scraping classique manipule surtout du HTML.
Qu'est-ce que Beautiful Soup ?
Beautiful Soup est une bibliothèque Python pure dédiée à l'extraction de données structurées depuis un site web. Elle permet l'analyse (parsing) de fichiers HTML et XML. Elle sert de module d'assistance pour interagir avec le HTML de façon plus pratique et idiomatique que via d'autres outils développeur.
-
Elle fait souvent gagner des heures, voire des jours de travail, car elle s'appuie sur vos parseurs préférés comme
lxmlethtml5libpour offrir des façons « Python » de naviguer, rechercher et modifier l'arbre d'analyse. -
Autre atout : Beautiful Soup convertit intelligemment les documents entrants en Unicode et les documents sortants en UTF-8. En tant que développeur, vous n'avez pas à vous en soucier, sauf si le document ne précise aucune encodage intrinsèque ou si Beautiful Soup ne parvient pas à le détecter.
-
Elle est aussi réputée plus rapide que d'autres techniques générales de parsing ou de scraping.
Types de parseurs
N'hésitez pas à en lire davantage ici.
Assez de théorie, non ? Installons Beautiful Soup et découvrons ses fonctionnalités en Python.
Première étape : installez la bibliothèque Beautiful Soup depuis votre terminal ou Jupyter Lab. Le plus simple est de passer par pip : assurez-vous donc que le module pip est déjà installé.
!pip3 install beautifulsoup4
Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.7/site-packages (4.7.1)
Requirement already satisfied: soupsieve>=1.2 in /usr/local/lib/python3.7/site-packages (from beautifulsoup4) (1.9.5)
Importer les bibliothèques nécessaires
Importons les packages requis pour scraper les données du site et les visualiser avec seaborn, matplotlib et bokeh.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
import re
import time
from datetime import datetime
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
from urllib.request import urlopen
from bs4 import BeautifulSoup
import requests
Scraper les livres les plus vendus sur Amazon
Voici l'URL que vous allez scraper : https://www.amazon.in/gp/bestsellers/books/. Le paramètre de page peut être modifié pour accéder aux données de chaque page. Pour tout récupérer, il faudra donc itérer sur l'ensemble des pages, mais commencez par déterminer le nombre de pages sur le site.
Pour se connecter à l'URL et récupérer le contenu HTML, vous aurez besoin de :
- Définir une fonction
get_dataqui prend en argument le numéro de page, - Définir un
user-agentpour éviter d'être identifié comme scraper, - Passer l'URL à
requests.getavec l'en-tête user-agent, - Extraire le contenu renvoyé par requests.get,
- Parser la page ciblée et l'affecter à la variable
soup,
Étape suivante et essentielle : identifier la balise parente sous laquelle se trouvent toutes les informations utiles. Vous allez extraire :
- Le nom du livre
- L'auteur
- La note
- Le nombre de clients ayant noté
- Le prix
L'image ci-dessous montre où se situe la balise parente : au survol, tous les éléments requis sont mis en évidence.

Comme pour la balise parente, vous devez repérer les attributs correspondant au nom du livre, à l'auteur, à la note, au nombre de clients et au prix. Rendez-vous sur la page à scraper, sélectionnez l'élément, faites un clic droit puis « Inspecter ». Vous identifierez ainsi précisément les champs d'information à extraire depuis le HTML brut, comme illustré ci-dessous :

Notez que certains auteurs ne sont pas enregistrés auprès d'Amazon : il faut donc appliquer une recherche find supplémentaire pour ces cas. Dans la cellule de code ci-dessous, vous trouverez des conditions if-else imbriquées pour gérer les noms d'auteurs/maisons d'édition.
no_pages = 2
def get_data(pageNo):
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0", "Accept-Encoding":"gzip, deflate", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "DNT":"1","Connection":"close", "Upgrade-Insecure-Requests":"1"}
r = requests.get('https://www.amazon.in/gp/bestsellers/books/ref=zg_bs_pg_'+str(pageNo)+'?ie=UTF8&pg='+str(pageNo), headers=headers)#, proxies=proxies)
content = r.content
soup = BeautifulSoup(content)
#print(soup)
alls = []
for d in soup.findAll('div', attrs={'class':'a-section a-spacing-none aok-relative'}):
#print(d)
name = d.find('span', attrs={'class':'zg-text-center-align'})
n = name.find_all('img', alt=True)
#print(n[0]['alt'])
author = d.find('a', attrs={'class':'a-size-small a-link-child'})
rating = d.find('span', attrs={'class':'a-icon-alt'})
users_rated = d.find('a', attrs={'class':'a-size-small a-link-normal'})
price = d.find('span', attrs={'class':'p13n-sc-price'})
all1=[]
if name is not None:
#print(n[0]['alt'])
all1.append(n[0]['alt'])
else:
all1.append("unknown-product")
if author is not None:
#print(author.text)
all1.append(author.text)
elif author is None:
author = d.find('span', attrs={'class':'a-size-small a-color-base'})
if author is not None:
all1.append(author.text)
else:
all1.append('0')
if rating is not None:
#print(rating.text)
all1.append(rating.text)
else:
all1.append('-1')
if users_rated is not None:
#print(price.text)
all1.append(users_rated.text)
else:
all1.append('0')
if price is not None:
#print(price.text)
all1.append(price.text)
else:
all1.append('0')
alls.append(all1)
return alls
La cellule de code suivante effectue les opérations suivantes :
- Appeler la fonction
get_datadans une bouclefor, - Itérer de 1 jusqu'au nombre de pages + 1,
- Comme la sortie est une liste imbriquée, l'aplatir puis la passer à un DataFrame,
- Enfin, enregistrer le DataFrame en CSV.
results = []
for i in range(1, no_pages+1):
results.append(get_data(i))
flatten = lambda l: [item for sublist in l for item in sublist]
df = pd.DataFrame(flatten(results),columns=['Book Name','Author','Rating','Customers_Rated', 'Price'])
df.to_csv('amazon_products.csv', index=False, encoding='utf-8')
Lecture du fichier CSV
Chargeons maintenant le fichier CSV que vous venez de créer et d'enregistrer ci-dessus. Étape facultative : vous pouvez aussi utiliser directement le DataFrame df et ignorer ce qui suit.
df = pd.read_csv("amazon_products.csv")
df.shape
(100, 5)
La forme du DataFrame indique 100 lignes et 5 colonnes.
Affichons les 5 premières lignes du jeu de données.
df.head(61)
| Book Name | Author | Rating | Customers_Rated | Price | |
|---|---|---|---|---|---|
| 0 | The Power of your Subconscious Mind | Joseph Murphy | 4.5 out of 5 stars | 13,948 | ₹ 99.00 |
| 1 | Think and Grow Rich | Napoleon Hill | 4.5 out of 5 stars | 16,670 | ₹ 99.00 |
| 2 | Word Power Made Easy | Norman Lewis | 4.4 out of 5 stars | 10,708 | ₹ 130.00 |
| 3 | Mathematics for Class 12 (Set of 2 Vol.) Exami... | R.D. Sharma | 4.5 out of 5 stars | 18 | ₹ 930.00 |
| 4 | The Girl in Room 105 | Chetan Bhagat | 4.3 out of 5 stars | 5,162 | ₹ 149.00 |
| ... | ... | ... | ... | ... | ... |
| 56 | COMBO PACK OF Guide To JAIIB Legal Aspects Pri... | MEC MILLAN | 4.5 out of 5 stars | 114 | ₹ 1,400.00 |
| 57 | Wren & Martin High School English Grammar and ... | Rao N | 4.4 out of 5 stars | 1,613 | ₹ 400.00 |
| 58 | Objective General Knowledge | Sanjiv Kumar | 4.2 out of 5 stars | 742 | ₹ 254.00 |
| 59 | The Rudest Book Ever | Shwetabh Gangwar | 4.6 out of 5 stars | 1,177 | ₹ 194.00 |
| 60 | Sita: Warrior of Mithila (Ram Chandra Series -... | Amish Tripathi | 4.4 out of 5 stars | 3,110 | ₹ 248.00 |
61 rows × 5 columns
Procédons à un prétraitement des colonnes Ratings, Customers_Rated et Price.
- Les notes étant sur 5, conservez uniquement la valeur numérique et supprimez le reste.
- Dans la colonne customers_rated, supprimez les virgules.
- Dans la colonne price, retirez le symbole de roupie, les virgules, puis coupez à la décimale.
- Convertissez enfin ces trois colonnes en entier ou flottant.
df['Rating'] = df['Rating'].apply(lambda x: x.split()[0])
df['Rating'] = pd.to_numeric(df['Rating'])
df["Price"] = df["Price"].str.replace('₹', '')
df["Price"] = df["Price"].str.replace(',', '')
df['Price'] = df['Price'].apply(lambda x: x.split('.')[0])
df['Price'] = df['Price'].astype(int)
df["Customers_Rated"] = df["Customers_Rated"].str.replace(',', '')
df['Customers_Rated'] = pd.to_numeric(df['Customers_Rated'], errors='ignore')
df.head()
| Book Name | Author | Rating | Customers_Rated | Price | |
|---|---|---|---|---|---|
| 0 | The Power of your Subconscious Mind | Joseph Murphy | 4.5 | 13948 | 99 |
| 1 | Think and Grow Rich | Napoleon Hill | 4.5 | 16670 | 99 |
| 2 | Word Power Made Easy | Norman Lewis | 4.4 | 10708 | 130 |
| 3 | Mathematics for Class 12 (Set of 2 Vol.) Exami... | R.D. Sharma | 4.5 | 18 | 930 |
| 4 | The Girl in Room 105 | Chetan Bhagat | 4.3 | 5162 | 149 |
Vérifions les types des colonnes du DataFrame.
df.dtypes
Book Name object
Author object
Rating float64
Customers_Rated int64
Price int64
dtype: object
Remplaçons les zéros du DataFrame par des NaN.
df.replace(str(0), np.nan, inplace=True)
df.replace(0, np.nan, inplace=True)
Compter le nombre de NaN dans le DataFrame
count_nan = len(df) - df.count()
count_nan
Book Name 0
Author 6
Rating 0
Customers_Rated 0
Price 1
dtype: int64
On observe ci-dessus que six livres n'ont pas de nom d'auteur, et qu'un livre n'a pas de prix associé. Ces informations sont cruciales pour un auteur souhaitant vendre ses livres et ne devraient pas être omises.
Supprimons ces NaN.
df = df.dropna()
Livre le plus cher par auteur sur Amazon
Identifions les auteurs dont un livre affiche le prix le plus élevé. Nous visualiserons les 20 premiers.
data = df.sort_values(["Price"], axis=0, ascending=False)[:15]
data
| Book Name | Author | Rating | Customers_Rated | Price | |
|---|---|---|---|---|---|
| 56 | COMBO PACK OF Guide To JAIIB Legal Aspects Pri... | MEC MILLAN | 4.5 | 114 | 1400.0 |
| 98 | Diseases of Ear, Nose and Throat | P L Dhingra | 4.7 | 118 | 1285.0 |
| 3 | Mathematics for Class 12 (Set of 2 Vol.) Exami... | R.D. Sharma | 4.5 | 18 | 930.0 |
| 96 | Madhymik Bhautik Vigyan -12 (Part 1-2) (NCERT ... | Kumar-Mittal | 5.0 | 1 | 765.0 |
| 6 | My First Library: Boxset of 10 Board Books for... | Wonder House Books | 4.5 | 3116 | 750.0 |
| 38 | Indian Polity - For Civil Services and Other S... | M. Laxmikanth | 4.6 | 1210 | 700.0 |
| 42 | A Modern Approach to Verbal & Non-Verbal Reaso... | R.S. Aggarwal | 4.4 | 1822 | 675.0 |
| 27 | The Intelligent Investor (English) Paperback –... | Benjamin Graham | 4.4 | 6201 | 650.0 |
| 99 | Law of CONTRACT & Specific Relief | Dr. Avtar Singh | 4.4 | 23 | 643.0 |
| 49 | All In One ENGLISH CORE CBSE Class 12 2019-20 | Arihant Experts | 4.4 | 493 | 599.0 |
| 72 | The Secret | Rhonda Byrne | 4.5 | 11220 | 556.0 |
| 86 | How to Prepare for Quantitative Aptitude for t... | Arun Sharma | 4.4 | 847 | 537.0 |
| 8 | Quantitative Aptitude for Competitive Examinat... | R S Aggarwal | 4.4 | 4553 | 435.0 |
| 16 | Sapiens: A Brief History of Humankind | Yuval Noah Harari | 4.6 | 14985 | 434.0 |
| 84 | Concept of Physics Part-2 (2019-2020 Session) ... | H.C. Verma | 4.6 | 1807 | 433.0 |
from bokeh.models import ColumnDataSource
from bokeh.transform import dodge
import math
from bokeh.io import curdoc
curdoc().clear()
from bokeh.io import push_notebook, show, output_notebook
from bokeh.layouts import row
from bokeh.plotting import figure
from bokeh.transform import factor_cmap
from bokeh.models import Legend
output_notebook()
p = figure(x_range=data.iloc[:,1], plot_width=800, plot_height=550, title="Authors Highest Priced Book", toolbar_location=None, tools="")
p.vbar(x=data.iloc[:,1], top=data.iloc[:,4], width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0
p.xaxis.major_label_orientation = math.pi/2
show(p)

Sur le graphique ci-dessus, on observe que les deux livres les plus chers sont signés Mecmillan et P L Dhingra.
Livres les mieux notés par auteur
Identifions maintenant quels auteurs ont les livres les mieux notés, et quels titres ressortent. Pour cela, nous filtrerons les auteurs dont moins de 1 000 clients ont noté.
data = df[df['Customers_Rated'] > 1000]
data = data.sort_values(['Rating'],axis=0, ascending=False)[:15]
data
| Book Name | Author | Rating | Customers_Rated | Price | |
|---|---|---|---|---|---|
| 26 | Inner Engineering: A Yogi’s Guide to Joy | Sadhguru | 4.7 | 4091 | 254.0 |
| 70 | Bhagavad-Gita (Hindi) | A. C. Bhaktivedanta | 4.7 | 1023 | 150.0 |
| 11 | The Alchemist | Paulo Coelho | 4.7 | 22182 | 264.0 |
| 47 | Harry Potter and the Philosopher's Stone | J.K. Rowling | 4.7 | 7737 | 234.0 |
| 84 | Concept of Physics Part-2 (2019-2020 Session) ... | H.C. Verma | 4.6 | 1807 | 433.0 |
| 16 | Sapiens: A Brief History of Humankind | Yuval Noah Harari | 4.6 | 14985 | 434.0 |
| 38 | Indian Polity - For Civil Services and Other S... | M. Laxmikanth | 4.6 | 1210 | 700.0 |
| 29 | Wings of Fire: An Autobiography of Abdul Kalam | Arun Tiwari | 4.6 | 3513 | 301.0 |
| 39 | The Theory of Everything | Stephen Hawking | 4.6 | 2004 | 199.0 |
| 25 | The Immortals of Meluha (Shiva Trilogy) | Amish | 4.6 | 4538 | 248.0 |
| 23 | Life's Amazing Secrets: How to Find Balance an... | Gaur Gopal Das | 4.6 | 3422 | 213.0 |
| 34 | Dear Stranger, I Know How You Feel | Ashish Bagrecha | 4.6 | 1130 | 167.0 |
| 17 | The Monk Who Sold His Ferrari | Robin Sharma | 4.6 | 5877 | 137.0 |
| 13 | How to Win Friends and Influence People | Dale Carnegie | 4.6 | 15377 | 99.0 |
| 59 | The Rudest Book Ever | Shwetabh Gangwar | 4.6 | 1177 | 194.0 |
p = figure(x_range=data.iloc[:,0], plot_width=800, plot_height=600, title="Top Rated Books with more than 1000 Customers Rating", toolbar_location=None, tools="")
p.vbar(x=data.iloc[:,0], top=data.iloc[:,2], width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0
p.xaxis.major_label_orientation = math.pi/2
show(p)

D'après ce graphique, les trois livres les mieux notés avec plus de 1 000 avis clients sont Inner Engineering: A Yogi’s Guide to Joy, Bhagavad-Gita (Hindi) et The Alchemist.
p = figure(x_range=data.iloc[:,1], plot_width=800, plot_height=600, title="Top Rated Books with more than 1000 Customers Rating", toolbar_location=None, tools="")
p.vbar(x=data.iloc[:,1], top=data.iloc[:,2], width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0
p.xaxis.major_label_orientation = math.pi/2
show(p)

Ce graphique présente, par ordre décroissant, les 10 auteurs dont les livres sont les mieux notés avec plus de 1 000 avis clients : Sadhguru, A. C. Bhaktivedanta et Paulo Coelho arrivent en tête.
Auteurs et livres avec le plus d'avis clients
Après avoir vu les meilleures notes et les auteurs les mieux classés, il est utile de confirmer l'auteur et le livre « références » sur la base du volume d'avis clients.
Voyons cela rapidement.
data = df.sort_values(["Customers_Rated"], axis=0, ascending=False)[:20]
data
| Book Name | Author | Rating | Customers_Rated | Price | |
|---|---|---|---|---|---|
| 11 | The Alchemist | Paulo Coelho | 4.7 | 22182 | 264.0 |
| 1 | Think and Grow Rich | Napoleon Hill | 4.5 | 16670 | 99.0 |
| 13 | How to Win Friends and Influence People | Dale Carnegie | 4.6 | 15377 | 99.0 |
| 16 | Sapiens: A Brief History of Humankind | Yuval Noah Harari | 4.6 | 14985 | 434.0 |
| 18 | Rich Dad Poor Dad : What The Rich Teach Their ... | Robert T. Kiyosaki | 4.5 | 14591 | 296.0 |
| 10 | The Subtle Art of Not Giving a F*ck | Mark Manson | 4.4 | 14418 | 365.0 |
| 0 | The Power of your Subconscious Mind | Joseph Murphy | 4.5 | 13948 | 99.0 |
| 48 | The Power of Your Subconscious Mind | Joseph Murphy | 4.5 | 13948 | 99.0 |
| 72 | The Secret | Rhonda Byrne | 4.5 | 11220 | 556.0 |
| 41 | 1984 | George Orwell | 4.5 | 10829 | 95.0 |
| 2 | Word Power Made Easy | Norman Lewis | 4.4 | 10708 | 130.0 |
| 46 | Man's Search For Meaning: The classic tribute ... | Viktor E Frankl | 4.4 | 8544 | 245.0 |
| 67 | The 7 Habits of Highly Effective People | R. Stephen Covey | 4.3 | 8229 | 397.0 |
| 47 | Harry Potter and the Philosopher's Stone | J.K. Rowling | 4.7 | 7737 | 234.0 |
| 40 | One Indian Girl | Chetan Bhagat | 3.8 | 7128 | 113.0 |
| 65 | Thinking, Fast and Slow (Penguin Press Non-Fic... | Daniel Kahneman | 4.4 | 7087 | 410.0 |
| 27 | The Intelligent Investor (English) Paperback –... | Benjamin Graham | 4.4 | 6201 | 650.0 |
| 17 | The Monk Who Sold His Ferrari | Robin Sharma | 4.6 | 5877 | 137.0 |
| 53 | Ram - Scion of Ikshvaku (Ram Chandra) | Amish Tripathi | 4.2 | 5766 | 262.0 |
| 93 | The Richest Man in Babylon | George S. Clason | 4.5 | 5694 | 129.0 |
from bokeh.transform import factor_cmap
from bokeh.models import Legend
from bokeh.palettes import Dark2_5 as palette
import itertools
from bokeh.palettes import d3
#colors has a list of colors which can be used in plots
colors = itertools.cycle(palette)
palette = d3['Category20'][20]
index_cmap = factor_cmap('Author', palette=palette,
factors=data["Author"])
p = figure(plot_width=700, plot_height=700, title = "Top Authors: Rating vs. Customers Rated")
p.scatter('Rating','Customers_Rated',source=data,fill_alpha=0.6, fill_color=index_cmap,size=20,legend='Author')
p.xaxis.axis_label = 'RATING'
p.yaxis.axis_label = 'CUSTOMERS RATED'
p.legend.location = 'top_left'
BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead
show(p)

Ce nuage de points compare, pour chaque auteur, la note moyenne et le nombre d'avis clients. On peut en tirer les enseignements suivants :
- Sans conteste, The Alchemist de Paulo Coelho est un best-seller : excellente note et très grand nombre d'avis.
- Le livre Ram - Scion of Ikshvaku (Ram Chandra) d'Amish Tripathi est noté 4,2 avec 5 766 avis. The Richest Man in Babylon de George S. Clason affiche un volume d'avis proche, mais une note globale de 4,5 : davantage de clients lui ont attribué une note élevée.
Conclusion
Félicitations pour avoir mené ce tutoriel à bien.
Ce tutoriel vous a introduit au scraping d'Amazon avec Beautiful Soup et à la mise en perspective des informations extraites via la bibliothèque de visualisation bokeh. Pour aller plus loin dans l'apprentissage du web scraping avec Beautiful Soup, essayez d'autres sites et voyez quelles informations vous pouvez en tirer. Nous proposons aussi un tutoriel sur le scraping de Reddit si vous souhaitez un accompagnement supplémentaire sur des sites populaires.
Si vous débutez avec Python et souhaitez approfondir, suivez le cours Introduction to Data Science in Python de DataCamp.