Curso
En una época en la que internet rebosa datos y, aparentemente, los datos son el nuevo petróleo, el web scraping se ha vuelto aún más importante y práctico en multitud de aplicaciones. El web scraping consiste en extraer información de un sitio web. A veces también se denomina web harvesting o extracción de datos web. Copiar texto de una web y pegarlo en tu equipo local también es scraping, aunque manual. En general, el web scraping automatiza esa extracción con la ayuda de crawlers. Los web crawlers son scripts que se conectan a la web mediante el protocolo HTTP y permiten obtener datos de forma automatizada.
Tanto si eres científico de datos, ingeniero o simplemente analizas grandes volúmenes de información, saber extraer datos de la web es una habilidad muy útil. Imagina que encuentras datos en internet y no hay una forma directa de descargarlos: el web scraping con Python te permite extraerlos y dejarlos en un formato útil para importarlos y trabajarlos de distintas maneras.
Algunas aplicaciones prácticas del web scraping:
- Recopilar currículos de candidatos con una habilidad específica,
- Extraer tuits de Twitter con determinados hashtags,
- Generación de leads en marketing,
- Rastrear detalles de productos y reseñas en e‑commerce como Amazon, el foco de este tutorial
Además de estos casos, el web scraping se usa ampliamente en procesamiento del lenguaje natural para extraer texto de sitios web y entrenar modelos de deep learning.
Antes de continuar, recuerda que sitios como Amazon pueden actualizar sus términos de servicio o introducir nuevas restricciones técnicas que afecten a cómo se accede o reutiliza su contenido. Es tu responsabilidad revisar los Términos de servicio del sitio y el archivo robots.txt para asegurarte de que tu caso de uso cumple las normas. Las políticas pueden cambiar y los permisos varían.
Retos potenciales del web scraping
-
Uno de los retos al extraer información es la diversidad de estructuras entre sitios. Es decir, cada web usa plantillas diferentes y únicas; por tanto, generalizar entre sitios puede ser complicado.
-
Otro reto es la durabilidad. Como los desarrolladores actualizan sus sitios, no puedes fiarte del mismo scraper durante mucho tiempo. Aunque los cambios sean menores, pueden impedirte recuperar los datos.
Para abordar estos retos hay varias soluciones posibles. Una es aplicar integración y despliegue continuos (CI/CD) y un mantenimiento constante, ya que los cambios del sitio serán dinámicos.
Otra vía, más realista a menudo, es usar las APIs (Application Programming Interfaces) que ofrecen muchas webs y plataformas. Por ejemplo, Facebook y Twitter proporcionan APIs pensadas para desarrolladores que quieran experimentar con sus datos o extraer información, por ejemplo, sobre amigos y amigos en común para trazar un grafo de conexiones. El formato de datos al usar APIs difiere del scraping habitual (JSON o XML), mientras que en scraping estándar sueles tratar con HTML.
¿Qué es Beautiful Soup?
Beautiful Soup es una biblioteca de Python puro para extraer datos estructurados de una web. Te permite parsear datos de archivos HTML y XML. Actúa como un módulo de apoyo e interactúa con el HTML de forma similar (y a menudo mejor) a como lo harías con las herramientas de desarrollo del navegador.
-
Suele ahorrarte horas o días de trabajo porque funciona con tus parsers favoritos como
lxmlyhtml5lib, y ofrece formas "Pythonicas" de navegar, buscar y modificar el árbol de parseo. -
Otra funcionalidad muy útil es su capacidad para convertir los documentos entrantes a Unicode y los salientes a UTF‑8. Como desarrollador, no tienes que preocuparte por esto salvo que el documento no especifique codificación o Beautiful Soup no pueda detectarla.
-
También se considera más rápida que otras técnicas generales de parseo o scraping.
Tipos de parsers
Puedes leer más al respecto aquí.
Ya vale de teoría, ¿no? Vamos a instalar Beautiful Soup y a explorar sus funciones y capacidades con Python.
Como primer paso, instala la biblioteca Beautiful Soup desde tu terminal o Jupyter Lab. La mejor forma es mediante pip, así que asegúrate de tener el módulo pip instalado.
!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)
Importar las bibliotecas necesarias
Importa los paquetes que utilizarás para extraer datos del sitio web y visualizarlos con seaborn, matplotlib y 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
Scraping de los libros más vendidos en Amazon
La URL que vas a rastrear es la siguiente: https://www.amazon.in/gp/bestsellers/books/. El parámetro de página se puede modificar para acceder a cada página. Por tanto, para abarcar todas, tendrás que iterar sobre todas las páginas y construir el dataset. Antes, eso sí, necesitas averiguar cuántas páginas hay en el sitio.
Para conectar con la URL y obtener el HTML necesitas:
- Definir una función
get_dataque reciba como argumento el número de página, - Definir un
user-agentpara ayudar a evitar la detección como scraper, - Pasar la URL a
requests.gete incluir el user-agent en los headers, - Extraer el contenido de requests.get,
- Parsear la página indicada y asignarla a la variable
soup,
El siguiente paso, clave, es identificar la etiqueta padre bajo la que se encuentra toda la información que necesitas. Los datos que vas a extraer son:
- Nombre del libro
- Autor
- Valoración
- Clientes que han valorado
- Precio
En la imagen de abajo se ve dónde está la etiqueta padre y, al situarte encima, se resaltan todos los elementos necesarios.

De forma similar, necesitas encontrar los atributos para el nombre, autor, valoración, clientes y precio. Ve a la página que quieras scrapear, selecciona el atributo, haz clic derecho y elige "Inspeccionar". Así localizarás los campos de información específicos que debes extraer del HTML, como se muestra a continuación:

Ten en cuenta que algunos nombres de autor no están registrados en Amazon, así que tendrás que aplicar un find adicional para esos casos. En la celda de código siguiente verás condiciones if-else anidadas para extraer nombres de autor o editorial.
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 siguiente celda de código realizará lo siguiente:
- Llamar a la función
get_datadentro de un bucle for, - El bucle
foriterará desde 1 hasta número de páginas+1. - Como la salida será una lista anidada, primero la aplanarás y luego la pasarás al DataFrame.
- Por último, guardarás el DataFrame como archivo 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')
Lectura del archivo CSV
Ahora carga el CSV que creaste y guardaste en la celda anterior. Este paso es opcional; también puedes usar directamente el DataFrame df y saltarte este paso.
df = pd.read_csv("amazon_products.csv")
df.shape
(100, 5)
La forma del DataFrame indica que tu archivo CSV tiene 100 filas y 5 columnas.
Imprimamos las primeras 5 filas del dataset.
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 filas × 5 columnas
Hagamos algo de preprocesado en las columnas Rating, Customers_Rated y Price.
- Como sabes que las valoraciones son sobre 5, quédate solo con la cifra y elimina el resto.
- En la columna customers_rated, elimina las comas.
- En price, elimina el símbolo de rupia, las comas y separa por el punto.
- Convierte finalmente las tres columnas a entero o float.
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 |
Verifiquemos los tipos de datos del DataFrame.
df.dtypes
Book Name object
Author object
Rating float64
Customers_Rated int64
Price int64
dtype: object
Sustituye los ceros del DataFrame por NaN.
df.replace(str(0), np.nan, inplace=True)
df.replace(0, np.nan, inplace=True)
Contar el número de NaN en el DataFrame
count_nan = len(df) - df.count()
count_nan
Book Name 0
Author 6
Rating 0
Customers_Rated 0
Price 1
dtype: int64
Según el resultado, hay seis libros sin nombre de autor y uno sin precio asociado. Esta información es clave para quien quiera vender sus libros y no debería omitirse.
Eliminemos esos NaN.
df = df.dropna()
El libro más caro por autor en Amazon
Vamos a ver qué autores tienen el libro con precio más alto. Visualizarás los resultados para los 20 principales.
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)

En el gráfico se observa que los dos libros con precio más alto son de Mecmillan y P L Dhingra.
Libros mejor valorados por autor
Veamos qué autores tienen los libros mejor valorados y cuáles son esos títulos. Para ello, filtraremos aquellos con menos de 1000 valoraciones de clientes.
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)

Del gráfico se desprende que los tres libros mejor valorados con más de 1000 reseñas son Inner Engineering: A Yogi’s Guide to Joy, Bhagavad-Gita (Hindi) y 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)

Este gráfico muestra los 10 autores principales en orden descendente con libros mejor valorados y más de 1000 reseñas: Sadhguru, A. C. Bhaktivedanta y Paulo Coelho.
Autores y libros con más reseñas de clientes
Aunque ya has visto los libros y autores mejor valorados, resulta más convincente y fiable concluir el mejor autor y libro según el número de clientes que han valorado ese título.
Vamos a verlo rápidamente.
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)

El gráfico es un diagrama de dispersión de autores con número de valoraciones de clientes frente a valoración media. Podemos concluir lo siguiente:
- Sin discusión, The Alchemist de Paulo Coelho es el más vendido: su valoración y el volumen de reseñas van de la mano.
- Ram - Scion of Ikshvaku (Ram Chandra), de Amish Tripathi, tiene una valoración de 4,2 con 5766 reseñas. Sin embargo, The Richest Man in Babylon, de George S. Clason, tiene un número de reseñas similar pero una valoración de 4,5. Es decir, más clientes dieron una nota alta a The Richest Man in Babylon.
Conclusión
¡Enhorabuena por completar el tutorial!
Este tutorial ha sido una introducción básica a cómo hacer scraping en Amazon con Beautiful Soup y a cómo dar sentido a la información extraída visualizándola con la biblioteca de gráficos Bokeh. Un buen siguiente paso para seguir aprendiendo web scraping con Beautiful Soup es extraer datos de otros sitios y ver qué conclusiones puedes obtener. También tenemos un tutorial sobre scraping de Reddit si quieres más guía con sitios populares.
Si acabas de empezar con Python y quieres profundizar más, haz el curso de DataCamp Introduction to Data Science in Python.
