Extraer datos estructurados de una página web con Python y BeautifullSoup

En este post voy a tratar el uso combinado de Python y la librería de BeautifullSoup como herramientas para extraer y almacenar un gran volumen de datos estructurados en HTML accesibles desde una página web de acceso público.

Pasos previos de configuración

Es necesario, antes de ponernos a trastear con Python, que contemos con:

  • Tener instalado Python en nuestra máquina
  • Tener instalado la herramienta “python-pip”
    • Si no lo tenemos:
      • sudo apt-get install python-pip
  • Instalar con pip las librerías bs4 y beautifullsoup
    • sudo pip install bs4
    • sudo pip install beautifullsoup

Introducción

En este caso de ejemplo, se usará esta técnica de extraer datos estructurados para recopilar el catálogo de productos de una conocida cadena de supermercados española (Día), ya que los datos que nos proporcionan no son datos de carácter personal y son de acceso público y por que la web es fácilmente entendible para este tutorial.

Análisis del sitio web

El objetivo es crear fichero .csv de todos sus productos con la estructura “Nombre producto, Precio”. Visualmente la página de productos se ve así:

BeautifullSoup y Python extraer datos estructurados

Ejemplo de presentación del catalogo del Día

A nivel interno el nombre y el precio del producto  se organizan cumpliendo que  tanto el nombre como el precio van seguidos unos de otros dentro del código HTML:

extraer datos estructurados

Estructura interna HTML

  • La etiqueta correspondiente al valor del nombre del producto tiene el atributo class=”details”
  • La etiqueta correspondiente al valor del precio del producto tiene el atributo class=”price”

Por lo que debemos extraer todas las etiquetas asociadas a estos atributos.

Pero hay que tener en cuenta que la página actual solo lista un número pequeño de los productos que dispone el portal para acceso público.

paginas extraer datos estructurados

Como se puede ver en la imagen tenemos unos botones que nos permiten acceder a las páginas siguientes donde poder listar más productos. Estos botones si los pulsamos, nos dan una pista de como funciona la cadena de consulta, donde podemos ver como generar esta URL y recorrer mediante un bucle todas las páginas posibles.

extraer datos estructurados

Ejemplo de dirección Página 1

extraer datos estructurados

Ejemplo de dirección Página 2

Como se ve en las imágenes, se observa que “page” toma el valor de la página actual del botón que hemos presionado, por lo que podemos generar un string con la URL a analizar para extraer los datos estructurados.

Programando nuestro script de extracción de datos estructurados

Abrimos un nuevo documento .py donde escribiremos con nuestro IDE de Python preferido lo siguiente:

# -*- coding: utf-8 -*-
import urllib2,unicodedata
from bs4 import BeautifulSoup

#método de análisis de una dirección web
def analisisDescarga(archivo,conexion):
    html = conexion.read()
    soup = BeautifulSoup(html)
    #obtenemos una lista de String con la condición de atributos class con valores details y price
    links = soup.find_all(True, {'class':['details','price']})
    #la lista alterna valores de nombre de producto y precio
    #   creamos una bandera para diferenciar si es valor o producto
    precio = False
    for tag in links:
        print("--")
        for linea in tag:
            linea = linea.strip();
            #adaptamos unicode a utf-8
            normalizado=unicodedata.normalize('NFKD', linea).encode('ascii','ignore')

            if len(normalizado)>1:
                if precio:
                    print('precio: '+normalizado)
                    precio= not precio
                    archivo.write(normalizado+'\n')
                else:
                    print('producto: '+normalizado)
                    precio = not precio
                    archivo.write(normalizado+'\t')
#este método se conectará con la web y establece un timeout que obliga a reintentar el fallo
def preparar(archivo,web,x):
    try:
        print(web)
        conector = urllib2.urlopen(web,timeout=10)#timeout de 10 segundos
        analisisDescarga(archivo,conector)
    except:
        print("Tiempo de espera agotado, volviendo a intentar")
        preparar(archivo,web,x)

#Programa principal
print('Comienza el programa')
archivo=open('productoPrecio.csv','w')
#El CSV separa las columnas por medio de tabuladores
for x in range(0,177):
    #Ruta de la página web
    url = 'http://www.dia.es/compra-online/productos/c/WEB.000.000.00000?q=%3Aname-asc&page='+str(x)+'&disp=grid'
    preparar(archivo,url,x)

archivo.close()
print('Fin del programa')

Resultado

Tras la ejecución del script se nos genera un archivo llamado productoPrecio.csv junto al archivo .py que define nuestro programa. Si lo abrimos con LibreOffice o Excel podemos ver y tratar los datos según nos intereses. En este caso vemos que el producto más caro del día es una botella de Whisky de entre un total de más de 4500 productos! 😛

extraer datos estructurados

Resultado ordenado en Excel

Importante

El origen, uso y tratamiento de los datos que obtengáis con esta técnica es vuestra responsabilidad. Este tutorial se ha hecho con fines únicamente didácticos.

Dependiendo del origen de la información (si son datos de carácter personal o no, si se han obtenido de medios accesibles para todo el público o no…), el uso y el tratamiento de los datos, estos deberán tratarse siguiendo legalidad vigente y es vuestra responsabilidad evaluar todos los requisitos legales y condiciones legales.

Cualquier duda, podéis dejar un comentario 🙂

  • warfire

    BUenas me parece muy interesante tu post

    mi pregunta es se puede hacer lo mismo con bash?

  • Sabier

    Buenas! He intentado aplicarlo a otra web pero me salta todo el rato: tiempo de espera agotado, volviendo a intentar y no sé donde esta el error… Me podrías echar una mano? GRACIAS!

  • Sabier

    Conseguido! Pero ahora tengo un gran problema! Tengo un Div class con un nombre, el siguiente que me interesa tiene un segundo nombre, pero después me encuentro que hay varios con ese segundo nombre en el mismo perfil y se me queda atascado en bucle, ya que al no poder pasar al siguiente se me reinicia la pagina y me copia todo el rato el mismo texto, cómo lo puedo solucionar??

  • Pablo

    Seguramente no alcanzaba el script a llegar a la web solicitada, ¿como lo solucionaste?

  • Pablo

    puedes postear alguna captura del código de la web y tu código?

  • bren

    Hola Sabier, Oye, tengo el mismo problema, como lo solucionaste?

  • Antonio Sanchez

    Hola buenas noches.

    Este artículo es justo lo que necesito. ¿Se puede adaptar el código para la web de carritus.com?

    Imagino que si, el problea es que no tengo ni idea de programación. ¿Alguien me podría ayudar?

  • m4tt

    Hola, te muy bueno el script.. !! me es justo lo que necesitaba… !!!!!!!!!!!!!!!!!!!!!!!
    Gracias

  • Henry Alberto Suárez Badillo

    Hola! Excelente paso a paso.
    Quisiera preguntar si es posible realizar scraping con Beautiful soup a una lista de urls