Web Scraping Con Scrapy: La manera fácil

Mihai Maxim el 30 Ene 2023

blog-image

Web scraping con Scrapy

Scrapy es una potente biblioteca de Python para extraer datos de sitios web. Es rápido, eficiente y fácil de usar - confía en mí, he estado allí. Si usted es un científico de datos, un desarrollador, o alguien que le encanta jugar con los datos, Scrapy tiene algo que ofrecerle. Y lo mejor de todo es que es gratuito y de código abierto.

Los proyectos Scrapy vienen con una estructura de archivos que ayuda a organizar el código y los datos. Esto facilita la creación y el mantenimiento de los raspadores web, por lo que merece la pena tenerlo en cuenta si tiene previsto realizar un raspado web serio. Web scraping con Scrapy Es como tener un asistente útil (aunque virtual) a tu lado mientras te embarcas en tu viaje de extracción de datos.

Lo que vamos a construir

Cuando aprendes algo nuevo, una cosa es leer sobre ello y otra hacerlo. Es por eso que hemos decidido construir un scraper juntos a medida que avanzamos a través de esta guía. Es la mejor manera de obtener una comprensión práctica de cómo funciona el raspado web con Scrapy.

¿Qué vamos a construir exactamente? Construiremos un raspador que extraiga definiciones de palabras del sitio web Urban Dictionary. Es un objetivo divertido para el scraping y ayudará a que el proceso de aprendizaje sea más ameno. Nuestro scraper será sencillo: devolverá las definiciones de varias palabras que se encuentran en el sitio web del Urban Dictionary. Utilizaremos el soporte integrado de Scrapy para seleccionar y extraer datos de documentos HTML con el fin de extraer las definiciones que necesitamos.

Empecemos. En la siguiente sección, repasaremos los requisitos previos que necesitarás para seguir este tutorial. Nos vemos allí.

Requisitos previos

Antes de sumergirnos en la construcción de nuestro scraper, hay algunas cosas que necesitarás configurar. En esta sección, vamos a cubrir cómo instalar Scrapy y configurar un entorno virtual para nuestro proyecto. La documentación de Scrapy sugiere instalar Scrapy en un entorno virtual dedicado. Al hacerlo, evitarás cualquier conflicto con los paquetes de tu sistema.

Estoy ejecutando Scrapy en Ubuntu 22.04.1 WSL (Windows Subsystem for Linux), así que voy a configurar un entorno virtual para mi máquina.

Te animo a que leas el capítulo "Entendiendo la estructura de carpetas" para entender completamente las herramientas con las que estamos trabajando. También, echa un vistazo al capítulo "Scrapy Shell", hará tu experiencia de desarrollo mucho más fácil.

Configuración de un entorno virtual Python

Para configurar un entorno virtual para Python en Ubuntu, puedes utilizar el comando mkvirtualenv. Primero, asegúrate de que tienes virtualenv y virtualenvwrapper instalados:

$ sudo apt-get install virtualenv virtualenvwrapper

Añada estas líneas al final de su archivo .bashrc:

export WORKON_HOME=$HOME/.virtualenvs

export PROJECT_HOME=$HOME/Deve

export VIRTUALENVWRAPPER_PYTHON='/usr/bin/python3'

source /usr/local/bin/virtualenvwrapper.sh

Yo utilicé Vim para editar el archivo, pero puedes elegir el editor que prefieras:

vim ~/.bashrc

// Utilice ctr + i para entrar en el modo insertar, utilice la flecha hacia abajo para desplazarse hasta el final del archivo.

// Pegue las líneas al final del archivo.

// Pulse escape para salir del modo insertar, escriba wq y pulse enter para guardar los cambios y salir de Vim.

A continuación, cree un nuevo entorno virtual con mkvirtualenv:

$ mkvirtualenv scrapy_env

Ahora deberías ver un (scrapy_env) añadido al principio de tu línea de terminal.

Para salir del entorno virtual, escriba $ desactivar

Para volver al entorno virtual scrappy_env, escribe $ workon scrapy_env

Instalación de Scrapy

Puede instalar Scrapy con el gestor de paquetes pip:

$ pip install scrapy

Esto instalará la última versión de Scrapy.

Puedes crear un nuevo proyecto con el comando scrapy startproject:

$ scrapy startproject miproyecto

Esto inicializará un nuevo proyecto Scrapy llamado "myproject". Debe contener la estructura del proyecto por defecto.

Comprender la estructura de carpetas

Esta es la estructura por defecto del proyecto:

myproject

├── myproject

│ ├── __init__.py

│ ├── items.py

│ ├── middlewares.py

│ ├── pipelines.py

│ ├── settings.py

│ └── spiders

│ └── __init__.py

└── scrapy.cfg

items.py

items.py es un modelo para los datos extraídos. Este modelo se utilizará para almacenar los datos que extraigas del sitio web.

Ejemplo:

import scrapy

class Producto(scrapy.Artículo):

nombre = scrapy.Campo()

precio = scrapy.Campo()

descripción = scrapy.Campo()

Aquí definimos un Item llamado Producto. Puede ser utilizado por un Spider (ver /spiders) para almacenar información sobre el nombre, precio y descripción de un producto.

/arañas

/spiders es una carpeta que contiene clases Spider. En Scrapy, las arañas son clases que definen cómo se debe raspar un sitio web.

Ejemplo:

import scrapy

from myproject.items import Product

class MySpider(scrapy.Spider):

name = 'myspider'

start_urls = ['<example_website_url>']



def parse(self, response):

# Extract the data for each product

for product_div in response.css('div.product'):

product = Product()

product['name'] = product_div.css('h3.name::text').get()

product['price'] = product_div.css('span.price::text').get()

product['description'] = product_div.css('p.description::text').get()

yield product

La "araña" recorre las start_urls, extrae el nombre, precio y descripción de todos los productos encontrados en las páginas (usando selectores css) y almacena los datos en el Item Producto (ver items.py). A continuación, "cede" estos elementos, lo que hace que Scrapy los pase al siguiente componente del pipeline (ver pipelines.py).

Yield es una palabra clave en Python que permite a una función devolver un valor sin terminar la función. En su lugar, produce el valor y suspende la ejecución de la función hasta que se solicite el siguiente valor.

Por ejemplo:

def count_up_to(max):

count = 1

while count <= max:

yield count

count += 1

for number in count_up_to(5):

print(number)

// returns 1 2 3 4 5 (each on a new line)

pipelines.py

Los pipelines se encargan de procesar los ítems (ver ítems.py y /spiders) que extraen las arañas. Puedes utilizarlas para limpiar el HTML, validar los datos y exportarlos a un formato personalizado o guardarlos en una base de datos.

Ejemplo:

import pymongo

class MongoPipeline(object):

def __init__(self):

self.conn = pymongo.MongoClient('localhost', 27017)

self.db = self.conn['mydatabase']

self.product_collection = self.db['products']

self.other_collection = self.db['other']



def process_item(self, item, spider):

if spider.name == 'product_spider':

//inserta el elemento en product_collection

elif spider.name == 'other_spider':

//inserta el elemento en la otra_colección

return item

Creamos un Pipeline llamado MongoPipeline. Se conecta a dos colecciones MongoDB (product_collection y other_collection). El pipeline recibe items (ver items.py) desde spiders (ver /spiders) y los procesa en la función process_item. En este ejemplo, la función process_item añade los ítems a sus colecciones designadas.

settings.py

settings.py almacena una variedad de configuraciones que controlan el comportamiento del proyecto Scrapy, tales como los pipelines, middlewares, y extensiones que deberían usarse, así como configuraciones relacionadas con cómo el proyecto debería manejar las peticiones y respuestas.

Por ejemplo, se puede utilizar para establecer el orden de ejecución de los pipelines (ver pipelines.py):

ITEM_PIPELINES = {

'myproject.pipelines.MongoPipeline': 300,

'myproject.pipelines.JsonPipeline': 302,

}

// MongoPipeline will execute before JsonPipeline, because it has a lower order number(300)

O establecer un contenedor de exportación:

FEEDS = {

'items': {'uri': 'file:///tmp/items.json', 'format': 'json'},

}

// this will save the scraped data to a items.json

scrappy.cfg

scrapy.cfg es el archivo de configuración para los ajustes principales del proyecto.

[settings] 

default = [nombre del proyecto].settings

[deploy]

#url = http://localhost:6800/

project = [nombre del proyecto].

middlewares.py

Hay dos tipos de middlewares en Scrapy: middlewares downloader y middlewares spider.

Los middlewares de descarga son componentes que se pueden utilizar para modificar solicitudes y respuestas, gestionar errores e implementar una lógica de descarga personalizada. Se sitúan entre la araña y el descargador de Scrapy.

Los middlewares de araña son componentes que pueden utilizarse para implementar una lógica de procesamiento personalizada. Se sitúan entre el motor y la araña.

El caparazón de chatarra

Antes de embarcarnos en el emocionante viaje de implementar nuestro raspador de Urban Dictionary, primero deberíamos familiarizarnos con Scrapy Shell. Esta consola interactiva nos permite probar nuestra lógica de raspado y ver los resultados en tiempo real. Es como una caja de arena virtual donde podemos jugar y afinar nuestro enfoque antes de soltar nuestra araña en la web. Créeme, te ahorrará mucho tiempo y dolores de cabeza a largo plazo. Así que vamos a divertirnos un poco y conocer el Scrapy Shell.

Abrir el caparazón

Para abrir el Scrapy Shell, primero tendrá que navegar hasta el directorio de su proyecto Scrapy en su terminal. A continuación, sólo tiene que ejecutar el siguiente comando:

scrapy shell

Esto abrirá el Scrapy Shell y se le presentará con un símbolo del sistema donde puede introducir y ejecutar comandos Scrapy. También puede pasar una URL como argumento al comando shell para raspar directamente una página web, así:

scrapy shell <url>

Por ejemplo:

scrapy shell https://www.urbandictionary.com/define.php?term=YOLO

Devolverá (en el objeto de respuesta) el html de la página web que contiene las definiciones de la palabra YOLO (en el Urban Dictionary).

Alternativamente, una vez que entras en el shell, puedes utilizar el comando fetch para obtener una página web.

fetch('https://www.urbandictionary.com/define.php?term=YOLO')

También puede iniciar el intérprete de órdenes con el parámetro nolog para que no muestre registros:

scrapy shell --nolog

Trabajar con el shell

En este ejemplo, he obtenido la URL "https://www.urbandictionary.com/define.php?term=YOLO" y he guardado el html en el archivo test_output.html.

(scrapy_env) mihai@DESKTOP-0RN92KH:~/myproject$ scrapy shell --nolog

[s] Available Scrapy objects:

[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)

[s] crawler <scrapy.crawler.Crawler object at 0x7f1eef80f6a0>

[s] item {}

[s] settings <scrapy.settings.Settings object at 0x7f1eef80f4c0>

[s] Useful shortcuts:

[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)

[s] fetch(req) Fetch a scrapy.Request and update local objects

[s] shelp() Shell help (print this help)

[s] view(response) View response in a browser

>>> response // response is empty

>>> fetch('https://www.urbandictionary.com/define.php?term=YOLO')

>>> response

<200 https://www.urbandictionary.com/define.php?term=Yolo>

>>> with open('test_output.html', 'w') as f:

... f.write(response.text)

...

118260

Ahora inspeccionemos test_output.html e identifiquemos los selectores que necesitaremos para extraer los datos de nuestro Urban Dictionary scraper.

blog-image

Podemos observar que:

  • Cada contenedor de definición de palabra tiene la clase "definición".
  • El significado de la palabra se encuentra dentro del div con la clase "significado".
  • Los ejemplos de la palabra se encuentran dentro del div con la clase "ejemplo".
  • La información sobre el autor de la entrada y la fecha se encuentran dentro del div con la clase "colaborador".

Ahora vamos a probar algunos selectores en el Scrapy Shell:

Para obtener referencias a cada contenedor de definición podemos utilizar selectores CSS o XPath:

Puede obtener más información sobre los selectores XPath aquí: https://www.webscrapingapi.com/the-ultimate-xpath-cheat-sheet

definiciones = response.css('div.definition')
definiciones = response.xpath('//div[contains(@class, "definición")]')

Debemos extraer el significado, el ejemplo y la información posterior de cada contenedor de definición. Probemos algunos selectores con el primer contenedor:

>>> first_def = definitions[0]

>>> meaning = first_def.css('div.meaning').xpath(".//text()").extract()

>>> meaning

['Yolo ', 'significa', ', '', 'Sólo se vive una vez', ''.']

>>> meaning = "".join(significado)

>>> significado

'Yolo significa, 'Sólo se vive una vez'.'

>>> ejemplo = first_def.css('div.ejemplo').xpath(".//texto()").extract()

>>> ejemplo = "".join(ejemplo)

>>> ejemplo

'"Ponte el cinturón de seguridad". Jessica said.\r "HAH, YOLO!" Replica Anna.\r(Luego proceden a tener un accidente de coche. Larga historia corta...Ponte el cinturón de seguridad.)'

>>> post_data = first_def.css('div.contributor').xpath(".//text()").extract()

>>> post_data

['by ', 'Soy feo', ' April 24, 2019']

Utilizando el shell Scrapy, hemos podido encontrar rápidamente un selector general que se adapta a nuestras necesidades.

definition.css('div.<meaning|example|contributor>').xpath(".//text()").extract() 

// returns an array with all the text found inside the <meaning|example|contributor>

ex: ['Yolo ', 'means', ', '', 'You Only Live Once', ''.']

Para obtener más información sobre los selectores de Scrapy, consulte la documentación. https://docs.scrapy.org/en/latest/topics/selectors.html

Aplicación del raspador de Urban Dictionary

¡Buen trabajo! Ahora que le has cogido el truco al uso de Scrapy Shell y entiendes el funcionamiento interno de un proyecto Scrapy, es hora de sumergirse en la implementación de nuestro raspador Urban Dictionary. A estas alturas, deberías sentirte seguro y preparado para asumir la tarea de extraer todas esas divertidísimas (y a veces cuestionables) definiciones de palabras de la web. Así que sin más preámbulos, ¡comencemos a construir nuestro scraper!

Definición de un artículo

Primero, implementaremos un Item: (ver items.py)

class UrbanDictionaryItem(scrapy.Item):

significado = scrapy.Field()

autor = scrapy.Field()

fecha = scrapy.Field()

ejemplo = scrapy.Field()

Esta estructura contendrá los datos extraídos del Spider.

Definir una araña

Así es como definiremos nuestro Spider (véase /spiders):

importar scrapy

from ..items import UrbanDictionaryItem

clase UrbanDictionarySpider(scrapy.Spider):

nombre = 'diccionario_urbano

start_urls = ['https://www.urbandictionary.com/define.php?term=Yolo']

def parse(self, response):

definiciones = response.css('div.definition')

para definición en definiciones:

item = UrbanDictionaryItem()

item['significado'] = definition.css('div.significado').xpath(".//text()").extract()

item['ejemplo'] = definition.css('div.ejemplo').xpath(".//texto()").extract()

author = definition.css('div.contributor').xpath(".//text()").extract()

item['fecha'] = autor[2]

item['autor'] = autor[1]

elemento de rendimiento

Para ejecutar la araña urban_dictionary, utilice el siguiente comando:

scrapy crawl urban_dictionary

// los resultados deberían aparecer en la consola (muy probablemente en la parte superior de los logs)

Creación de una canalización

En este punto, los datos están sin desinfectar.

{'author': 'Soy ugly',

'date': ' April 24, 2019',

'example': ['“Put your ',

'seatbelt',

' on.” Jessica said.\n',

'“HAH, YOLO!” Replies Anna.\n',

'(They then proceed to have a ',

'car crash',

'. ',

'Long story short',

'...Wear a seatbelt.)'],

'meaning': ['Yolo ', 'means', ', ‘', 'You Only Live Once', '’.']}

Queremos modificar los campos "ejemplo" y "significado" para que contengan cadenas, no matrices. Para ello, escribiremos un Pipeline (ver pipelines.py) que transformará las matrices en cadenas concatenando las palabras.

class SanitizePipeline:

def process_item(self, item, spider):

# Sanitize the 'meaning' field

item['meaning'] = "".join(item['meaning'])

# Sanitize the 'example' field

item['example'] = "".join(item['ejemplo'])



# Limpia el campo 'fecha'

item['fecha'] = item['fecha'].strip()

return item

//ex: ['Yolo ', 'significa', ', '', 'Sólo se vive una vez', ''.'] se convierte en

'Yolo significa, 'Sólo se vive una vez'.'

Habilitar la canalización

Después de definir el Pipeline, necesitamos habilitarlo. Si no lo hacemos, nuestros objetos UrbanDictionaryItem no serán desinfectados. Para ello, añádelo a tu archivo settings.py (ver settings.py):

 ITEM_PIPELINES = {

'myproject.pipelines.SanitizePipeline': 1,

}

De paso, también puede especificar un archivo de salida para colocar los datos obtenidos.

 FEEDS = {

'items': {'uri': 'file:///tmp/items.json', 'format': 'json'},

}

// this will put the scraped data in an items.json file.

Opcional: Implementación de un middleware proxy

El scraping web puede ser un poco pesado. Uno de los principales problemas con los que nos encontramos a menudo es que muchos sitios web requieren la ejecución de javascript para mostrar completamente su contenido. Esto puede causar grandes problemas para nosotros como web scrapers, ya que nuestras herramientas a menudo no tienen la capacidad de ejecutar javascript como lo hace un navegador web normal. Esto puede conducir a la extracción de datos incompletos, o peor aún, a que nuestra IP sea baneada del sitio web por hacer demasiadas peticiones en un corto período de tiempo.

Nuestra solución a este problema es WebScrapingApi. Con nuestro servicio, sólo tiene que realizar solicitudes a la API y ésta se encargará de todo el trabajo pesado por usted. Ejecutará JavaScript, rotará proxies, e incluso manejará CAPTCHAs, asegurando que usted pueda raspar incluso el más obstinado de los sitios web con facilidad.

Un middleware proxy reenviará cada solicitud de obtención hecha por Scrapy al servidor proxy. El servidor proxy hará la petición por nosotros y nos devolverá el resultado.

Primero, definiremos una clase ProxyMiddleware dentro del archivo middlewares.py.

import base64

class ProxyMiddleware:

def process_request(self, request, spider):

# Set the proxy for the request

request.meta['proxy'] = "http://proxy.webscrapingapi.com:80"

request.meta['verify'] = False

# Set the proxy authentication for the request

proxy_user_pass = "webscrapingapi.proxy_type=residential.render_js=1:<API_KEY>"

encoded_user_pass = base64.b64encode(proxy_user_pass.encode()).decode()

request.headers['Proxy-Authorization'] = f'Basic {encoded_user_pass}'

En este ejemplo, hemos utilizado el servidor proxy WebScrapingApi.

webscrapingapi.proxy_type=residential.render_js=1 is the proxy authentication username, <API_KEY> the password.

Puede obtener una API_KEY gratuita creando una nueva cuenta en https://www.webscrapingapi.com/.

Después de definir el ProxyMiddleware, todo lo que tenemos que hacer es habilitarlo como DOWNLOAD_MIDDLEWARE en el archivo settings.py.

DOWNLOADER_MIDDLEWARES = {

'myproject.middlewares.ProxyMiddleware': 1,

}

Y, eso es todo. Como puedes ver, el web scraping con Scrapy puede simplificar mucho nuestro trabajo.

Conclusión

¡Lo hemos conseguido! Construimos un scraper que puede extraer definiciones de Urban Dictionary y aprendimos mucho sobre Scrapy en el camino. Desde la creación de elementos personalizados hasta la utilización de middlewares y pipelines, hemos demostrado lo potente y versátil que puede ser el web scraping con Scrapy. ¿Y lo mejor de todo? Todavía queda mucho por descubrir.

Enhorabuena por haber llegado hasta el final de este viaje conmigo. Ahora debería sentirse seguro de su capacidad para abordar cualquier proyecto de web scraping que se le presente. Sólo recuerde, el web scraping no tiene por qué ser desalentador o abrumador. Con las herramientas y los conocimientos adecuados, puede ser una experiencia divertida y gratificante. Si alguna vez necesita ayuda, no dude en ponerse en contacto con nosotros en https://www.webscrapingapi.com/. Sabemos todo sobre el web scraping y estaremos encantados de ayudarle en todo lo que podamos.

Noticias y actualidad

Manténgase al día de las últimas guías y noticias sobre raspado web suscribiéndose a nuestro boletín.

We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Artículos relacionados

miniatura
GuíasCómo raspar datos de productos de Amazon: Guía completa de mejores prácticas y herramientas

Explore las complejidades del scraping de datos de productos de Amazon con nuestra guía en profundidad. Desde las mejores prácticas y herramientas como Amazon Scraper API hasta las consideraciones legales, aprenda a superar los desafíos, eludir los CAPTCHA y extraer información valiosa de forma eficiente.

Suciu Dan
avatar de autor
Suciu Dan
15 minutos de lectura
miniatura
Ciencia del Web ScrapingScrapy vs. Selenium: Guía completa para elegir la mejor herramienta de Web Scraping

Explore la comparación en profundidad entre Scrapy y Selenium para el scraping web. Desde la adquisición de datos a gran escala hasta la gestión de contenido dinámico, descubra los pros, los contras y las características únicas de cada uno. Aprenda a elegir el mejor marco de trabajo en función de las necesidades y la escala de su proyecto.

WebscrapingAPI
avatar de autor
WebscrapingAPI
14 min leer
miniatura
GuíasTutorial de Scrapy Splash: Dominar el arte del scraping de sitios web renderizados en JavaScript con Scrapy y Splash

Aprenda a scrapear sitios web dinámicos con JavaScript utilizando Scrapy y Splash. Desde la instalación hasta la escritura de una araña, el manejo de la paginación y la gestión de las respuestas de Splash, esta completa guía ofrece instrucciones paso a paso tanto para principiantes como para expertos.

Ștefan Răcila
avatar de autor
Ștefan Răcila
6 min leer