Splash

Splash es una herramienta de renderizado de pΓ‘ginas web para web scraping de sitios dinΓ‘micos que utilizan JavaScript. Permite obtener el contenido completo de las pΓ‘ginas renderizadas, lo que facilita la extracciΓ³n de datos de aplicaciones web modernas. Proporciona funciones para interactuar con elementos dinΓ‘micos y obtener datos precisos.

Para poder utilizar splash necesitas tener instalado docker y para utilizarlo en el vanegador tienes que ejecutar el siguiente comando:

sudo docker run -it -p 8050:8050 scrapinghub/splash

# Busca en el navegador
http://127.0.0.1:8050

Splash funciona con el lenguje Lua y al igual que en C los scripts requeiren de una funcion main con su return al final. Para ejecutar las distintas acciones es recomendable utilizar la funcion assert, ya que esta comprueba si la funcion en su interior ha sido ejecutada correctamente y de no ser asi devuelve un error.

funcion main(splash, args)
    url = args.url
    assert(splash:go(url))
    assert(splash:wait(1))
    return
    {
        image = splash:png(),
        html = splash:html()
    }
end

Buscar elementos

Cuando se trabaja con Splash, una de las tareas fundamentales es la bΓΊsqueda y extracciΓ³n de elementos especΓ­ficos en el HTML renderizado de una pΓ‘gina web. Splash proporciona diversas opciones y comandos para llevar a cabo esta tarea.

SelecciΓ³n de elementos por etiqueta

Puedes utilizar el comando splash:select para seleccionar elementos basados en su etiqueta. Por ejemplo, para seleccionar todos los elementos div en la pΓ‘gina, puedes utilizar el siguiente cΓ³digo:

local divs = splash:select_all('div')
for _, div in ipairs(divs) do
    -- Realizar acciones con los elementos seleccionados
end

SelecciΓ³n de elementos por clase o ID

Si deseas buscar elementos por su clase o ID, puedes utilizar los selectores de CSS en conjunto con el comando splash:select. Por ejemplo, para seleccionar un elemento con una clase especΓ­fica, puedes hacer lo siguiente:

local element = splash:select('.mi-clase')

Si en cambio deseas seleccionar un elemento por su ID, puedes utilizar:

local element = splash:select('#mi-id')

ExtracciΓ³n de atributos y texto de elementos

Una vez que hayas seleccionado un elemento, puedes extraer atributos especΓ­ficos o el texto contenido en Γ©l. Por ejemplo, para obtener el valor del atributo href de un enlace (<a>), puedes utilizar:

local link = splash:select('a')
local href = link.node.attributes.href

local href = link:get_attribute('href')

Si deseas obtener el texto contenido dentro de un elemento, puedes utilizar la propiedad node.text. Por ejemplo:

local paragraph = splash:select('p')
local text = paragraph.node.text

BΓΊsqueda de elementos anidados

Splash tambiΓ©n te permite buscar elementos anidados dentro de un elemento seleccionado previamente. Puedes utilizar el comando :select en conjunto con el selector CSS correspondiente. Por ejemplo, para seleccionar todos los elementos <li> dentro de una lista <ul>, puedes hacer lo siguiente:

luaCopy codelocal ul = splash:select('ul')
local lis = ul:select_all('li')

Recuerda adaptar los selectores CSS segΓΊn la estructura y los elementos especΓ­ficos que deseas buscar en la pΓ‘gina.

Acciones con Splash

Splash es una herramienta poderosa para interactuar con pΓ‘ginas web dinΓ‘micas y realizar diversas acciones automatizadas. A continuaciΓ³n, exploraremos las diferentes acciones que se pueden realizar con Splash y cΓ³mo utilizarlas en el contexto del web scraping.

Centrar elementos

El mΓ©todo focus se utiliza para establecer el foco en un elemento HTML, como un campo de entrada de texto o un botΓ³n. Esto permite que el elemento estΓ© listo para recibir eventos de teclado o interacciones del usuario.

local input = assert(splash:select('.my-input'))
input:focus()

En este ejemplo, input:focus() establece el foco en un elemento con la clase CSS .my-input. DespuΓ©s de eso, el elemento estΓ‘ listo para recibir eventos de teclado o interacciones del usuario.

Escribir texto en campos de formulario

Una de las formas de escribir texto en campos de formulario utilizando Splash es a travΓ©s del mΓ©todo splash:send_text. Sin embargo, tambiΓ©n es posible utilizar el mΓ©todo element:send_text para interactuar directamente con un elemento especΓ­fico. Por ejemplo:

local input = splash:select('.search-input')
input:send_text('texto de bΓΊsqueda')

splash:send_text('.search-input', 'texto de bΓΊsqueda')

En este caso, se selecciona el elemento de entrada con la clase .search-input utilizando splash:select y luego se utiliza send_text en ese elemento para escribir el texto de bΓΊsqueda especificado.

Hacer clic en elementos

El mΓ©todo splash:mouse_click que mencionamos anteriormente se puede utilizar para simular clics en elementos. Sin embargo, tambiΓ©n es posible utilizar el mΓ©todo element:click para hacer clic en un elemento especΓ­fico. Por ejemplo:

local button = splash:select('.btn-submit')
button:click()

splash:mouse_click('.btn-submit')

AquΓ­, se selecciona el botΓ³n con la clase .btn-submit utilizando splash:select y se utiliza click en ese elemento para simular el clic.

Presionar teclas

AdemΓ‘s de la opciΓ³n de usar splash:send_keys para enviar eventos de teclado, tambiΓ©n es posible utilizar el mΓ©todo element:send_keys para presionar teclas en un elemento especΓ­fico. Por ejemplo:

local input = splash:select('.search-input')
input:send_keys('<Enter>')

En este caso, se selecciona el campo de entrada con la clase .search-input utilizando splash:select y se utiliza send_keys en ese elemento para simular la pulsaciΓ³n de la tecla Enter.

Esperar a elementos

En ocasiones, es necesario esperar a que aparezcan ciertos elementos en la pΓ‘gina antes de realizar acciones adicionales. Puedes utilizar el comando splash:wait para esperar a que un elemento especΓ­fico estΓ© presente en la pΓ‘gina. Por ejemplo:

local input = splash:select('.search-input')
splash:wait(input)

splash:wait('.elemento-esperado')

Este comando espera hasta que aparezca un elemento con la clase .elemento-esperado. Tambien se puede utilizar este metodo para esperar un timepo especifico. Por ejemplo 5 segundos:

splash:wait(5)

Capturar capturas de pantalla

Splash te permite capturar capturas de pantalla de la pΓ‘gina renderizada. Puedes usar el comando splash:png para obtener una imagen PNG de la pΓ‘gina. Por ejemplo:

local screenshot = splash:png()

Este comando captura una captura de pantalla de la pΓ‘gina actual y la guarda en la variable screenshot.

Modo incognito

El atributo splash.private_mode_enabled en Splash habilita el modo de navegaciΓ³n privada, que protege la privacidad al eliminar cookies, ignorar cachΓ©s y no almacenar el historial de navegaciΓ³n.

splash.private_mode_enabled = true

User-Agent

Cuando utilizas Splash, tienes varios mΓ©todos disponibles para cambiar el User-Agent en tus solicitudes. Los mΓ©todos mΓ‘s comunes son los siguientes:

set_user_agent

El mΓ©todo set_user_agent te permite establecer un User-Agent personalizado en el objeto splash. Puedes utilizarlo dentro del script Lua para cambiar el User-Agent de todas las solicitudes siguientes. AquΓ­ tienes un ejemplo:

function main(splash, args)
    splash:set_user_agent("Mi User-Agent Personalizado")

    -- Resto del cΓ³digo...
end

Al llamar a set_user_agent, especificas el User-Agent que deseas utilizar en tus solicitudes posteriores.

set_custom_headers

El mΓ©todo set_custom_headers te permite establecer encabezados personalizados en el objeto splash. Esto incluye la posibilidad de cambiar el User-Agent. Puedes utilizarlo dentro del script Lua para configurar los encabezados de las solicitudes. AquΓ­ tienes un ejemplo:

function main(splash, args)
    headers = {
        ["User-Agent"] = "Mi User-Agent Personalizado"
    }
    splash:set_custom_headers(headers)

    -- Resto del cΓ³digo...
end

Al llamar a set_custom_headers, proporcionas un diccionario de encabezados personalizados. Puedes incluir el User-Agent y otros encabezados segΓΊn tus necesidades.

on_request

El mΓ©todo on_request te permite manipular cada solicitud individualmente y modificar sus encabezados, incluido el User-Agent. Puedes utilizarlo para personalizar el User-Agent en solicitudes especΓ­ficas. AquΓ­ tienes un ejemplo:

function main(splash, args)
    splash:on_request(function(request)
        if string.find(request.url, "example.com") then
            request:set_header("User-Agent", "Mi User-Agent Personalizado")
        end
        return request
    end)

    -- Resto del cΓ³digo...
end

Al utilizar on_request, puedes verificar la URL de cada solicitud y establecer el User-Agent de manera selectiva en base a tus criterios.

Diferencias entre los mΓ©todos

  • set_user_agent y set_custom_headers son mΓ©todos generales para establecer el User-Agent en todas las solicitudes o en todas las solicitudes con encabezados personalizados. Son ΓΊtiles cuando deseas aplicar el mismo User-Agent a todas las solicitudes.

  • on_request te permite modificar las solicitudes individualmente y personalizar el User-Agent segΓΊn criterios especΓ­ficos. Es ΓΊtil cuando deseas tener un control mΓ‘s granular sobre el User-Agent en diferentes solicitudes.

En resumen, los mΓ©todos set_user_agent y set_custom_headers son adecuados para establecer un User-Agent global, mientras que on_request te brinda la flexibilidad de personalizar el User-Agent en solicitudes individuales. Puedes elegir el mΓ©todo que mejor se adapte a tus necesidades y escenarios de uso en particular.

scrapy-splash

Scrapy-Splash es una extensiΓ³n de Scrapy que permite el scrapeo de contenido dinΓ‘mico al interactuar con Splash, un renderizador web basado en navegador. Simplifica el proceso de renderizado de pΓ‘ginas web con JavaScript, lo que facilita el scrapeo de sitios que dependen de interacciones dinΓ‘micas.

Orden de funcionamiento

AquΓ­ estΓ‘ el orden de ejecuciΓ³n nuevamente:

  1. Scrapy envΓ­a una solicitud a Splash.

  2. El script Lua en Splash se ejecuta.

  3. Dentro del script Lua, se realizan las acciones, como hacer clic en botones o escribir texto.

  4. Splash procesa y renderiza la pΓ‘gina web, teniendo en cuenta las acciones realizadas en el script Lua.

  5. Splash devuelve el HTML renderizado junto con otros datos como respuesta a Scrapy.

  6. Scrapy recibe la respuesta de Splash y continΓΊa con el procesamiento de la respuesta en su propia lΓ³gica de scrapeo.

En resumen, las acciones que realices con scrapy-splash se ejecutan dentro del script Lua en Splash, entre el envΓ­o de la solicitud desde Scrapy y la devoluciΓ³n de la respuesta a Scrapy. Esto permite interactuar con la pΓ‘gina web antes de extraer los datos deseados.

Integrar Scrapy-splash

Para utilizar Scrapy Splash en nuestro proyecto, primero necesitamos instalar el descargador scrapy-splash:

pip install scrapy-splash

Luego, debemos agregar la configuraciΓ³n requerida de Splash en el archivo settings.py de nuestro proyecto Scrapy:

# settings.py

# Splash Server Endpoint
SPLASH_URL = 'http://localhost:8050'


# Enable Splash downloader middleware and change HttpCompressionMiddleware priority
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

# Enable Splash Deduplicate Args Filter
SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}

# Define the Splash DupeFilter
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

Usar splash desde scrapy

Como vamos a utilizar splash para hacer el request a la pagina web, es necesario reeescribir el emtodo start_requests y en vez de usar el metodo scrapy.Request tenemos que usar un metodo propio de la libreria splash-request que se llama SplashRequest:

import scrapy
from quotes_js_scraper.items import QuoteItem
from scrapy_splash import SplashRequest 

lua_script = """
function main(splash, args)
    assert(splash:go(args.url))

  while not splash:select('div.quote') do
    splash:wait(0.1)
    print('waiting...')
  end
  return {html=splash:html()}
end
"""

class QuotesSpider(scrapy.Spider):
    name = 'quotes'

    def start_requests(self):
        url = 'https://quotes.toscrape.com/scroll'
        yield SplashRequest(
            url, 
            callback=self.parse, 
            endpoint='execute', 
            args={'lua_source': lua_script}
            )

    def parse(self, response):
        print(response.body)

Last updated