Extracción de datos de sitios web con Scrapy (I): recopilando información de productos de Zara

Feb 18, 2021 | Python | 0 Comentarios

Introducción al web scraping

En internet tenemos a nuestra disposición grandes cantidades de datos útiles para analizar o utilizar en nuestros modelos de machine learning.

Actualmente, podemos obtener esta valiosa información gracias al web scraping, que es el nombre que recibe el proceso que nos permite extraer el contenido de sitios web de manera automática.

Así, entre otras aplicaciones del web scraping está la recopilación de las características (precio, foto, opiniones, etc.) de los productos de una tienda online o la extracción del titular y cuerpo de las noticias contenidas en un periódico digital.

Afortunadamente, en el lenguaje Python existe una gran cantidad de herramientas para rastrear y extraer datos online y en este tutorial nos centraremos en el popular framework Scrapy.

Por supuesto, también tenéis la posibilidad de implementar vuestro propio rastreador web en Python para adaptarlo a las necesidades concretas que tengáis y conocer como funciona el web scraping por dentro.

Sin embargo, mediante Scrapy tenemos una solución más rápida de utilizar y que además nos proporciona soluciones a los problemas típicos que nos encontramos a la hora de rastrear como la gestión de cookies y sesiones, el filtrado de peticiones duplicadas y el seguimiento de las redirecciones.

En este tutorial, vamos a realizar un caso práctico donde mostraremos paso a paso como extraer con Scrapy los datos de un conjunto de productos de ropa de la web de Zara de forma automatizada para finalmente almacenarlos en un sistema de almacenamiento MongoDB.

Pero primero de todo, empecemos entendiendo el funcionamiento de Scrapy.​

 

¿Qué es Scrapy y cómo funciona?

Scrapy no es una librería como pudiera parecer, sino un completo framework de web scraping para Python. En la siguiente imagen se muestra la arquitectura de Scrapy con todos sus componentes.

 

Arquitectura de Scrapy, imagen extraida de la documentación oficial

 

A grandes rasgos, el funcionamiento de Scrapy es el siguiente:

  • En primer lugar, el Engine recibe las peticiones (Requests) iniciales que le envía la araña (Spider), las programa en el scheduler y solicita las siguientes peticiones a rastrear.
  • El Scheduler va enviando las peticiones a procesar al Engine y este a su vez las envía al componente Downloader.
  • La página es descargada y se crea una respuesta (Response) de esta página que el Engine se encarga de enviar a la araña para que sea procesada.
  • Es entonces cuando la araña o rastreador devuelve al Engine los ítems (Items) con la información extraída de la página.
  • Después, estos ítems son enviados a los Pipelines para procesar y almacenar la información.
  • El proceso se repite hasta que el Scheduler se quede sin peticiones.

Una de las principales ventajas de este framework es que las peticiones se procesan y programan de manera asíncrona. Debido a ello, Scrapy no espera a que termine una petición para enviar otra y se pueden ejecutar a la vez de forma concurrente, acelerando el proceso en gran medida. Además, si una petición falla, el resto de las peticiones seguirán ejecutándose.

Una vez repasado el funcionamiento de Scrapy, es hora de pasar a la práctica.

 

Creación del proyecto

En este ejemplo práctico, vamos a extraer la información de una serie de productos de ropa de la tienda online de Zara. En concreto nos vamos a centrar en obtener el nombre, el precio y la descripción de cada artículo seleccionado, pero podríamos extraer cualquier elemento que queramos, incluso las imágenes.

A modo de ejemplo y para hacernos una idea, podemos ver una imagen de la página de un producto donde están señalados los tres elementos que queremos extraer con Scrapy.

 

Producto en Zara

Producto en Zara

  •  

Antes de nada, debemos instalar la librería Scrapy y sus dependencias mediante el siguiente comando:

pip install Scrapy

Una vez instalado, comenzamos creando un nuevo proyecto Scrapy posicionándonos en el directorio deseado y ejecutando el comando:

scrapy startproject zara

Así, se creará el directorio zara con la siguiente estructura:

zara /
  scrapy.cfg         # fichero de configuración
  zara /             # módulo del proyecto
    __init__.py
    items.py          # fichero con la definición de los items
    middlewares.py    # project middlewares file
    pipelines.py      # fichero con los pipelines
    settings.py       # fichero con los ajustes del proyecto
    spiders/          # directorio con nuestras arañas
    __init__.py

Seguidamente definimos cada uno de estos ficheros creados en el directorio zara:

  • scrapy.cfg: fichero de configuración que se encuentra en el directorio raíz del proyecto y contiene el nombre del módulo con los ajustes del proyecto.
  • zara: módulo del proyecto
  • zara/items.py: contiene las definiciones de nuestros ítems, que son los objetos estructurados que usaremos para cargar los datos que extraigamos y son muy similares a los diccionarios en Python. Así, este tipo de objeto también almacena la información mediante pares clave-valor.
  • zara/middelwares.py: contiene los middlewares del proyecto.
  • zara/pipelines.py: en este fichero implementaremos los pipelines que se encargarán de procesar los ítems una vez hayan sido rastreados por nuestra araña.
  • zara/settings.py: fichero con los ajustes del proyecto.
  • zara/spiders: directorio donde se encontrarán nuestras arañas.

Los tres grandes pasos que debemos de realizar para poder rastrear y extraer la información de nuestra página y que explicaremos a continuación son los siguientes:

  • Definir nuestro objeto Item y los campos que queremos extraer en el fichero items.py.
  • Crear nuestra araña que extraerá la información de las páginas.
  • Implementar el pipeline para procesar el ítem una vez rastreado y almacenarlo en nuestra base de datos MongoDB.

 

Definición del Item

Empezamos definiendo un nuevo Item al que llamaremos Producto en nuestro fichero items.py. Además, declaramos como objetos scrapy.Field los tres campos que deseamos extraer de cada artículo: su nombre, descripción y precio.

import scrapy

class Producto(scrapy.Item):
    nombre = scrapy.Field()
    precio = scrapy.Field()
    descripcion = scrapy.Field()

 

Creación de nuestra araña y extracción de los datos

Las arañas o rastreadores son clases que se encargan de extraer la información de cada página. En este caso, creamos un nuevo fichero en la carpeta spiders llamado zara_spider.py. Antes de mostrar el código completo de nuestra clase a la que llamaremosZaraSpider, es importante explicar la función de ciertos atributos y métodos que debemos definir.

  • name: nombre de nuestra araña.
name = "zara"
  • start_urls: cuando se ejecuta un proceso de Scrapy, la araña comienza a hacer peticiones a las URLs que se han definido en el atributo start_urls. En nuestro caso utilizamos una lista con la URL de los siete productos de ropa de Zara de los cúales queremos obtener los datos:
# URLs para comenzar a rastrear
start_urls = [
      'https://www.zara.com/es/es/abrigo-oversize-con-lana-p02268744.html?v1=99425057&v2=1712675',
      'https://www.zara.com/es/es/camiseta-b%C3%A1sica-medium-weight-p00381300.html?v1=86668649&v2=1720409',
      'https://www.zara.com/es/es/sudadera-capucha-cremalleras-p00761406.html?v1=99422974&v2=1720409',
      'https://www.zara.com/es/es/pantal%C3%B3n-chino-carrot-fit-p00706475.html?v1=89361432&v2=1717453',
      'https://www.zara.com/es/es/sandalia-plana-track-p13647710.html?v1=86083745&v2=1471790',
      'https://www.zara.com/es/es/polo-rayas-p09240419.html?v1=99423481&v2=1720387',
      'https://www.zara.com/es/es/mochila-t%C3%A9cnica-p13201720.html?v1=99425027&v2=1720458'
]

Nuestra araña ya tiene un método start_requests() implementado por defecto, que se encarga de generar una respuesta por cada una de estas URLs y de enviarla al método callback por defecto denominado parse.

Asimismo, podéis sobreescribir este método si queréis modificar funcionamiento, por ejemplo, en caso de que busquéis filtrar las URLs con las que comenzar a rastrear.

  • allowed_domains: lista opcional con el nombre de los dominios que nuestra araña puede rastrear. En este ejemplo no pasaría nada en caso de que no la añadamos ya que nos vamos a limitar a rastrear únicamente las URLs indicadas en start_urls.
# Dominios permitidos 
allowed_domains = ['zara.com']
  • parse: como hemos mencionado antes, el rastreador llama a este método de callback, pasándole los objetos de respuesta de las páginas como argumento. En el método parse nos encargaremos de extraer la información que queramos de cada URL mediante expresiones XPaths, expresiones regurales (REgex) o selectores CSS.

En nuestro caso, utilizaremos el lenguaje XPath, que trata a los documentos XML y HTML como árboles y nos permitirá seleccionar los diferentes nodos como por ejemplo, un atributo, de manera sencilla. No me detendré a explicar la sintaxis de este lenguaje, pero podéis echar un vistazo a este tutorial con el que seguramente podráis extraer la mayoría de los campos que necesitéis.

Por ejemplo, para extraer el nombre de una prenda de una página de Zara, podemos utilizar la siguiente expresión:

//h1[@class="product-detail-info__name"]/text()

Esta expresión selecciona los nodos de texto que se encuentran dentro de los elementos h1 cuya clase (class) sea igual a product-detail-info__name.

Dicho todo esto, solo nos queda juntar todas las piezas y mostrar el código completo de nuestra clase ZaraSpider.

import scrapy
from zara.items import Producto

class ZaraSpider(scrapy.Spider):
    # Nombre de la araña
    name = "zara"
    
    # Dominios permitidos
    allowed_domains = ['zara.com']
    
    # URLs para comenzar a rastrear
    start_urls = [
        'https://www.zara.com/es/es/abrigo-oversize-con-lana-p02268744.html?v1=99425057&v2=1712675',
        'https://www.zara.com/es/es/camiseta-b%C3%A1sica-medium-weight-p00381300.html?v1=86668649&v2=1720409',
        'https://www.zara.com/es/es/sudadera-capucha-cremalleras-p00761406.html?v1=99422974&v2=1720409',
        'https://www.zara.com/es/es/pantal%C3%B3n-chino-carrot-fit-p00706475.html?v1=89361432&v2=1717453',
        'https://www.zara.com/es/es/sandalia-plana-track-p13647710.html?v1=86083745&v2=1471790',
        'https://www.zara.com/es/es/polo-rayas-p09240419.html?v1=99423481&v2=1720387',
        'https://www.zara.com/es/es/mochila-t%C3%A9cnica-p13201720.html?v1=99425027&v2=1720458'
    ]
    
    def parse(self, response):
        producto = Producto()
        
        # Extraemos el nombre del producto, la descripcion y su precio
        producto['nombre'] = response.xpath('//h1[@class="product-detail-info__name"]/text()').extract_first()
        producto['precio'] = response.xpath('//span[@class="price__amount"]/text()').extract_first()
        producto['descripcion'] = response.xpath('//div[@class="expandable-text product-detail-info__description"]//text()').extract_first()
        
        yield producto

 

Almacenando los ítems en MongoDB

El siguiente paso consiste en implementar un pipeline que se encargue de almacenar cada página en una colección de MongoDB (Scrapy también permite exportar los datos a ficheros CSV o JSON mediante los Feed Exports).

De esta manera, para almacenar la información MongoDB añadiremos al fichero pipelines.py la implementación de un ejemplo de pipeline disponible en la documentación de Scrapy. En consecuencia, el fichero pipelines.py quedaría de la siguiente manera:

import pymongo
from itemadapter import ItemAdapter

class MongoPipeline:

    collection_name = 'scrapy_items'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].insert_one(ItemAdapter(item).asdict())
        return item

En la variable collection_name le indicamos el nombre de la colección donde vamos a almacenar los documentos con los datos.

Además, no debemos olvidarnos de indicar en el fichero de ajustes (settings.py) de Scrapy, por un lado, la ruta y el nombre de nuestro pipeline, y por otro lado, el nombre de nuestra base de datos y la dirección de MongoDB. Para ello, debéis añadir en el fichero las siguientes líneas con la información de vuestra base de datos y del pipeline:

# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html

ITEM_PIPELINES = {
    'zara.pipelines.MongoPipeline': 300,
}

MONGO_URI = 'mongodb://localhost:27017'
MONGO_DATABASE = 'nombre-db'

 

Cambiando el agente de usuario para evitar bloqueos

Algunos sitios web tienen mecanismos anti-rastreo, por lo que pueden bloquear el acceso de nuestra araña si detectan algún comportamiento sospechoso. Debido a ello y para evitar este tipo de situaciones, es recomendable cambiar el agente de usuario (user agent) con el que se identifica nuestra araña. De hecho, si intentáis utilizar el agente de usuario que viene por defecto para rastrear la web de Zara, veréis que al descargar las páginas no os devolverá más que códigos de error 404.

Así, podéis cambiar el agente de usuario de manera sencilla modificando la siguiente línea en el archivo settings.py:

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"

En nuestro caso, nos estamos identificando como un navegador Mozilla Firefox con el sistema operativo Windows.

Una vez configurado este ajuste, ya estamos listos para ejecutar nuestra araña y comprobar los resultados.

 

Ejecutando nuestra araña

Para hacer funcionar a nuestra araña, debemos ejecutar el siguiente comando desde la carpeta del proyecto:

scrapy crawl zara

Una vez finalizado, en las últimas líneas del log del proceso se muestra diversa información interesante como el tiempo total del rastreo (elapsed_time_seconds) o el número de ítems rastreados (item_scraped_count).

2021-02-13 22:31:26 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 2259,
 'downloader/request_count': 7,
 'downloader/request_method_count/GET': 7,
 'downloader/response_bytes': 359195,
 'downloader/response_count': 7,
 'downloader/response_status_count/200': 7,
 'elapsed_time_seconds': 11.992025,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2021, 2, 13, 21, 31, 26, 467670),
 'item_scraped_count': 7,
 'log_count/DEBUG': 14,
 'log_count/INFO': 11,
 'response_received_count': 7,
 'scheduler/dequeued': 7,
 'scheduler/dequeued/memory': 7,
 'scheduler/enqueued': 7,
 'scheduler/enqueued/memory': 7,
 'start_time': datetime.datetime(2021, 2, 13, 21, 31, 14, 475645)}

Por último, solo nos queda comprobar que, en efecto, los datos de las prendas de ropa se han insertado correctamente en nuestra colección de MongoDB. Para realizar la comprobación, entramos en el GUI Robo 3T y realizamos la consulta correspondiente. A continuación, podemos ver el resultado:

 

Productos insertados en MongoDB

 

Como se puede observar en la imagen, los siete productos de ropa se han insertado con éxito en la colección, cada uno en un documento distinto con su nombre, precio y descripción.

En este tutorial hemos aprendido como extraer la información de una serie de URLs seleccionadas del sitio web de Zara, pero ¿No sería fantástico poder obtener los datos de absolutamente todas o gran parte de las páginas que componen un dominio dado?

En la siguiente publicación de esta serie explicaremos como hacer que nuestra araña extraiga y navegue a través de los enlaces de las páginas de nuestro interés de manera iterativa hasta que rastré todo el dominio o un número máximo establecido de páginas.

Como siempre recordar que podéis obtener el código utilizado en este tutorial en nuestra cuenta de Github. ¡Hasta la próxima!

También te puede interesar:

5 trucos para Jupyter Notebook que no debes perderte

Descubre 5 trucos para Jupyter Notebook no tan conocidos que te ayudarán familiarizarte más con esta herramienta y ser más eficiente.

Otros 5 trucos de Jupyter Notebook que probablemente desconozcas

En este artículo se explican 5 nuevos trucos para Jupyter Notebook que probablemente desconozcas y que mejorarán vuestra productividad.

Extracción de datos de sitios web con Scrapy (II): rastreando todas las URLs del sitio web de Zara

En este post construimos un rastreador con Scrapy que extrae los datos de manera recursiva todas las URLs del sitio web de Zara.

Extracción de datos de Twitter con Python (sin consumir la API)

En esta publicación os enseñaremos como poder extraer datos de Twitter en Python mediante la librería Twint. De esta forma, podremos obtener facilmente los últimos tweets que contengan cierta palabra o que pertenezcan a un determinado usuario y aplicar varios filtros.

Aplicación de la Distribución t de Student en Python: Ejemplos prácticos

Distribución t en Python: Aprende cómo aplicar la Distribución t en Python con ejemplos para pruebas de hipótesis e intervalos de confianza.

Aprendiendo Bootstraping con Python

Aprende cómo aplicar el bootstraping en la práctica utilizando Python. Explore casos de uso y ejemplos de código para fortalecer tus habilidades estadísticas.

Dominando Apache Spark (VI): Diferentes tipos de Joins en DataFrames con ejemplos en PySpark

Descubre los secretos de los joins en DataFrames con Spark en este artículo. Aprende a utilizar diferentes tipos de joins en PySpark con ejemplos detallados para perfeccionar tus habilidades de procesamiento de datos.

Consejos y ejemplos para utilizar Prompts en ChatGPT en programación

Aprende a utilizar los promps en ChatGPT para abordar desafíos comunes en el desarrollo.

Dominando Apache Spark (VII): Funciones para cargar y exportar datos en PySpark

En este artículo, exploramos funciones avanzadas para importar y exportar datos en PySpark.

Otros 6 Trucos clave para Promps en ChatGPT para Programación

Descubre consejos y trucos clave para sacar el máximo provecho de los promps en ChatGPT en tus proyectos de programación.

Ads Blocker Image Powered by Code Help Pro

Por favor, permite que se muestren anuncios en nuestro sitio web

Querido lector,

Esperamos que estés disfrutando de nuestro contenido. Entendemos la importancia de la experiencia sin interrupciones, pero también queremos asegurarnos de que podamos seguir brindándote contenido de alta calidad de forma gratuita. Desactivar tu bloqueador de anuncios en nuestro sitio nos ayuda enormemente a lograrlo.

¡Gracias por tu comprensión y apoyo!