scieee AI-readable full text Open interactive document viewer

Servicio de búsqueda automatizada de anuncios de viviendas de alquiler basado en Python, Telegram y SQL

León Silva, Óscar

Abstract

La automatización de procesos es uno de los aspectos clave dentro del mundo de la ingeniería. La gran evolución sufrida durante los últimos 20 años en el mundo ingenieril, especialmente en el apartado de las telecomunicaciones y la informática, ha permitido al ser humano descargarse de tareas monótonas y tediosas a través de la aplicación de esta característica, haciendo incluso estas tareas de una forma más veloz y eficaz. Nuestra sociedad, la española, sufre a día de hoy un grave problema en cuanto a la adquisición de la vivienda, especialmente la gente joven, a los cuales se nos resiste el hecho de poder independizarnos y comenzar nuestra vida adulta de forma completa. Actualmente vivimos una etapa en la que rebosa la especulación de la vivienda, y la idea de poder encontrar un alquiler en condiciones dignas puede ser un infierno. En la situación en la que nos encontramos, una vivienda que sale al mercado en forma de alquiler puede durar en el mismo a penas días, a veces horas incluso, debido a la enorme demanda que hay en comparación con la oferta existente. Es por ello que para lograr este objetivo, es clave ser especialmente rápido y constante, lo que conlleva pasar horas frente al ordenador cada día, consultando los anuncios de los principales portales de vivienda (Idealista, Fotocasa, …). Es ahí donde la característica de la automatización nombrada al comienzo, de forma que se libere al usuario del desempeño de esta tarea mediante la implementación de una herramienta la cual lleve a cabo el desarrollo de esta actividad . ¿Se imaginan escribir en un formulario tus preferencias para encontrar una vivienda y que un servidor escanease los distintos portales de forma periódica, compartiéndote en un chat o un correo anuncios acorde a tus parámetros? De eso trata esta memoria.

Full text

1 Trabajo de Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Servicio de búsqueda automatizada de anuncios de viviendas de alquiler basado en Python, Telegram y SQL Autor: Óscar León Silva Tutor: María Teresa Ariza Gómez Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2023 2 Trabajo de Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Servicio de búsqueda automatizada de anuncios de viviendas de alquiler basado en Python, Telegram y SQL Autor: Óscar León Silva Tutor: María Teresa Ariza Gómez Profesor Titular Dpto. Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2023 3 Trabajo de Fin de Grado: Servicio de búsqueda automatizada de anuncios de viviendas de alquiler basado en Python, Telegram y SQL Autor: Óscar León Silva Tutor: María Teresa Ariza Gómez El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores: Presidente: Vocal/es: Secretario: acuerdan otorgarle la calificación de: El Secretario del Tribunal Fecha: 4 Agradecimientos A mi padre, don Óscar León del Río, principal artífice de la persona que soy hoy día, tanto personal como profesionalmente. Si no fuese por él, no habría descubierto el maravilloso mundo de las telecomunicaciones, ni existiría por ende este documento. A mi pareja, doña Macarena Sánchez Gutiérrez, por creer siempre en mí, incluso cuando ni yo mismo lo hice. Como no, a mi hermana mayor, doña Miriam León Silva, por cuidarme en los buenos y sobre todo en los malos momentos. Ellos tres forman los pilares fundamentales de mi vida. También, a don Pablo López Vílchez, por ayudarme siempre en todo y hacer de esta etapa aún más bonita. Y por último, gracias a mí, por nunca rendirme, por pelear hasta el último minuto de cada examen, por cada hora y minuto de estudio que dediqué a aprender todo aquello que me ha hecho la persona que soy. Óscar León Silva Sevilla, 2023 5 Resumen La automatización de procesos es uno de los aspectos clave dentro del mundo de la ingeniería. La gran evolución sufrida durante los últimos 20 años en el mundo ingenieril, especialmente en el apartado de las telecomunicaciones y la informática, ha permitido al ser humano descargarse de tareas monótonas y tediosas a través de la aplicación de esta característica, haciendo incluso estas tareas de una forma más veloz y eficaz. Nuestra sociedad, la española, sufre a día de hoy un grave problema en cuanto a la adquisición de la vivienda, especialmente la gente joven, a los cuales se nos resiste el hecho de poder independizarnos y comenzar nuestra vida adulta de forma completa. Actualmente vivimos una etapa en la que rebosa la especulación de la vivienda, y la idea de poder encontrar un alquiler en condiciones dignas puede ser un infierno. En la situación en la que nos encontramos, una vivienda que sale al mercado en forma de alquiler puede durar en el mismo a penas días, a veces horas incluso, debido a la enorme demanda que hay en comparación con la oferta existente. Es por ello que para lograr este objetivo, es clave ser especialmente rápido y constante, lo que conlleva pasar horas frente al ordenador cada día, consultando los anuncios de los principales portales de vivienda (Idealista, Fotocasa, …). Es ahí donde la característica de la automatización nombrada al comienzo, de forma que se libere al usuario del desempeño de esta tarea mediante la implementación de una herramienta la cual lleve a cabo el desarrollo de esta actividad . ¿Se imaginan escribir en un formulario tus preferencias para encontrar una vivienda y que un servidor escanease los distintos portales de forma periódica, compartiéndote en un chat o un correo anuncios acorde a tus parámetros? De eso trata esta memoria. 6 Abstract Process automation is one of the key aspects in the world of engineering. The great evolution suffered during the last 20 years in the engineering world, especially in the area of telecommunications and computer science, has allowed the human being to unload of monotonous and tedious tasks through the application of this feature, making even these tasks in a faster and more efficient way. Our society, the Spanish, suffers today a serious problem regarding the acquisition of housing, especially young people, to whom the fact of being able to become independent and start our adult life in a complete way resists us. We are currently living in a period in which housing speculation is rampant, and the idea of being able to find a decent rental can be hell. In the situation in which we find ourselves, a house that comes on the market in the form of rent can last only days, sometimes even hours, due to the enormous demand that exists in comparison with the existing supply. That is why to achieve this goal, it is key to be especially fast and constant, which means spending hours in front of the computer every day, consulting the ads on the main housing portals (Idealista, Fotocasa, ...). It is there where the characteristic of automation named at the beginning, so that the user is freed from the performance of this task through the implementation of a tool which carries out the development of this activity. Can you imagine writing in a form your preferences to find a house and a server scanning the different portals periodically, sharing with you in a chat or email ads according to your parameters? That's what this memory is all about. 7 Índice Contenido Agradecimientos ..................................................................................................................................................................... 4 Resumen ............................................................................................................................................................................... 5 Abstract ................................................................................................................................................................................. 6 Índice .................................................................................................................................................................................... 7 Índice de figuras ..................................................................................................................................................................... 9 Índice de tablas .................................................................................................................................................................... 11 1 Introducción ........................................................................................................................................................... 12 1.1 Contexto ............................................................................................................................................. 12 1.2 Motivación .......................................................................................................................................... 12 1.3 Objetivos ............................................................................................................................................ 12 1.4 Antecedentes ..................................................................................................................................... 13 1.5 Descripción de la solución propuesta ................................................................................................ 13 1.6 Estructura del documento .................................................................................................................. 14 2 Tecnologías empleadas ............................................................................................................................................ 15 2.1 Web Scraping ..................................................................................................................................... 15 2.1.1 Web Scraping: ventajas e inconvenientes ......................................................................................... 15 2.1.2 Web Scraping: tecnologías actuales .................................................................................................. 16 2.2 Python ................................................................................................................................................ 18 2.2.1 Python: Requests-html ....................................................................................................................... 19 2.2.2 Python: BeautifulSoup ........................................................................................................................ 20 2.2.3 Python: Selenium ............................................................................................................................... 20 2.2.4 Python: Telebot .................................................................................................................................. 21 2.3 Google Chrome y Chromedriver ........................................................................................................ 22 2.4 Postgresql .......................................................................................................................................... 23 2.5 Github ................................................................................................................................................. 23 3 Arquitectura del software ........................................................................................................................................... 24 4 Implementación del servicio ........................................................................................................................................ 26 4.1 Estructura de directorios .................................................................................................................... 26 4.2 Base de datos .................................................................................................................................... 27 4.3 Aplicación Python: RentingScraper ................................................................................................... 29 4.3.1 Aplicación Python: scrapers ............................................................................................................... 29 4.3.2 Aplicación Python: telegramBot ......................................................................................................... 32 4.3.3 Aplicación Python: logger ................................................................................................................... 36 4.3.4 Aplicación Python: ejecutable del servicio ......................................................................................... 37 5 Despliegue del proyecto ............................................................................................................................................ 40 6 Demostración práctica .............................................................................................................................................. 42 7 Conclusiones .......................................................................................................................................................... 59 7.1 Revisión de cumplimiento de objetivos .............................................................................................. 59 7.2 Debilidades y fortalezas ..................................................................................................................... 60 7.3 Continuación del proyecto .................................................................................................................. 61 7.4 Conclusión final .................................................................................................................................. 61 Anexo: Instalación del servicio .................................................................................................................................................... 62 Bibliografía ............................................................................................................................................................................ 65 8 9 Índice de figuras Ilustración 1- Solución propuesta ............................................................................................................................. 14 Ilustración 2: Web Scraping ..................................................................................................................................... 15 Ilustración 3: Código de ejemplo para web estática ................................................................................................. 17 Ilustración 4: Ranking lenguajes de programación .................................................................................................. 18 Ilustración 5: Código de ejemplo requests-html ....................................................................................................... 19 Ilustración 6: Código de ejemplo beautifulsoup ....................................................................................................... 20 Ilustración 7: Código de ejemplo selenium .............................................................................................................. 21 Ilustración 8: Código de ejemplo telebot .................................................................................................................. 21 Ilustración 9: Creación Bot de Telegram .................................................................................................................. 22 Ilustración 10: Repositorio del proyecto .................................................................................................................. 23 Ilustración 11: Arquitectura clásica cliente-servidor ................................................................................................ 24 Ilustración 12: Arquitectura implementada .............................................................................................................. 24 Ilustración 13: Estructura de directorios ................................................................................................................... 26 Ilustración 14: Fichero subscriptions.sql .................................................................................................................. 27 Ilustración 15: Fichero searchResults.sql ................................................................................................................. 28 Ilustración 16: Fichero setUpDataBase.sh ............................................................................................................... 28 Ilustración 17: Función deleteAccentMarks ............................................................................................................. 29 Ilustración 18: Función calculateScore .................................................................................................................... 29 Ilustración 19: Función alreadyInDB ....................................................................................................................... 30 Ilustración 20: Función saveSearchResult ................................................................................................................ 30 Ilustración 21: Función filtersAdded ........................................................................................................................ 30 Ilustración 22: Función makeRequestHtml .............................................................................................................. 31 Ilustración 23: Función setUpAndRunBrowser ....................................................................................................... 31 Ilustración 24: Fichero help.md ................................................................................................................................ 32 Ilustración 25: Función send_welcome_message .................................................................................................... 33 Ilustración 26: Fichero aboutMe.md ........................................................................................................................ 33 Ilustración 27: Función send_default_message ........................................................................................................ 34 Ilustración 28: Creación de la instancia de Telebot .................................................................................................. 35 Ilustración 29: Diagrama de flujo checkParameters ................................................................................................. 35 Ilustración 30: Fichero myLogger.py ....................................................................................................................... 36 Ilustración 31: Clase RentingScraper ....................................................................................................................... 37 Ilustración 32: Método checkSubscriptionsStatus .................................................................................................... 37 Ilustración 33: Creación de la instancia de RentingScraper ..................................................................................... 38 Ilustración 34: Fichero rentingScraper.service ......................................................................................................... 38 Ilustración 35: Fichero installDependencies.sh ........................................................................................................ 39 Ilustración 36: Denegación de acceso a Milanuncios ............................................................................................... 40 Ilustración 37: Despliegue del servicio .................................................................................................................... 41 Ilustración 38: Acceso al bot RentingScraper .......................................................................................................... 42 Ilustración 39: Chat de RentingScraper .................................................................................................................... 43 Ilustración 40: Inicio de conversación con RentingScraper ..................................................................................... 43 Ilustración 41: Información inicial del logger .......................................................................................................... 44 Ilustración 42: Mensaje por defecto del bot ............................................................................................................. 45 Ilustración 43: Mensaje de ayuda del bot ................................................................................................................. 45 Ilustración 44: Mensaje aboutMe ............................................................................................................................. 46 Ilustración 45: Subscripciones iniciales en el sistema .............................................................................................. 47 Ilustración 46: Respuesta al comando initSearch ..................................................................................................... 48 Ilustración 47: Respuesta ante una subscripción exitosa .......................................................................................... 49 Ilustración 48: Respuesta del bot tras finalizar la búsqueda ..................................................................................... 49 16 • Testing: este concepto, proveniente del inglés, hace referencia a la realización de pruebas sobre una aplicación web. ¿Y qué relación tiene esto con el Web Scraping? Pues que al poder obtener elementos HTML de las páginas web e incluso poder interactuar con los mismos (por ejemplo, botones, inputs de texto, etc), esto permite la realización de pruebas sobre estos entornos de manera programática, lo cual lo convierte en una importante herramienta para el desarrollo de este tipo de aplicaciones. Aun así, como en cualquier aspecto de la vida, no todo son ventajas dentro de este proceso. Existen detalles los cuales hacen que la puesta en práctica de este proceso sea algo más indeseable de lo que comentábamos al principio. A continuación, se exponen algunas de estas: • Legalidad: aunque la práctica en sí del Web Scraping no es ilegal [2], la extracción o el propio tratamiento de la información de los datos que manejan sí pueden llegar a ser un problema, por lo que para cierto tipo de prácticas puede que la práctica de este proceso no sea lo más adecuado, aunque como hemos dicho la práctica en sí del mismo no es ilegal, siempre que cumplamos con los derechos de autor, no realicemos competencia desleal, y cumplamos con la Ley Orgánica de Protección de Datos (LOPD). • Bot detection: como es comprensible, la inmensa mayoría de los administradores de portales web existentes no desean que su sitio web sea inundado por peticiones realizadas por ordenadores en lugar de personas, y teniendo en cuenta que el porcentaje de tráfico que circula por la red es mayoritariamente generado por bots [3], es un aspecto que suele estar bastante vigilado, por lo que el uso del Web Scraping a veces puede estar parcial o incluso totalmente limitado por los propios servidores web. 2.1.2 Web Scraping: tecnologías actuales Debido a su amplia utilidad y sumado además a la tendencia de los últimos años de la automatización de tareas monótonas (como puede ser la búsqueda de información, que es el caso de este proyecto), el Web Scraping es un recurso que se ha ido mejorando e integrando con herramientas como pueden ser Python o Javascript, los cuales son ambos los lenguajes de programación más usados de la actualidad. Antes de pasar a comentar las distintas tecnologías utilizadas a día de hoy para la práctica del Web Scraping, es necesario hacer un inciso en el cuál pongamos en situación al lector de los diferentes tipos de páginas web que existen a día de hoy, ya que en función de esta, la práctica a llevar a cabo es diferente. Bien, a continuación, se exponen los distintos tipos de páginas web existentes: • Páginas web estáticas: en este tipo de páginas, el cliente recibe el contenido HTML de la página web al completo en la respuesta de la petición, por lo que no precisa de ninguna comunicación adicional con el servidor para obtener más información mientras permanezca en esa página. • Páginas web dinámicas: a diferencia del anterior, en este tipo de páginas, el cliente recibe el contenido de la página web de forma parcial, obteniéndose el resto de forma dinámica a través de peticiones asíncronas con el servidor (estas peticiones son conocidas como peticiones AJAX). Bien, dicho esto, pasamos a mostrar las diferentes herramientas existentes, centrándonos exclusivamente en los lenguajes de programación Python y Javascript, ya que ambos son los más empleados para esta tarea [4]. Tanto para Python como para Javascript, existen distintas librerías utilizadas para el Web Scraping. Todas ellas se pueden agrupar en 2 grupos distintos, según el tipo de página web de la cual se desean extraer los datos, los cuales son los siguientes: 17 • Web Scraping para contenido estático: en este caso, el procedimiento es similar para ambos lenguajes de programación, además de ser bastante simple. Primeramente, se procede a realizar una petición HTML a un servidor, este último nos devolverá el contenido en la respuesta. Para lograr esto, en Python existen librerías como requests o requests-html, ambas nos permiten realizar peticiones HTML añadiendo las cabeceras necesarias. En cuanto al caso de Javascript, tenemos disponibles herramientas como fetch api o axios. Una vez hemos obtenido la respuesta, tenemos que ‘parsear’ (concepto utilizado en la programación, el cual se refiere al análisis de unos datos) los datos de la misma para poder proceder con el Web Scraping. Para ello, en Python disponemos de la librería bs4, o más conocida como BeautifulSoup, y para Javascript se dispone de DOMParser. Finalmente, ya podríamos proceder a la extracción de los datos del documento HTML, por ejemplo, en Python, si quisiéramos obtener todos los elementos etiquetados como párrafo en HTML, bastaría con hacer uso de la función find_all, perteneciente a la librería anteriormente mencionada, bs4. A continuación, en la Ilustración 3 se expone un sencillo código de ejemplo utilizando Python: Ilustración 3: Código de ejemplo para web estática • Web Scraping para contenido dinámico: este segundo caso es algo más complejo, debido a que precisamos del renderizado del código Javascript incluido en la página web. Para ello, necesitamos hacer uso de un navegador, que son los entornos donde se ejecuta este código de aplicación, pero claro, un navegador por sí solo implica muchos recursos y puede ser que esto nos suponga un problema para el desarrollo de nuestra actividad. En ese caso, para minimizar el uso de recursos, existe la posibilidad de ejecutar el navegador en modo headless [5], es decir, sin interfaz gráfica, aunque cabe decir también que la visita de algunos sitios web haciendo uso de este recurso puede dar problemas (carga de contenido HTML de forma incompleta, detección de este recurso provocando un bloqueo del mismo, etc). Bien, dicho esto, tanto en Python como en Javascript existen librerías para el desempeño del Web Scraping en este tipo de páginas. En el caso de Python, tenemos disponible la librería Selenium, la cual nos permite hacer uso de un navegador de forma automatizada. En cuanto a Javascript, se dispone de Puppeteer, que es una API de alto nivel la cual nos permite manejar Google Chrome de forma automatizada. Finalizando con la sección referente al Web Scraping, cabe decir que varios de los conceptos mencionados en esta subsección serán explicados en mayor detalle, como pueden ser las librerías de Python referenciados, el uso de navegadores en modo headless, etc. 18 2.2 Python El lenguaje de programación Python [6] es un lenguaje de alto nivel multiparadigma. Este lenguaje es interpretado por el intérprete, valga la redundancia, el cual va ejecutando cada instrucción del código de aplicación línea a línea, de manera que si existiera algún tipo de error por inconsistencias en el propio código, se produciría lo que se conoce como error en tiempo de ejecución. Esta herramienta de la que hablamos es uno de los lenguajes más utilizados actualmente por la comunidad de desarrolladores. Este último aspecto que comentamos se puede apreciar en la Ilustración 4, la cual muestra una gráfica (fuente: https://spectrum.ieee.org/top-programming-languages-2022) de los lenguajes de programación más utilizados en el año 2022: Ilustración 4: Ranking lenguajes de programación 19 Bien dicho esto, ¿qué nivel de aplicabilidad tiene Python para el Web Scraping? ¿por qué utilizar Python para este proyecto y no otro lenguaje? Como se ha mencionado al comienzo de este apartado, Python es un lenguaje multiparadigma, lo que hace que este sea una herramienta altamente versátil y pueda utilizarse para infinidad de tareas [7] (data analysis, machine learning, programación web, etc). Respondiendo a las preguntas anteriores, se debe decir que el motivo está ampliamente relacionado con la información mostrada en la Ilustración 4, y es que debido a su gran uso y a su naturaleza, existen una gran cantidad de librerías ya implementadas en Python para el Web Scraping, las cuales hacen que sea una de las opciones favoritas frente a otras alternativas como pueden ser Java o Javascript. Además, otra de las razones de su aplicación dentro de este proyecto es la compatibilidad con otras herramientas, como puede ser Telegram por ejemplo, otra de las herramientas que usaremos en este proyecto. 2.2.1 Python: Requests-html Requests-html es una de las librerías de Python [8] la cual nos permite realizar peticiones a una URL dada, de forma que podamos obtener el contenido de una página web. Se debe decir que otra de las librerías igualmente usadas para este fin es la librería Requests [9], aunque ambas presentan una gran diferencia, y es que la librería Requests solo permite interactuar con páginas web estáticas (es decir, no es capaz de renderizar el código javascript incluido en la página), mientras que Requests-html puede trabajar tanto con páginas web estáticas como dinámicas, ya que esta última integra el uso del navegador Chromium en modo headless para ello, pudiendo hacer uso de él para renderizar el contenido dinámico de la página que queremos visualizar. En la Ilustración 5 se puede observar un pequeño y sencillo fragmento de código el cual muestra el funcionamiento de esta librería: Ilustración 5: Código de ejemplo requests-html Como aclaración, debemos decir que en el código anterior realizaría las siguientes acciones: 1) Crear una instancia de la clase HTMLSession, la cual se encarga de abrir el navegador Chromium en modo headless. 2) Hacer una petición HTTP GET a la URL guardada en la variable url. 3) Ejecutar el código Javascript incluido en el propio contenido HTML de la página obtenida en la respuesta una vez hayan transcurrido 20 segundos desde la lectura de esta instrucción por parte del intérprete, además de realizar un desplazamiento hacia debajo de 256 píxeles (pudiendo cargar así contenido asociado al desplazamiento de la propia página). 4) Mostrar por pantalla el contenido HTML obtenido de la página en texto plano. 20 2.2.2 Python: BeautifulSoup La librería BeautifulSoup es una de las herramientas más populares de Python [10] para el desarrollo del Web Scraping, ya que esta nos permite realizar el ‘parseo’ (concepto anteriormente visto) del contenido de una página web obtenida en texto plano a formato HTML, permitiéndonos así poder proceder a la manipulación de los elementos contenidos dentro de la propia página. A continuación, en la Ilustración 6 se muestra un breve código de ejemplo haciendo uso de esta librería: Ilustración 6: Código de ejemplo beautifulsoup Del código de ejemplo mostrado en la Ilustración 6, podemos decir lo siguiente: 1) Tras obtener la página HTML perteneciente a la URL mostrada en la imagen y renderizar el código de Javascript incluido en la propia página, se realiza el ‘parseo’ del contenido en texto plano al formato HTML (línea 9). 2) Una vez hecho, se obtienen todos los elementos de la etiqueta a de HTML contenidos en la página. 3) Por último, se imprime por pantalla el valor del atributo href de cada una de las etiquetas a incluidas en la página. Como se puede apreciar, la acción que se está llevando en el código anterior es un proceso sencillo de Web Scraping. Antes de finalizar con este apartado, es preciso mencionar que tanto la librería anteriormente vista, Request-html, como la librería Selenium (la cual veremos a continuación), ambas permiten la manipulación de elementos HTML, al igual que la librería BeautifulSoup, pero debido a que esta última es de uso específico para esta tarea, su amplia documentación y su extensión entre la comunidad de desarrolladores, se ha preferido su uso por delante de las dos anteriores. 2.2.3 Python: Selenium Selenium es una librería de Python [11] la cual permite el uso automatizado del navegador a través de un driver (se verá más en detalle en un apartado posterior). Esta librería es una gran herramienta y se encuentra muy extendida debido a su versatilidad, además de tener una documentación bastante extensa debido a su uso. Los motivos de la popularidad de esta librería son las funcionalidades que ofrece, ya que esta nos permite llevar a cabo dos tareas muy destacadas como son el Testing y el Web Scraping (ambas comentadas en el capítulo de Introducción de esta memoria). Cabe mencionar que por motivos relacionados con el Bot detecting, han surgido variantes de esta librería como pueden ser Selenium-stealth [12] o Undetected-chromedriver [13], ambas tienen como objetivo eludir los mecanismos que los servidores web utilizan para bloquear el acceso a su sitio web a usuarios no deseados como son los propios Bots. 21 Como venimos haciendo en los apartados anteriores, para comprender de forma superficial el funcionamiento de la librería, hacemos uso de un ejemplo en sí. En la Ilustración 7 de a continuación, se muestra un breve ejemplo del uso de esta librería: Ilustración 7: Código de ejemplo selenium Se puede apreciar con facilidad que este ejemplo es algo más complejo que los anteriores. La razón de ello es que Selenium nos permite una amplia configuración del navegador (líneas 6 a la 17 en el ejemplo). En este ejemplo concretamente, podemos apreciar cómo se hace uso del navegador en modo headless (línea 10). El resto de las opciones de configuración serán comentadas más adelante, concretamente en el capítulo de Implementación. 2.2.4 Python: Telebot La librería Telebot contiene las herramientas necesarias para poder gestionar un Bot de Telegram [14] [15] ya previamente creado, pudiendo definir las acciones que este mismo realice ante determinados inputs que han sido contemplados en el diseño del Bot. Para ello, se hace uso de los decoradores [16] de Python, los cuales nos permiten definir y/o ampliar el comportamiento de una función. En la Ilustración 8 se muestra un pequeño fragmento de código, el cual asocia un comando del Bot que estamos gestionando a esa función a través del decorador message_handler definido en la propia librería de Telebot: Ilustración 8: Código de ejemplo telebot Definido este comportamiento, cuando lancemos la ejecución del Bot, tendremos que este responderá al cliente con el mensaje “¡Hola!” ante el envío del comando hello. De esta manera, la librería principalmente nos permite asociar funciones a eventos, lo que se conoce como Application Programming Interface (API) en el campo de la programación. 22 Bien, hemos introducido de forma breve la librería de Telebot, pero no se ha comentado hasta ahora como se lleva a cabo la creación de un Bot en sí [17]. Para ello, se hace uso de otro Bot de Telegram, el cual se llama BotFather. Este nos permite gestionar nuestros Bots, de forma que podemos crear uno nuevo, eliminar uno anterior y modificar parámetros de uno ya existente (como la descripción del Bot, su nombre, su foto de perfil, etc). En la Ilustración 9 se muestra un fragmento de la conversación con BotFather para la creación del Bot que ha sido utilizado en este proyecto: Ilustración 9: Creación Bot de Telegram En la captura se puede apreciar cómo le indicamos al Bot a través del comando newbot (en Telegram los comandos se inician con el carácter ‘/’) que queremos crear un nuevo Bot. Este nos responde pidiéndonos el nombre y el username que queremos que tenga nuestro Bot. Una vez estos datos han sido facilitados, este nos indica que la operación se ha llevado a cabo con éxito y nos devuelve un token, que no es más que un código identificativo de nuestro Bot, con el cuál podremos acceder a él programáticamente a través del uso de la librería Telebot. 2.3 Google Chrome y Chromedriver Una vez vistos todos los principales recursos de Python que han sido utilizados en el proyecto (en especial la librería Selenium), podemos pasar a detallar otra de las herramientas utilizadas en el proyecto, como lo es Google Chrome y a su vez Chromedriver. Google Chrome es uno de los navegadores web más extendidos en la actualidad. Este cuenta con una gran cantidad de extensiones que permiten mejorar su funcionamiento, las cuales pueden ser encontradas en el propio market del navegador. Otra de las características de este navegador (como no puede ser de otra manera debido a su gran extensión) es su integración con otras herramientas, como por ejemplo Python, a través del uso de la librería Selenium. Aunque existen otros navegadores webs ampliamente extendidos en el mercado que también son compatibles con Selenium, como es el caso de Firefox, este no permite el mismo nivel de configurabilidad que Chrome. Este es el principal motivo por el cual se ha decidido el uso de Google Chrome en lugar de Firefox. Bien, se ha explicado qué es Google Chrome y el motivo de su uso frente a otras alternativas, pero ¿qué es Chromedriver? Esta no es más que una herramienta open-source [18] en la que se basa Selenium para la automatización del navegador Google Chrome. Sin ella no sería posible hacer uso del navegador de forma programática. En cuanto a Firefox, existe su análogo llamado Geckodriver [19]. 23 2.4 Postgresql La persistencia de datos es uno de los puntos más comunes en cualquier proyecto en el que se realice tratamiento de datos, y este proyecto no iba a ser diferente debido a su naturaleza, ya que tendremos que registrar las diferentes subscripciones de cada uno de los usuarios, y los resultados obtenidos de cada uno de ellos. Por tanto, se ha tomado al servidor Postgresql como el ideal para este proyecto, por su amplia documentación y su asentamiento en el mercado. Postresql es un servidor de base de datos [20] open-source de tipo relacional el cual hace uso del lenguaje de consultas SQL. Dentro de sus características más destacadas [21] se encuentran la compatibilidad con el protocolo de seguridad SSL, el protocolo de red IPv6 y el control de acceso (a través de contraseñas, certificados, etc). En cuanto a su integración con otras herramientas como Python, existe una librería específica para ello llamada Psycopg2 [22], la cual ha sido utilizada también en este proyecto. 2.5 Github Uno de los aspectos más importantes dentro del proceso software, es el control de versiones. La puesta en práctica de este tipo de herramientas es fundamental durante toda la etapa de desarrollo del sistema, ya que esta nos permite trabajar de forma más eficiente, disponer de trazabilidad del sistema, corregir bugs que puedan producirse, y un largo etc. Github es una de las plataformas de control de versiones de software más extendidas en la actualidad. Esta nos permite disponer de un repositorio remoto (o los que se necesiten) en el cual podemos almacenar nuestra aplicación de forma segura, llevando un control de la misma a través de las distintas herramientas que Git nos ofrece (creación de ramas, commits, logs, etc). En la Ilustración 10 se muestra una captura del repositorio remoto creado en Github para el seguimiento de este proyecto: Ilustración 10: Repositorio del proyecto En la captura anterior se puede apreciar ligeramente la estructura de directorios seguida para el software. Esta será comentada más adelante, concretamente en el capítulo Arquitectura del software. 24 3 Arquitectura del software En este tercer capítulo se pretende plasmar al lector la estructura seguida para la implementación del servicio al que hemos denotado como ‘RentingScraper’. Para ello, nos hemos basado en una arquitectura clásica Cliente-Servidor, delegando la parte cliente en los servidores de Telegram mediante el uso de un Bot gestionado a través de Python. En la Ilustración 11 se muestra un simple esquema de la arquitectura Cliente-Servidor, en la cual solo interactúan estos dos únicos equipos en cuanto a lo que a nivel de aplicación se refiere: Ilustración 11: Arquitectura clásica cliente-servidor Continuando con la explicación de nuestra arquitectura en concreto, tenemos un pequeño detalle diferenciador con respecto al esquema anterior, y es que al hacer uso de los servidores de Telegram como se ha comentado previamente, estos actúan como proxy, encaminando los mensajes del cliente hacia nuestro servidor, donde finalmente serán procesados de forma completa. Teniendo esto en cuenta, y continuando con el esquema de la Ilustración 1, dando un poco más de detalle a este último, nos quedaría una arquitectura como la que se muestra en la Ilustración 12: Ilustración 12: Arquitectura implementada Como podemos apreciar en el diagrama anterior, siempre que el cliente quiera realizar algún tipo de transacción con nuestro servidor tendrá que acceder previamente por Telegram. La desventaja de ello es que si por casual los servidores de esta aplicación sufrieran algún tipo de incidencia la cual imposibilitara su uso o estos se encontrasen inaccesibles debido a un problema de encaminamiento en la red, nuestra aplicación dejaría de prestar servicio independientemente de su correcto funcionamiento. Este aspecto que comentamos evidentemente es muy negativo, ya que nosotros no tenemos control sobre servidores externos, pero es el coste que tenemos que asumir al querer delegar la parte del cliente en un servidor ajeno al nuestro, en lugar de implementarlo nosotros mismos. Esto nos permitiría conseguir un ahorro temporal considerable, el cual podríamos invertir en un mejor desarrollo de la parte servidor. 25 En cuanto a lo que al servidor se refiere, este contendría un sistema operativo Ubuntu 22.04, el cual contaría con los siguientes recursos: • Python 3.8.10 o superior: para poder ejecutar la aplicación Python desarrollada, incluyendo además las librerías comentadas en la sección 2 del capítulo Tecnologías empleadas (además de otras librerías de menor relevancia, pero también necesarias, como por ejemplo la librería regex). • PostgreSQL 12 o superior: para almacenar los datos relevantes para el correcto funcionamiento de nuestro servicio (como por ejemplo las suscripciones vigentes en el sistema). • Google Chrome 112: para poder acceder a los distintos portales web para extraer la información requerida. • Chromedriver 112: para poder controlar el navegador Google Chrome programáticamente. Además, cabe añadir que el servicio desarrollado, será gestionado por la herramienta Systemd [22] (incluida en cualquier distribución de Linux actualmente). Esta no es más que una herramienta de gestión del sistema, la cual nos permite manejar recursos del mismo, en especial los servicios, de forma que podamos llevar un control de ellos a través del uso de ficheros de registro, también conocidos como logs. 32 o milanuncios.py: este fichero es similar al anterior, contiene una función para la generación de la URL deseada, llamada createMilanunciosURL, y otra función para la extracción de los datos, llamada getDataFromMilanuncios. El procedimiento para ambas funciones es muy similar a los utilizados en el fichero fotocasa.py, pero con una pequeña peculiaridad en cuanto a la extracción de los datos. La cuestión es que el servidor web Milanuncios, tras varios accesos haciendo uso del navegador automatizado mediante Selenium terminaba bloqueándonos el acceso, y esta al ser una web dinámica no podía ser accedida a través de otros métodos, por lo que se optó por la siguiente solución: 1. Realizar una petición HTTP GET al servidor con la petición obtenida de la función createMilanunciosURL haciendo uso de la función makeRequestHtml, obteniendo así el HTML inicial con el código Javascript incluido. 2. Guardar el contenido HTML de la respuesta localmente en el directorio temp, el cual fue mencionado al comienzo de este capítulo. 3. Abrir el archivo HTML almacenado en el servidor con el navegador en modo headless haciendo uso de Selenium (con la función setUpAndRunBrowser). 4. Cargar el contenido dinámico de la página con ayuda del navegador, realizando un desplazamiento vertical hacia abajo. 5. Proceder a la extracción de los datos y persistir los que sean necesarios. o pisos.py: este fichero contiene las funciones necesarias para la extracción de los datos de los anuncios encontrados en el portal pisos.com. En él, podemos encontrar las funciones createPisosURL, la cual tiene exactamente el mismo objetivo que createFotocasaURL, pero teniendo en cuenta las peculiaridades de este servidor web, y por último, la función getDataFromPisos, la cual es muy similar a la función getDataFromFotocasa en lo que al proceso se refiere, con la peculiaridad de que el portal web Pisos.com es de contenido estático, por lo que para obtener el HTML se hace uso de la función makeRequestHtml. o yaencontre.py: este último fichero del directorio scrapers, contiene los recursos necesarios para la extracción de los datos de los anuncios encontrados en el portal yaencontre.com. En él, podemos encontrar dos funciones, las cuales son createYaencontreURL, que se encarga de generar la URL apropiada en función de las preferencias del usuario, y getDataFromYaencontre, que se encarga de extraer los datos de los anuncios encontrados en dicho portal web siguiendo exactamente el mismo proceso que en la función getDataFromPisos (ya que ambos portales usan solamente contenido estático). 4.3.2 Aplicación Python: telegramBot En este apartado se detallará el contenido del directorio telegramBot, el cual contiene el código fuente encargado de gestionar el bot de nuestro servicio RentingScraper. A continuación, se explica cada uno de los distintos ficheros contenidos en dicho directorio: o resources: este subdirectorio contiene mensajes predefinidos por el desarrollador encapsulados en ficheros de extensión markdown, los cuales son utilizados por el bot para responder a diferentes peticiones del usuario. En la Ilustración 24 se muestra como ejemplo el contenido del fichero help.md, utilizado por el bot cuando el usuario solicita ayuda: Ilustración 24: Fichero help.md 33 o rentingScraperBot.py: contiene el conjunto de funciones utilizadas para gestionar las peticiones del usuario, las cuales son las siguientes: ▪ send_welcome_message: esta función es utilizada por el bot cuando este recibe los comandos hello o start por parte del cliente. En ella se hace uso del fichero welcome.md contenido en el directorio resources para responder al usuario. En la Ilustración 25 se muestra el contenido de dicha función: Ilustración 25: Función send_welcome_message Como se puede observar, la función se asocia a los comandos indicados anteriormente mediante el uso del decorador message_handler. ▪ send_help_message: esta función es muy similar a la anterior, difiriendo solamente en el comando al que se encuentra asociada y el contenido del mensaje de respuesta al usuario. En este caso esta función se encuentra asociada al comando help, y ante este responderá con el contenido del fichero help.md, recogido en la carpeta de resources. ▪ send_aboutMe_message: también muy similar a las dos anteriores, esta se ejecuta ante la recepción del comando aboutMe, y responde al usuario con el contenido del fichero aboutMe.md, el cual podemos observar a continuación en la Ilustración 26: Ilustración 26: Fichero aboutMe.md ▪ initSearch: esta función, la cual se encuentra asociada al comando initSearch, es la utilizada para registrar una nueva subscripción de búsqueda en el servicio. Para ello, primeramente, el bot responde ante este comando con tres mensajes distintos, los cuales son meramente informativos, con el fin de dar a conocer al usuario el método de subscripción del usuario. Más tarde, una vez el usuario ha comprendido el método de registro (el cual se basa en cumplimentar un formulario con unas determinadas características), y ha completado el registro de forma adecuada, la subscripción es gestionada por el propio servicio, registrándola en la base de datos, y realizando una primera búsqueda, y devolviendo posteriormente al usuario los distintos resultados encontrados. ▪ getCurrentSearchs: esta función es ejecutada por el servicio ante la recepción del comando getCurrentSearchs. Su finalidad no es otra que devolver al usuario las diferentes subscripciones que tiene actualmente activas. Para ello, se realiza una consulta a la base de datos, obteniendo los distintos subscriptionId (campo mencionado anteriormente en el apartado 4.2) que existen para ese usuario, y devolviéndoselos a este último. ▪ getDataFromSubWithId: asociada al comando getDataFromSearchWithId, esta función devuelve los parámetros de búsqueda asociados a una subscripción del propio usuario. Para ello, el bot le solicita al usuario el subscriptionId (el cual puede ser obtenido a través del comando getCurrentSearchs), y una vez dado, el bot responde al usuario con los parámetros de dicha subscripción en caso de que esta última se encuentre recogida en el sistema. ▪ endSearch: esta función se encuentra asociada al comando endSearch, y tiene como objetivo eliminar una subscripción de un usuario en caso de que este último lo desee. 34 ▪ send_default_message: esta función es utilizada por el bot, en caso de que el mensaje recibido por parte del usuario no fuese uno de los contemplados por el sistema. A continuación, en la Ilustración 27 se muestra el contenido de dicha función: Ilustración 27: Función send_default_message Como se puede observar, haciendo uso del decorador message_handler y con ayuda de una expresión lambda (no es más que una pequeña función en Python de pequeño contenido), se asocia la ejecución de esta función al envío de cualquier mensaje no contemplado por el sistema. El resto de las funciones del fichero que se explican a continuación no están asociadas a ningún comando del bot como tal. Algunas de ellas son funciones que están asociadas a pasos intermedios de un comando, por ejemplo, en la función initSearch, se hace uso del método register_next_step_handler de la librería telebot, para indicar que el próximo mensaje recibido del cliente será manejado por la función processSubscription. El resto de las funciones son herramientas que el propio servicio utiliza para manejar el bot de acorde a las necesidades de la aplicación, como es el caso de la función refreshSubscription (se explicará a continuación). ▪ searchAnnounces: utilizada por el servicio para realizar la búsqueda de anuncios en los distintos portales web utilizados (Fotocasa, Milanuncios, Yaencontre y Pisos). Para ello se hace uso de las funciones explicadas en el apartado 4.3.1. ▪ refreshSubscription: esta función es la que usa el servicio para realizar una nueva búsqueda de una subscripción previamente registrada en el sistema la cual precisa ser actualizada. Para ello se apoya en la función anterior, searchAnnounces y en otra función showAnnounces (se explicará a continuación). ▪ processSubscription: esta función es la que utiliza el bot una vez ha recibido el formulario de subscripción por parte del cliente. Su lógica es la siguiente: 1. Verifica que los parámetros dados por el usuario son válidos mediante el uso de la función checkParameters (definida en el fichero auxFunctions.py). 2. En caso de que los parámetros sean válidos, se genera un nuevo subscriptionId (un hash de 32 bits) y se registra la subscripción en el sistema. 3. Si la subscripción ha sido almacenada correctamente en la base de datos, se procede a realizar la búsqueda de anuncios teniendo en cuenta los datos indicados por el usuario. 4. Una vez terminado el proceso de búsqueda, se indica al usuario el número de anuncios que han sido encontrados, además de las distintas opciones de visualización de estos (se comentará más en detalle en el capítulo siguiente). ▪ showAnnounces: esta función tiene como finalidad mostrar los anuncios encontrados al usuario, teniendo en cuenta la opción de visualización que este haya indicado. Para ello, hace una consulta a la base de datos solicitando los anuncios que correspondan, filtrando por las preferencias de visualización y por la fecha en la que fueron encontrados, escogiendo los de fecha y hora más reciente. ▪ getParametersFromSubscription: esta función es la que se ejecuta una vez el usuario ha indicado el subscriptionId de la subscripción que quiere consultar, habiendo ejecutado previamente el comando getDataFromSearchWithId. Al ejecutar la función, se realiza una consulta a la base de datos, solicitando los parámetros de búsqueda registrados para la subscripción que el usuario indicó, y una vez obtenidos, los devuelve al usuario a través del chat. ▪ ensureBeforeDeleteSubscription: utilizada por el bot para preguntar al usuario si realmente desea eliminar la subscripción que indicó tras llamar al comando endSearch. Para ello, verifica si existe una subscripción registrada en el sistema con el subscriptionId indicado por el usuario, en caso afirmativo le pregunta al usuario si está seguro de eliminar la subscripción. 35 ▪ confirmUnsubscription: utilizada por el bot para eliminar una subscripción del sistema una vez el usuario ha asegurado que desea finalizar la subscripción con el identificador que indicó previamente. Cabe mencionar que además de dichas funciones, en este fichero Python podemos observar la creación de una instancia de la clase Telebot de la propia libería telebot de Python. Esta instancia se guarda en una variable de ámbito global, con el fin de que pueda ser manejado posteriormente por el propio ejecutable del servicio (el cual será explicado más adelante). A continuación, se expone en la Ilustración 28 lo anteriormente mencionado: Ilustración 28: Creación de la instancia de Telebot Como podemos observar, para la creación de la instancia hacemos uso del token que obtuvimos durante la creación del bot mediante el chat con BotFather. o auxFunctions.py: este fichero de Python contiene un conjunto de funciones auxiliares utilizadas por el módulo anteriormente explicado rentingScraperBot.py. Las funciones en cuestión son las siguientes: ▪ deleteAccentMarks: esta función es exactamente la misma que la del módulo common_resrc.py del directorio scrapers. Su objetivo es eliminar caracteres acentuados del formulario dado por el usuario, con el fin de evitar errores futuros. ▪ createRegex: esta función se encarga de generar una expresión regular para la extracción de cada uno de los campos del formulario de registro. ▪ checkParameters: su objetivo es verificar si el formulario fue cumplimentado correctamente por el usuario. Para ello, se apoya en la función anterior, createRegex, para extraer los datos de cada uno de los campos del formulario. El siguiente diagrama de flujo de la Ilustración 29 muestra la lógica utilizada en esta función para cada uno de los campos: Ilustración 29: Diagrama de flujo checkParameters 36 En caso de que se detectase algún tipo de error en alguno de los campos, este se notificaría al usuario a través del chat. ▪ checkIfSubscriptionExists: comprueba si el usuario en cuestión tiene una subscripción vigente con los mismos parámetros que indicó en el formulario, de manera que no existan duplicidades. ▪ saveSubscription: se encarga de almacenar una subscripción en la base de datos del sistema. ▪ deleteSubscription: esta función es utilizada por el bot para eliminar una subscripción de la base de datos del sistema. o validData.py: este fichero de Python contiene dos listas constantes las cuales son utilizadas por el bot para verificar si los parámetros ‘Provincia y ‘Tipo de vivienda’ indicados por el usuario son válidos o no. A continuación, se exponen las dos constantes recogidas en este fichero: ▪ cities: esta lista de Python almacena en cadenas de texto las distintas provincias de la nación española, de forma que si el usuario indica una provincia que no se encuentra en esta lista, se detenga el procesado de la subscripción, reportando al usuario el error producido. ▪ houseTypes: esta otra lista contiene los distintos posibles valores para el parámetro ‘tipo de vivienda’, de manera que si el usuario indica un valor que no se encuentra recogido en esta lista, se detenga el procesado de la subscripción, reportando al usuario que el valor dado para ese parámetro no es válido. 4.3.3 Aplicación Python: logger En este apartado se mostrará el contenido del directorio logger del proyecto. En él podemos encontrar el fichero myLogger.py, el cual contiene una instancia única de la clase Logger, de manera que todos los módulos que importen este fichero hagan uso del mismo logger, y no se creen diferentes instancias del mismo. A continuación, se muestra en la Ilustración 30 el contenido de dicho fichero: Ilustración 30: Fichero myLogger.py Como vemos, la única acción que se lleva a cabo es la creación de la instancia de la clase Logger. Esta se configura en el ejecutable del servicio, el cual será explicado en el apartado de a continuación. 37 4.3.4 Aplicación Python: ejecutable del servicio En este apartado se detallará el contenido del ejecutable del servicio, el cual será llamado por la herramienta systemd en el arranque del servicio. El archivo ejecutable, el cual se encuentra en el propio directorio src, contiene una clase llamada RentingScraper, que a su vez contiene dos métodos, ambos se explican a continuación: • __init__: es el método constructor de la clase. Este método es llamado al crear una nueva instancia de la clase RentingScraper. En la Ilustración 31 se muestra el contenido de dicho método: Ilustración 31: Clase RentingScraper Como podemos observar, en dicha función tenemos un bucle infinito, en el cual llamamos al método de la clase checkSubscriptionsStatus, reporta un mensaje en el logger y actualiza el estado del bot, llamando al método process_new_updates. Tras ello, se duerme la ejecución del proceso durante 5 segundos mediante la instrucción sleep. En cuanto al método checkSubscriptionsStatus, tenemos que este se encarga de verificar si existen subscripciones vigentes en el sistema que necesiten ser actualizadas o no, en cuyo caso, se realizará una nueva búsqueda para cada una de las subscripciones que lo precisen, compartiendo los resultados con el usuario una vez estos han sido correctamente obtenidos. Finalmente, tras compartir los resultados con el usuario, se actualiza el campo lastUpdate de dicha subscripción en la base de datos, asignando el instante en el que la subscripción fue actualizada por última vez. A continuación, se expone en la Ilustración 32 el código de aplicación del método en cuestión: Ilustración 32: Método checkSubscriptionsStatus 38 Por último, dentro del fichero ejecutable podemos observar la llamada a la construcción la única instancia de la clase RentingScraper, la cual se encargará de gestionar el servicio de forma completa, además de la configuración del logger creado en el fichero myLogger.py comentado en el apartado anterior. A continuación, se puede observar en la Ilustración 33 lo anteriormente mencionado: Ilustración 33: Creación de la instancia de RentingScraper Además del ejecutable del servicio, dentro del directorio src también podemos encontrar el fichero de extensión service, el cual debe ser copiado al directorio /etc/systemd/systemd del sistema para que la aplicación pueda ser gestionada por systemd. A continuación, en la Ilustración 34, podemos observar el contenido de dicho fichero: Ilustración 34: Fichero rentingScraper.service Del contenido de dicho fichero podemos destacar que el arranque de nuestro servicio es totalmente dependiente del servidor PostgreSQL. Esto se asigna a través de la directiva Requires, la cual implica las dependencias que el servicio necesita para su arranque. 39 Por último, se debe comentar que dentro de la carpeta raíz del proyecto podemos encontrar el shell script llamado installDependencies.sh el cual puede ser utilizado para instalar las librerías y los recursos necesarios para que el servicio pueda ser arrancado y operar de manera adecuada. A continuación, se expone en la Ilustración 35 el contenido de dicho fichero: Ilustración 35: Fichero installDependencies.sh Como se puede apreciar, es necesario que dicho fichero sea ejecutado como usuario root. Una vez ejecutado, este verificará si el software necesario se encuentra instalado en el sistema, instalándolo en caso negativo. Además, se instalarán las librerías de Python necesarias a través del administrador de paquetes de Python, PIP, solo en caso de que estas no se encuentren previamente instaladas. También se instalará la la versión de Google Chrome utilizada y su driver Chromedriver (manualmente modificado por el desarrollador como se comentó en el apartado 2.3). Por último, copiará el fichero rentingScraper.service en el directorio del sistema /etc/systemd/system/ para que este pueda ser gestionado por systemd. 40 5 Despliegue del proyecto Antes de detallar como se llevaría a cabo el despliegue de la aplicación, debe tenerse en cuenta que este proyecto ha sido desarrollado para proveer un servicio a nivel local, con objetivos experimentales, de manera que lo explicado en el capítulo 4 de esta memoria debería ser adaptado para un despliegue a gran escala para dar un servicio a nivel nacional. Teniendo en cuenta el comentario anterior, para que pudiéramos proveer un servicio de un nivel de calidad óptima al usuario deberíamos de tener un Data Center operativo con un significativo número de servidores, todos ellos replicas unos de otros, siendo estos conectados a un equipo Proxy el cual redirige la petición del usuario a uno de ellos, de forma que la carga del servicio se encuentre correctamente balanceada y no haya ningún equipo servidor saturado por peticiones de distintos usuarios. Además, la base de datos del sistema tendría que estar centralizada en un equipo externo a los que contienen el código de la aplicación, accesible a cada uno de los equipos servidores y por supuesto con sus respectivas replicas, de manera que si una falla, el servicio siga estando operativo. También deberíamos de contar con un conjunto importante de servidores Proxy con diferentes direcciones IP públicas utilizados para reencaminar las peticiones HTTP hacia los distintos portales web que utiliza el servicio para la extracción de los datos. La razón de esto es que si los servidores web que utilizamos para buscar la información detectan un acceso elevado [24] [25] a sus servidores [por parte de una misma dirección IP, estos podrían bloquearnos el servicio, impidiéndonos así el poder realizar nuestra actividad de manera normal. Este comportamiento que se menciona ha sido experimentado durante la etapa de desarrollo del proyecto, tras realizar un importante número de accesos a los distintos servidores web utilizados para el Web Scraping de los datos. En la Ilustración 36, puede apreciarse este aspecto de forma clara: Ilustración 36: Denegación de acceso a Milanuncios 41 Teniendo en cuenta todo lo anterior explicado en este apartado, la arquitectura para el despliegue de nuestro servicio para una escala a nivel nacional quedaría aproximadamente tal y como se muestra en la Ilustración 37: Ilustración 37: Despliegue del servicio El diagrama anterior sería una aproximación del escenario de despliegue del servicio en la realidad. Como podemos ver tendríamos nuestros equipos servidores y bases de datos en una subred privada de acceso restringido enmascarada por el router del operador el cual nos diese servicio. Esta red privada sería de acceso restringido mediante un firewall (el cual podría estar implementado en el mismo equipo que el proxy balanceador de carga) el cual permitiría el acceso desde el exterior solamente a las peticiones del cliente del servicio. En cuanto a las consultas que se hicieran a los distintos portales web para conseguir los datos requeridos por el cliente, haríamos uso del conjunto de proxies los cuales deberían de ser públicos desde el punto de vista de la red (es decir, cada uno de ellos con una dirección IP pública), pero con un acceso restringido de forma que solo pudieran ser utilizados por nosotros, permitiéndonos así balancear el tráfico para que los distintos servidores web que consultamos no nos denieguen el acceso. 48 Como se puede apreciar en la captura anterior, efectivamente no consta ninguna subscripción nuestra en el servicio. Para comenzar con una nueva subscripción, debemos hacer uso del comando initSearch, el cual el bot nos sugiere su uso tal y como accedemos al chat de este. Antes de comenzar, cabe mencionar que en la implementación actual, el servicio permite tener tantas subscripciones diferentes como se quieran para un mismo usuario. Bien, dicho esto podemos proceder con la subscripción. Si escribimos el comando mencionado, obtenemos como resultado el mostrado en la Ilustración 46: Ilustración 46: Respuesta al comando initSearch Como respuesta por parte del bot, obtenemos una serie de mensajes descriptivos los cuales nos indican la forma adecuada de proceder con el registro de la subscripción. El primer mensaje en cuestión nos indica que copiemos el último mensaje y lo editemos para cumplimentar el registro, resaltando que los 3 primeros campos son obligatorios. El segundo mensaje nos comenta que los campos no obligatorios podemos dejarlos sin completar, es decir, por defecto, si no tenemos interés en esos campos. El tercer mensaje nos indica como debemos proceder cumplimentando ciertos campos del formulario. En cuanto al cuarto mensaje, este nos recalca que cuanto más preciso seamos al dar la información, mejores resultados encontrará, por lo que debemos tenerlo en cuenta. Por último, tenemos el quinto y último mensaje, el cual se corresponde con el formulario de subscripción del servicio. Si cumplimentamos este último de manera adecuada, obtendremos un resultado similar al mostrado en la Ilustración 47: 49 Ilustración 47: Respuesta ante una subscripción exitosa Como vemos, tras cumplimentar el formulario correctamente, el bot nos indica que nuestra subscripción será procesada y en un plazo corto de tiempo obtendremos nuestros resultados. A continuación, en la Ilustración 48 podemos observar la respuesta obtenida una vez la búsqueda ha finalizado: Ilustración 48: Respuesta del bot tras finalizar la búsqueda 50 Como podemos observar, tras finalizar la búsqueda este nos indica el número de anuncios encontrados, y nos manda otro mensaje indicándonos las distintas opciones que tenemos para la visualización de los mismos. Como se observa, todas ellas son bastante explícitas y descriptivas. Profundizando un poco más en la opción 4, tenemos que esta opción nos despliega los 10 anuncios que el servicio ha considerado que son los de mejores prestaciones mediante el uso de la función calculateScore, detallada en el capítulo anterior. Si consultamos el lado del servidor, podemos observar que la información reportada en el log del servicio es la mostrada en la Ilustración 49: Ilustración 49: Salida del logger al registrar una nueva subscripción Como se puede apreciar en la captura anterior, el logger nos informa que una nueva subscripción se va a procesar y nos despliega el número de resultados obtenidos para cada uno de los portales web consultados. Si observamos el estado de la base de datos, obtenemos que el resultado de la tabla subscriptions es el mostrado en la Ilustración 50: Ilustración 50: Estado de la base de datos tras la subscripción 51 Como vemos la subscripción ha quedado correctamente recogida en el sistema, y por tanto esta será actualizada periódicamente cada 4 horas mientras el usuario no decida eliminarla. Si continuamos con la parte cliente del servicio, y seleccionamos una de las distintas opciones que tenemos disponibles, tenemos como resultado lo que se muestra en la Ilustración 51: Ilustración 51: Anuncios obtenidos tras la búsqueda 52 Tras seleccionar la opción número 4, tenemos que el bot nos manda en una lista descendiente los anuncios de mejores características encontrados en esta primera búsqueda. Como vemos, en cada uno de los mensajes aparece el título del anuncio, el precio de la vivienda, sus principales características (superficie, habitaciones y baños) y por último, el enlace del mismo, por si queremos consultar más información. Si accedemos al enlace del anuncio de mejores características según el servicio, por ejemplo, obtenemos la página mostrada en la Ilustración 52: Ilustración 52: Mejor resultado obtenido 53 Como se puede apreciar en la parte inferior de la captura, se observa que la vivienda cuenta con una gran cantidad de extras, como piscina, terraza, ascensor, etc. Además, si nos fijamos en las características de este y comprobamos con los filtros de búsqueda que introdujimos (Ilustración 47), vemos que el anuncio cumple todas las especificaciones que indicamos, por lo que tanto el filtrado de anuncios como la puntuación de los mismos parece que funciona como se espera. Volviendo al chat, si observamos el anuncio número 10 mostrado en la Ilustración 53, el cuál es el de peores características de los 10 dados según el servicio: Ilustración 53: Anuncios de peor puntuación según el servicio 54 Como vemos, a diferencia de los otros anuncios, este último pertenece al portal de Fotocasa, además, el chat nos renderiza la previsualización del enlace, permitiéndonos obtener así mucha más información. Si nos dirigimos al enlace adjuntado en el mensaje, tenemos como resultado lo mostrado en la Ilustración 54: Ilustración 54: Resultado de peor puntuación según el servicio Como podemos observar, aunque el precio de la vivienda es menor que el anterior que consultamos, esta brinda unas prestaciones menores que el anterior, puesto que no consta de terraza ni piscina, por lo que el servicio cumple con lo que se espera en cuanto a la puntuación de los anuncios encontrados. Volviendo al lado del servidor, si consultamos los resultados registrados en la base de datos para esta subscripción, enfocándonos en el apartado de la puntuación, se tiene como resultado lo mostrado en la Ilustración 55: Ilustración 55: Resultados obtenidos vistos desde la base de datos 55 Como vemos el resultado de la consulta se corresponde con los obtenidos en el propio chat. En la imagen, se puede apreciar que el anuncio de mejores características obtuvo una puntuación de 10.65, mientras que el de peores características según el servicio, obtuvo una puntuación de 7.83 aproximadamente. Retomando la parte cliente, si escribimos los comandos getCurrentSearchs y getDataFromSearchWithId respectivamente, obtenemos como resultado lo mostrado en la Ilustración 56: Ilustración 56: Obtención de los datos de una subscripción registrada Como se puede apreciar en la imagen anterior, los datos obtenidos son idénticos a los que fueron introducidos en el momento de la subscripción (Ilustración 47). 56 En cuanto a la actualización de cada una de las subscripciones, esta se realiza periódicamente en el servidor, de forma que si se encontrasen nuevos resultados, estos se compartirían con el usuario. En la Ilustración 57, puede observarse la información del logger cuando una (o varias) subscripción precisa ser actualizada: Ilustración 57: Información del logger al actualizar una subscripción Como vemos, el logger informa de que hay una subscripción pendiente de ser actualizada y por tanto, el servidor procede a la búsqueda de nuevos anuncios en los diferentes portales web usados para la consulta. Como se observa en las trazas de log impresas en la terminal, en ninguno de los portales web consultados existen nuevos anuncios, por tanto, no se comparte ningun tipo de información con el usuario a través del chat. En el caso de que se encontrasen nuevos anuncios, el usuario recibiría un mensaje similar al mostrado en la Ilustración 58: Ilustración 58: Mensaje de actualización de una subscripción vigente Como se puede observar, el bot nos mandaría 2 mensajes nuevos en caso de encontrar nuevos resultados para una subscripción nuestra, indicándonos en el primero de ellos el número de nuevos resultados, y el identificador de la subscripción al que pertenecen. En cuanto al segundo mensaje, este es exactamente el mismo que el de la Ilustración 48, de modo que podamos seleccionar los anuncios que deseamos observar de esta última búsqueda. En cuanto a la gestión de errores de la herramienta, esta implementa mensajes de error de carácter informativo, tanto en el logger de este como en el chat de Telegram, de manera que, si ocurriera algún tipo de error durante el servicio, tanto el administrador del mismo como el usuario, ambos serían notificados a través de estos. Centrándonos en el error más común y probable de suceder, siendo este la introducción de datos por parte del cliente, tenemos que si el servidor detecta que algún dato no fue correctamente dado por parte del usuario, este sería notificado a través del chat como se muestra en la Ilustración 59: 57 Ilustración 59: Campo obligatorio del formulario no cumplimentado En el ejemplo de la imagen anterior, el usuario ha tratado de realizar una nueva subscripción, pero en este caso no ha modificado los campos del formulario, por lo que la subscripción no se procesa. Por poner otro ejemplo, en la Ilustración 60 se muestra otro error al cumplimentar el formulario, dando un dato no válido para uno de los campos: Ilustración 60: Valor inválido para el campo 'rango de precio' 64 Finalmente, una vez hemos instalado todas las dependencias del servicio, solamente queda copiar el archivo rentingScraper.service (el cual podemos encontrar dentro del proyecto) en la ruta /etc/systemd/system, para que este pueda ser gestionado a través de la herramienta del sistema Systemd. Una vez hecho, tendremos que ejecutar el comando systemctl daemon-reload para que systemd pueda recargar su configuración y tenga en cuenta el nuevo fichero añadido. Una vez hecho, si ejecutamos el comando systemctl status rentingScraper, deberíamos obtener el resultado mostrado en la Ilustración 65: Ilustración 65: Estado del servicio una vez cargado con Systemd Por último, si quisiéramos habilitar el arranque del servicio junto con el arranque del sistema, podríamos hacer uso de los comandos systemctl enable postgresql y systemctl enable rentingScraper. Instalación automatizada Por razones obvias, el procedimiento seguido en el apartado Instalación manual ha sido automatizado en un script de bash, de forma que al ejecutarlo se instalen todas las dependencias requeridas del servicio, además de la copia de los archivos necesarios en las rutas indicadas (ficheros Chromedriver y retingScrpaer.service). Para ello, debemos ejecutar dicho script como usuario administrador del sistema. Dicha acción puede ser llevada a cabo a través del comando sudo ./installDependencies.sh, por ejemplo (otra opción sería ejecutar su – root -c ./installDependencies.sh). Por último, se expone a continuación en la Ilustración 66 el contenido de dicho script (aunque ya fue expuesto en la Ilustración 35): Ilustración 66: Contenido fichero installDependencies.sh 65 Bibliografía A continuación, se exponen los recursos utilizados a lo largo de todo el desarrollo de este proyecto, de manera que si el lector lo desea pueda profundizar en algún aspecto el cual haya llamado su atención: [ 1 ] https://www.mindbowser.com/web-scraping-advantages/ [ 2 ] https://datstrats.com/blog/scraping-es-legal-espana/ [ 3 ] https://www.techspot.com/news/98721-nearly-half-all-internet-traffic-2022-came-bots.html [ 4 ] https://blog.apify.com/web-scraping-javascript-vs-python-2022/ [ 5 ] https://es.wikipedia.org/wiki/Navegador_sin_interfaz_gr%C3%A1fica [ 6 ] https://www.python.org/doc/essays/blurb/ [ 7 ] https://www.coursera.org/articles/what-is-python-used-for-a-beginners-guide-to-using-python [ 8 ] https://pypi.org/project/requests-html/ [ 9 ] https://pypi.org/project/requests/ [ 10 ] https://pypi.org/project/beautifulsoup4/ [ 11 ] https://pypi.org/project/selenium/ [ 12 ] https://pypi.org/project/selenium-stealth/ [ 13 ] https://pypi.org/project/undetected-chromedriver/ [ 14 ] https://pypi.org/project/telebot/ [ 15 ] https://core.telegram.org/bots/api [ 16 ] https://builtin.com/software-engineering-perspectives/python-symbol [ 17 ] https://www.freecodecamp.org/news/how-to-create-a-telegram-bot-using-python/ [ 18 ] https://chromedriver.chromium.org/ [ 19 ] https://www.toolsqa.com/selenium-webdriver/selenium-geckodriver/ [ 20 ] https://www.postgresql.org/about/ [ 21 ] https://www.postgresql.org/about/featurematrix/ [ 22 ] https://pypi.org/project/psycopg2/ [ 23 ] https://www.digitalocean.com/community/tutorials/what-is-systemd [ 24 ] https://www.zenrows.com/blog/selenium-avoid-bot-detection#rotating-http-header-information-and-user-agent [ 25 ] https://stackoverflow.com/questions/33225947/can-a-website-detect-when-you-are-using-selenium-with- chromedriver