Web Scraping de Tablas HTML desde Wikipedia (o Cualquier Página Web)
- Fase 1: Conección y obtención de codigo fuente
- Fase 2: Análisis de estructura HTML y extracción de datos
- Fase 3: Guardado del Conjunto de Datos
- Resumiendo lo realizado
¿Alguna vez te topaste con alguna tabla de Wikipedia que justo necesitabas para tu investigación o proyecto de análisis? Como la tabla de medallas de los Juegos Panamericanos, Las Elecciones Presidenciales o datos de las Exportaciones de un Pais, ¿Si? Bueno, Aquí aprenderás como obtener datos de cualquier tabla.
Esta es la tabla que buscamos obtener que contiene información del Censo de la Provincia de Trujillo del año 2017). Para hacer Web Scraping a esta página, utilizaremos las bibliotecas Requests y Pandas de Python.
Al finalizar este Tutorial, tendrás como resultado una tabla lista para su análisis. Lo podrás guardar en tu directorio de trabajo o descargar en tu computadora como archivo CSV (o el formato que gustes). Espero que te resulte útil; sin más que añadir... "Happy Coding!"
Comenzaremos importando la biblioteca requests
para realizar la petición HTTP que nos devolverá el código fuente de la página web y pandas
, para usar su método .read_html()
que nos ayudará a extraer todas las tablas HTML. Usaremos este método y no la biblioteca Beautiful Soup
porque de esta forma es mucho más fácil y funciona bien en cualquier página web que contenga tablas HTML debido a su estructura simple de leer.
Para tu conocimiento:Usando
.read_html()
no solo nos permite extraer tablas HTML desde un enlace (lo que usaremos hoy), sino también desde un archivo html o una cadena de caracteres (string) que contenga HTML.
import requests
# Manipular código y guardar datos tabulares en archivo CSV
import pandas as pd
# url de la página web a «escrapear»
url = 'https://es.wikipedia.org/wiki/Provincia_de_Trujillo_(Per%C3%BA)'
# pasar "User-agent" para simular interacción con la página usando Navegador web
headers = {"User-agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'}
respuesta = requests.get(url, headers=headers)
# El código de respuesta <200> indicará que todo salió bien
print(respuesta)
Aparte de la respuesta positiva que nos devolvió la petición, seguro te diste cuenta que pasamos el parámetro headers
al método requests.get()
, esto sirve para evitar que el servidor de la página web nos bloqueen el acceso. Aunque con Wikipedia no tenemos ese problema, otras páginas son restrictivas cuando se trata de bots tratando de «escrapear» sus datos. Siguiento esta buena práctica, nos ahorarremos dolores de cabeza luego.
Por último, guardamos en una variable todas las tablas encontradas en el objecto HTML.
all_tables = pd.read_html(respuesta.content, encoding = 'utf8')
Ahora veamos cuantas tablas hay en la página web
print(f'Total de tablas encontradas: {len(all_tables)}')
Debido a que hay varias tablas, usaremos el parámetro match
y le pasaremos una palabra o frase que solo se encuentre en la tabla que nos interesa.
matched_table = pd.read_html(respuesta.text, match='Fuente: Censos Nacionales 2017')
# imprime numero de tablas que coinciden con parametro match
print(f'Total de tablas encontradas: {len(matched_table)}')
Guardamos nuestra tabla de interés en una variable.
censo_trujillo = matched_table[0]
# Verificamos si es la tabla que buscamos
censo_trujillo.tail(5)
¡Bien! Es la tabla que buscamos exportar, pero vemos que las 2 últimas filas no son necesarias, por lo que pasamos a eliminarlas.
censo_trujillo.drop(censo_trujillo.tail(2).index, inplace=True)
# Verificar si se eliminaron los registros no deseados
censo_trujillo.tail(2)
Ahora asignaremos el UBIGEO como índice de la tabla.
censo_trujillo.set_index('UBIGEO', inplace = True)
# Verificamos el cambio de índice
censo_trujillo.head(2)
También, vemos que en las columnas Hogares,Viviendas y Población que contienen números, hay un espacio en medio de los números que le da Wikipedia como formato para mejorar su visualización. Sin embargo, necesitamo que nuestros datos numéricos estén limpios sin ningún simbolo o espacios para poder realizar operaciones.
Removamos ese espacio que no aporta nuestros datos. Para ello usaremos la biblioteca normalize
.
from unicodedata import normalize
Creamos una funcion y lo correremos a toda nuestra tabla para quitar los molestos espacios en blanco.
def remove_whitespace(x):
"""Funcion para normalizar datos con Unicode para luego quitar los espacios usando .replace().
Argumentos de entrada: Nombre de columna o lista con nombres de columnas.
Retorna: columna o columnas sin espacios en blanco
"""
if isinstance(x, str):
return normalize('NFKC', x).replace(' ', '')
else:
return x
Aplicamos la funcion para quitar espacios en blanco a toda las columnas con datos numéricos.
numeric_cols = ['Hogares','Viviendas','Población']
# Aplicar función remove_whitespace a columnas en variable y las reemplazamos en tabla
censo_trujillo[numeric_cols] = censo_trujillo[numeric_cols].applymap(remove_whitespace)
# Verificamos si se quitaron los espacios en blanco
censo_trujillo.head()
Ahora veamos los tipos de datos.
censo_trujillo.dtypes
Como vemos, todos las columnas son de tipo de dato Objeto (lo que Pandas considera como una cadena de caracteres o String). Como Object es muy amplio, necesitamos definir el tipo de dato correcto a cada columna para que luego se realizar operaciones con los datos.
Aquí va la primera opción para hacerlo, reusando la variable numeric_cols
usada líneas arriba.
censo_trujillo[numeric_cols] = censo_trujillo[numeric_cols].apply(pd.to_numeric)
# Verificamos que las columnas con números tengan el tipo de dato numérico asignado
censo_trujillo.dtypes
Como ves, nuestras columnas con números ya tienen el formato correcto, pero la columna Distrito aún se mantiene como Object
. Aunque no habría mayor inconveniente, es mejor especificar que datos contiene cada columna.
Aquí va la seguida opción para cambiar el tipo de dato a múltiples columnas.
convert_dict = {
'Distrito': 'string',
'Hogares': 'int',
'Viviendas': 'int',
'Población': 'int'
}
censo_trujillo = censo_trujillo.astype(convert_dict)
# Verificamos que las columnas con números tengan el tipo de dato numérico asignado
censo_trujillo.dtypes
Por fin, una ves los datos están limpios y con el tipo de dato correcto, vamos a guardarlos en formato CSV para usarlos luego.
censo_trujillo.to_csv('censo_provincia_trujillo_2017.csv')
# Leamos el archivo para verificar su creacion
pd.read_csv('censo_provincia_trujillo_2017.csv').head(3)
Si lo trabajaste en Jupyter Notebook o tu Editor de Código Favorito, debe estar ubicado en la misma carpeta donde corriste tu archivo .pynb
. Pero, si lo trabajaste en Google Colab (como yo), puedes usar la biblioteca files
para descargar el archivo en la computadora donde estés trabajando.
from google.colab import files
# Descarga archivo con datos de tabla
files.download("censo_provincia_trujillo_2017.csv")
print('Listo, en un momento saldrá la opción "Guardar Como" para descargar el archivo...')
¡Genial! Ya tenemos los datos de una tabla de Wikipedia lista para su uso. Aunque la tabla extraida es pequeña en dimensión, esta forma de trabajo puedes aplicarla para extraer cualquier tabla que encuentres interesante, ya sea de Wikipedia o de cualquier otra página web que contenga tablas HTML.
- Leímos las tablas HTML de una página de Wikipedia
- Removimos los espacios en formato Unicode que impedían la conversión al tipo de dato correcto
- Convertimos el tipo de dato de todas las columnas al correcto
- Guardamos la tabla extraída como formato csv para su posterior utilización
- Finalmente, descargamos el archivo csv en la computadora de trabajo
¡Espera! Una cosa más, como estámos en el mes de diciembre, te regalo este pensamiento:
Si lo lees y lo entiendes está genial, aunque eso no te asegura haberlo aprendido. Para dominarlo necesitas practicarlo.
Por eso, te sugiero que guardes este artículo en tus Favoritos para que lo practiques luego. O si quieres practicarlo ya mismo, aquí te dejo el artículo en Google Colab, donde podrás abrir y correr el código sin necesidad de instalar nada, solo tu navegador web y tus ganas de aprender.
Si te gustó, puedes ver mis otras publicaciones, seguro te serán de utilidad. Si gustas apoyarme, comparte este artículo en tus Redes Sociales (Facebook, Linkedint, Twitter) o si estás de buen ánimo, invítame una taza de café ☕. Nos vemos 🏃💨