Técnicas de Web Scraping para Extracción de Datos con Python

Implementación de Web Scraping para Recolección de Datos

Este artículo explora técnicas prácticas de web scraping utilizando Python, enfocándose en la extracción automática de datos de sitios web. Se presentan tres ejercicios que abordan diferentes escenarios: la obtención de rankings universtiarios, la recopilación de precios de productos comerciales, y la descarga sistemática de recursos multimedia como imágenes.

Extracción de Información de Rankings Universitarios

En este ejercicio, se emplean las bibliotecas requests y BeautifulSoup para recuperar datos estructurados de un sitio web especializado en rankings educativos. El código descarga la página, analiza el contenido HTML y extrae campos clave como posición, nombre de la institución, ubicación geográfica, clasificación y puntuación.


import requests
from bs4 import BeautifulSoup
import re

# Dirección URL del recurso a analizar
portal = "http://www.shanghairanking.cn/rankings/bcur/2020"

# Encabezados HTTP para simular una solicitud de navegador
encabezados = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/85.0.4183.121 Safari/537.36"
}

# Obtener el contenido web con codificación adecuada
resultado = requests.get(portal, headers=encabezados)
resultado.encoding = "utf-8"

# Parsear el HTML y seleccionar elementos de tabla
analizador = BeautifulSoup(resultado.text, "html.parser")
registros = analizador.select("tbody tr")

# Mostrar encabezado de la tabla de resultados
print(f"{'Pos':<4}{'Institución':<20}{'Región':<12}{'Clase':<12}{'Puntos':<8}")

for registro in registros:
    celdas = registro.find_all("td")
    if len(celdas) >= 5:
        # Extraer datos de cada celda con limpieza de texto
        pos = celdas[0].get_text(strip=True)
        
        # Manejar posibles anidamientos en el nombre
        elemento_span = celdas[1].select_one("a span")
        if elemento_span:
            institucion = elemento_span.get_text(strip=True)
        else:
            texto_bruto = celdas[1].get_text(strip=True)
            institucion = re.sub(r"[^\u4e00-\u9fff]", "", texto_bruto)
        
        institucion = re.sub(r"\s*双一流\s*", "", institucion)
        region = celdas[2].get_text(strip=True)
        clase = celdas[3].get_text(strip=True)
        puntos = celdas[4].get_text(strip=True)
        
        print(f"{pos:<4}{institucion:<20}{region:<12}{clase:<12}{puntos:<8}")

Obtención de Precios en Plataformas de Comercio Electrónico

Este caso utiliza requests y expresiones regulares (re) para buscar y extraer información de productos específicos (mochilas) de un sitio de ventas en línea. El script localiza bloques de listados, parsea precios y nombres, y presenta los datos de forma ordenada.


import requests
import re
import random

# URL de búsqueda con término codificado
enlace_busqueda = "http://search.dangdang.com/?key=%E6%9C%89%E4%B9%A6%E5%8C%85&act=input"

try:
    # Realizar la petición y detectar codificación
    respuesta_html = requests.get(enlace_busqueda)
    respuesta_html.encoding = respuesta_html.apparent_encoding
except Exception as error:
    print(f"Fallo en la conexión: {error}")
    exit()

contenido = respuesta_html.text

# Identificar el bloque principal de productos
bloque_productos = re.search(r'<ul .="" class="bigimg cloth_shoplist">', contenido, re.S)
if not bloque_productos:
    print("Estructura de productos no encontrada.")
    exit()

fragmento_html = bloque_productos.group()

# Extraer cada artículo dentro de la lista
articulos = re.findall(r"<li.>", fragmento_html, re.S)
random.shuffle(articulos)

numero = 1
for articulo in articulos:
    # Capturar precio y nombre mediante patrones
    patron_precio = re.search(r'<span class="price_n">¥(.*?)</span>', articulo)
    patron_nombre = re.search(r'title="(.*?)"', articulo)
    
    if patron_precio and patron_nombre:
        precio_formateado = "€" + patron_precio.group(1)
        nombre_articulo = patron_nombre.group(1)
        print(f"{numero}. {nombre_articulo}  ——  {precio_formateado}")
        numero += 1
</li.></ul>

Descarga Automatizada de Imágenes desde Portales Web

Este ejercicio demuestra la recuperación masiva de archivos gráficos (formatos JPEG/JPG/PNG) a partir de múltiples páginas de un sitio de noticias. Se emplea urllib.request junto con re para localizar enlaces de imágenes, resolver rutas relativas y descargar los recursos en un dierctorio local.


import re
import urllib.request
import os

# Colección de URL a explorar
sitios = [
    "https://news.fzu.edu.cn/yxfd.htm",
    "https://news.fzu.edu.cn/yxfd/1.htm",
    "https://news.fzu.edu.cn/yxfd/2.htm",
    "https://news.fzu.edu.cn/yxfd/3.htm",
    "https://news.fzu.edu.cn/yxfd/4.htm",
    "https://news.fzu.edu.cn/yxfd/5.htm",
]

acumulador_enlaces = []

for sitio in sitios:
    print(f"Analizando: {sitio}")
    solicitud = urllib.request.Request(sitio, headers={"User-Agent": "Mozilla/5.0"})
    conexion = urllib.request.urlopen(solicitud)
    html = conexion.read().decode("utf-8", "ignore")

    # Buscar atributos src que apunten a archivos de imagen
    expresion = re.compile(r'src="([^"]+?\.jpg)"', re.IGNORECASE)
    hallazgos = expresion.findall(html)
    print(f"Encontradas {len(hallazgos)} referencias")

    # Obtener base del dominio para rutas relativas
    coincidencia_dominio = re.match(r"(https?://[^/]+)", sitio)
    dominio_base = coincidencia_dominio.group(1) if coincidencia_dominio else ""

    # Normalizar todos los enlaces a formato absoluto
    enlaces_completos = []
    for enlace in hallazgos:
        if enlace.startswith("http"):
            enlaces_completos.append(enlace)
        elif enlace.startswith("/"):
            enlaces_completos.append(dominio_base + enlace)
        else:
            # Construir ruta absoluta desde la página actual
            directorio = sitio.rsplit("/", 1)[0]
            enlaces_completos.append(f"{directorio}/{enlace}")

    acumulador_enlaces.extend(enlaces_completos)

# Eliminar entradas duplicadas
conjunto_unico = list(set(acumulador_enlaces))

print(f"Total de recursos únicos: {len(conjunto_unico)}. Procesando descargas...")
# Preparar carpeta de destino
os.makedirs("archivos_graficos", exist_ok=True)

for indice, recurso in enumerate(conjunto_unico, start=1):
    try:
        nombre_local = f"archivos_graficos/imagen_{indice}.jpg"
        urllib.request.urlretrieve(recurso, nombre_local)
        print(f"Guardado: {nombre_local}")
    except Exception as fallo:
        print(f"Error en {recurso}: {fallo}")

Etiquetas: Python web-scraping BeautifulSoup requests re

Publicado el 7-18 20:54