Python
Tutorial Python: Web Scraping para ciencia de datos
Aprende Beautiful Soup para extraer y analizar datos web. Domina el web scraping y transforma datos en datasets listos para ciencia de datos.
Aprende Python y certifícateIntroducción a la Integración de Beautiful Soup y Pandas
La integración de Beautiful Soup y Pandas es esencial para transformar datos extraídos de páginas web en estructuras organizadas y analizables. Mientras que Beautiful Soup es una biblioteca poderosa para parsear y navegar por documentos HTML, Pandas ofrece herramientas robustas para manipular y analizar datos en formato tabular.
Al extraer información de una página web usando Beautiful Soup, los datos suelen estar en formato no estructurado, lo que dificulta su análisis directo. Aquí es donde entra en juego Pandas, permitiéndonos convertir esos datos en DataFrames, estructuras que facilitan el manejo y análisis de la información.
Por ejemplo, consideremos que queremos extraer una tabla:
from bs4 import BeautifulSoup
import pandas as pd
# Tabla HTML de ejemplo
html_doc = """
<table>
<thead>
<tr>
<th scope="col">Breed</th>
<th scope="col">Average Lifespan</th>
<th scope="col">Characteristics</th>
</tr>
</thead>
<tbody>
<tr>
<td>Siberian</td>
<td>12-15 years</td>
<td>Playful and Affectionate</td>
</tr>
<tr>
<td>Bengal</td>
<td>10-16 years</td>
<td>Energetic and Vocal</td>
</tr>
<tr>
<td>Ragdoll</td>
<td>12-17 years</td>
<td>Calm and Friendly</td>
</tr>
</tbody>
</table>
"""
# Analizar el contenido HTML
soup = BeautifulSoup(html_doc, 'html.parser')
# Encontrar la tabla específica
tabla = soup.find('table')
# Extraer los encabezados
encabezados = [th.text.strip() for th in tabla.find_all('th')]
# Extraer las filas de la tabla
filas = []
for fila in tabla.find_all('tr')[1:]:
celdas = [celda.text.strip() for celda in fila.find_all('td')]
filas.append(celdas)
# Crear un DataFrame de Pandas
df = pd.DataFrame(filas, columns=encabezados)
# Mostrar el DataFrame
print(df)
En este ejemplo, usamos Beautiful Soup para extraer los datos de la tabla HTML y luego Pandas para organizar esos datos en un DataFrame. Este enfoque nos permite aprovechar las funciones de análisis y manipulación de datos que ofrece Pandas.
Es crucial manejar correctamente los datos durante este proceso. A menudo, los datos extraídos pueden contener caracteres especiales o espacios en blanco innecesarios. Usar métodos como str.strip()
ayuda a limpiar los datos antes de convertirlos en un DataFrame.
Además, al trabajar con múltiples tablas o fuentes de datos, Pandas facilita la concatenación y fusión de DataFrames, permitiéndonos combinar datos de diferentes orígenes de manera efectiva.
# Supongamos que tenemos otro DataFrame
df_otro = pd.DataFrame({'Columna1': [1, 2], 'Columna2': [3, 4]})
# Podemos concatenar los DataFrames
df_concatenado = pd.concat([df, df_otro], ignore_index=True)
La sinergia entre Beautiful Soup y Pandas es especialmente útil en proyectos de ciencia de datos, donde la obtención y el análisis de información precisa es fundamental. Al integrar estas herramientas, optimizamos el flujo de trabajo desde la extracción de datos hasta su análisis y visualización.
Extracción de datos y construcción de DataFrames
Al realizar web scraping con Beautiful Soup, es fundamental extraer los datos correctamente para luego estructurarlos en DataFrames de Pandas. Esto permite analizar y manipular la información de forma más efectiva.
Para comenzar, supongamos que deseamos extraer una lista de productos y sus precios de una página web. Primero, debemos enviar una solicitud a la página y crear un objeto BeautifulSoup:
import requests
from bs4 import BeautifulSoup
# Enviar solicitud HTTP a la página web
url = 'https://www.ejemplo.com/productos'
respuesta = requests.get(url)
soup = BeautifulSoup(respuesta.content, 'html.parser')
Una vez obtenido el objeto soup
, procedemos a localizar los elementos que contienen la información relevante. Si los productos están dentro de etiquetas <div>
con la clase producto
, podemos utilizar:
# Encontrar todos los contenedores de productos
productos = soup.find_all('div', class_='producto')
Ahora, iteramos sobre la lista productos
para extraer el nombre y el precio de cada uno. Podemos almacenar esta información en listas:
# Listas para almacenar los datos
nombres = []
precios = []
for producto in productos:
# Extraer el nombre del producto
nombre = producto.find('h2', class_='nombre').text.strip()
nombres.append(nombre)
# Extraer el precio del producto
precio = producto.find('span', class_='precio').text.strip()
precios.append(precio)
Es importante limpiar los datos extraídos usando strip()
para eliminar espacios en blanco innecesarios. Además, podemos convertir los precios a valores numéricos:
# Convertir precios a números decimales
precios = [float(precio.replace('€', '').replace(',', '.')) for precio in precios]
Con los datos preparados, procedemos a construir un DataFrame de Pandas:
import pandas as pd
# Crear un diccionario con los datos
datos = {'Nombre': nombres, 'Precio': precios}
# Crear el DataFrame
df = pd.DataFrame(datos)
# Mostrar el DataFrame
print(df)
De esta manera, hemos transformado los datos extraídos en una estructura tabular lista para su análisis.
Es frecuente que necesitemos extraer datos de múltiples páginas (paginación). Para ello, podemos implementar un bucle que recorra las páginas y acumule los datos:
nombres = []
precios = []
# Suponiendo que hay 5 páginas de productos
for pagina in range(1, 6):
# Actualizar la URL con el número de página
url_pagina = f'https://www.ejemplo.com/productos?page={pagina}'
respuesta = requests.get(url_pagina)
soup = BeautifulSoup(respuesta.content, 'html.parser')
productos = soup.find_all('div', class_='producto')
for producto in productos:
nombre = producto.find('h2', class_='nombre').text.strip()
nombres.append(nombre)
precio = producto.find('span', class_='precio').text.strip()
precios.append(float(precio.replace('€', '').replace(',', '.')))
Después de extraer todos los datos, construimos el DataFrame como antes.
Es esencial manejar errores y casos excepcionales durante la extracción. Por ejemplo, si algún producto no tiene precio:
try:
precio = producto.find('span', class_='precio').text.strip()
precios.append(float(precio.replace('€', '').replace(',', '.')))
except AttributeError:
# Si no se encuentra el precio, asignar un valor nulo
precios.append(None)
Al combinar Beautiful Soup para la extracción y Pandas para la estructuración, podemos enfrentar tareas más complejas de web scraping y análisis de datos.
Limpieza y preprocesamiento de datos scrapeados
Al extraer información con Beautiful Soup, es común enfrentarse a datos que requieren limpieza y preprocesamiento antes de ser utilizados efectivamente. Los datos scrapeados pueden contener espacios en blanco innecesarios, caracteres especiales o formatos inconsistentes que deben ser tratados.
Uno de los primeros pasos es eliminar espacios en blanco y saltos de línea que pueden interferir con el análisis. Utilizar el método strip()
es esencial para remover estos caracteres del inicio y fin de las cadenas.
texto_crudo = elemento.get_text()
texto_limpio = texto_crudo.strip()
Para eliminar caracteres especiales o no deseados, las expresiones regulares proporcionan una herramienta potente. El módulo re
de Python permite redefinir el texto según patrones específicos.
import re
texto_limpio = re.sub(r'[^a-zA-Z0-9\s]', '', texto_limpio)
La normalización del texto es crucial, especialmente cuando se trabaja con múltiples fuentes que pueden tener diferentes codificaciones. Convertir el texto a minúsculas y eliminar acentos garantiza una comparación uniforme.
texto_normalizado = texto_limpio.lower()
import unicodedata
def eliminar_acentos(cadena):
return ''.join(caracter for caracter in unicodedata.normalize('NFD', cadena)
if unicodedata.category(caracter) != 'Mn')
texto_sin_acentos = eliminar_acentos(texto_normalizado)
Al manejar números, es importante convertir cadenas numéricas que puedan incluir símbolos o comas decimales al formato adecuado. Esto facilita cálculos y análisis posteriores.
precio_crudo = elemento_precio.get_text().strip()
precio_formateado = precio_crudo.replace('€', '').replace('.', '').replace(',', '.')
precio_numerico = float(precio_formateado)
En el caso de fechas y horas, utilizar la librería datetime
ayuda a parsear y estandarizar diferentes formatos.
from datetime import datetime
fecha_cruda = elemento_fecha.get_text().strip()
fecha_formato = datetime.strptime(fecha_cruda, '%d/%m/%Y')
Cuando se extraen listas o tablas, es eficaz emplear comprensiones de listas para limpiar cada elemento de manera concisa.
filas = tabla.find_all('tr')
datos = [[celda.get_text().strip() for celda in fila.find_all('td')] for fila in filas]
Es crucial manejar los valores nulos o faltantes para evitar errores en el análisis. Se pueden asignar valores predeterminados o utilizar condiciones para verificar la existencia de datos.
titulo = elemento.find('h1').get_text().strip() if elemento.find('h1') else 'Sin título'
Para asegurar la consistencia de codificación, especialmente al trabajar con diferentes idiomas o caracteres especiales, es recomendable especificar la codificación apropiada al leer el contenido.
contenido = requests.get(url).content
soup = BeautifulSoup(contenido, 'html.parser', from_encoding='utf-8')
Al preparar los datos para un DataFrame de Pandas, es útil convertir listas de elementos limpios en un formato estructurado.
import pandas as pd
nombres = [item.get_text().strip() for item in soup.find_all('span', class_='nombre')]
precios = [float(item.get_text().strip().replace('€', '').replace(',', '.')) for item in soup.find_all('span', class_='precio')]
df = pd.DataFrame({'Nombre': nombres, 'Precio': precios})
La aplicación cuidadosa de estas técnicas de limpieza y preprocesamiento asegura que los datos extraídos sean de alta calidad y estén listos para su análisis en proyectos de ciencia de datos.
Enriquecimiento de datasets con datos de múltiples fuentes web
En el ámbito de la ciencia de datos, es común enfrentarse al desafío de enriquecer datasets existentes con información adicional proveniente de diversas fuentes web. Beautiful Soup, en combinación con Pandas, permite extraer y fusionar datos de múltiples sitios web para obtener un conjunto de datos más completo y valioso.
Al trabajar con datos de diferentes fuentes, es fundamental considerar la consistencia y compatibilidad de la información. Cada sitio web puede presentar sus datos en formatos y estructuras distintas, por lo que es necesario implementar estrategias para alinear y unificar estos datos antes de integrarlos en el dataset principal.
Por ejemplo, supongamos que disponemos de un dataset con una lista de productos y queremos enriquecerlo con reseñas y calificaciones obtenidas de diferentes tiendas en línea. Primero, utilizamos Beautiful Soup para extraer la información relevante de cada sitio web:
import requests
from bs4 import BeautifulSoup
def obtener_reseñas(url):
respuesta = requests.get(url)
soup = BeautifulSoup(respuesta.content, 'html.parser')
reseñas = []
elementos_reseña = soup.find_all('div', class_='reseña')
for elemento in elementos_reseña:
usuario = elemento.find('span', class_='usuario').get_text(strip=True)
calificación = float(elemento.find('span', class_='calificación').get_text(strip=True))
comentario = elemento.find('p', class_='comentario').get_text(strip=True)
reseñas.append({'usuario': usuario, 'calificación': calificación, 'comentario': comentario})
return reseñas
En este fragmento de código, extraemos las reseñas de un producto específico. Usamos el método get_text(strip=True)
para obtener el texto sin espacios innecesarios.
Una vez extraídos los datos de diferentes fuentes, necesitamos integrarlos en nuestro dataset. Esto implica mapear la información de cada fuente al esquema común del dataset. Pandas ofrece funciones como merge
y concat
para combinar DataFrames de manera eficiente.
import pandas as pd
# Dataset principal con información de productos
df_productos = pd.read_csv('productos.csv')
# DataFrame con reseñas de la fuente A
reseñas_a = obtener_reseñas('https://tiendaA.com/producto123/reseñas')
df_reseñas_a = pd.DataFrame(reseñas_a)
df_reseñas_a['fuente'] = 'Tienda A'
# DataFrame con reseñas de la fuente B
reseñas_b = obtener_reseñas('https://tiendaB.com/producto123/reseñas')
df_reseñas_b = pd.DataFrame(reseñas_b)
df_reseñas_b['fuente'] = 'Tienda B'
# Unir las reseñas de ambas fuentes
df_reseñas = pd.concat([df_reseñas_a, df_reseñas_b], ignore_index=True)
# Agregar las reseñas al dataset principal
df_enriquecido = df_productos.merge(df_reseñas, on='producto_id', how='left')
Aquí, hemos utilizado pd.concat
para combinar las reseñas de diferentes fuentes en un solo DataFrame. La columna adicional 'fuente'
nos permite identificar el origen de cada reseña.
Es importante manejar las diferencias en los formatos de datos entre las fuentes. Por ejemplo, si las calificaciones están en escalas distintas (por ejemplo, de 1 a 5 en una fuente y de 1 a 10 en otra), debemos normalizar los valores antes de fusionarlos.
# Normalizar calificaciones de la fuente B (escala de 1 a 10 a escala de 1 a 5)
df_reseñas_b['calificación'] = df_reseñas_b['calificación'] / 2
Además, es recomendable implementar mecanismos para controlar la calidad de los datos. Esto incluye detectar y eliminar duplicados, manejar valores nulos y validar la coherencia de la información.
Para enriquecer aún más nuestro dataset, podemos extraer datos de APIs públicas o fuentes abiertas. Por ejemplo, obtener información meteorológica para analizar cómo las condiciones climáticas afectan las ventas de ciertos productos.
# Obtener datos meteorológicos de una API
def obtener_clima(fecha, ubicación):
url_api = f'https://api.clima.com/datos?fecha={fecha}&ubicación={ubicación}'
respuesta = requests.get(url_api)
datos_clima = respuesta.json()
return {'fecha': fecha, 'ubicación': ubicación, 'temperatura': datos_clima['temperatura']}
# Aplicar la función para enriquecer el dataset
df_productos['fecha'] = pd.to_datetime(df_productos['fecha'])
df_productos['ubicación'] = 'Madrid' # Suponiendo que la ubicación es Madrid para todos los registros
datos_clima = df_productos[['fecha', 'ubicación']].drop_duplicates().apply(
lambda fila: obtener_clima(fila['fecha'], fila['ubicación']), axis=1)
df_clima = pd.DataFrame(datos_clima.tolist())
# Fusionar los datos meteorológicos con el dataset principal
df_enriquecido = df_productos.merge(df_clima, on=['fecha', 'ubicación'], how='left')
La sincronización de datos es otro aspecto clave. Si los datos de las fuentes externas se actualizan con frecuencia, es posible que necesitemos diseñar un sistema de actualización periódica para mantener nuestro dataset al día.
Por último, al manejar múltiples fuentes, es útil emplear una arquitectura modular en nuestro código. Esto facilita el mantenimiento y la escalabilidad del proyecto, permitiendo agregar o modificar fuentes sin afectar significativamente el resto del sistema.
Como hemos visto, el enriquecimiento de datasets con datos de múltiples fuentes web requiere una combinación de habilidades en extracción de datos, procesamiento y manejo de datos, así como una atención cuidadosa a los detalles para garantizar la calidad y coherencia de los datos integrados.
Otras lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
Ejercicios de programación de Python
Evalúa tus conocimientos de esta lección Web Scraping para ciencia de datos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
En esta lección
Objetivos de aprendizaje de esta lección
- Entender cómo integrar Beautiful Soup con Pandas para manipulación de datos.
- Aprender a extraer datos no estructurados de páginas web.
- Dominar la limpieza y preprocesamiento de datos scrapeados.
- Enriquecer datasets con información de múltiples fuentes web.
- Manejar errores comunes en el web scraping y realizar un análisis eficaz.