Recuperación y Acceso a la Información Implementación de un Crawler Básico Objetivo Creación de un crawler sencillo para obtener documentos de la Web (páginas HTML) y construir un corpus documental sobre el que funcionará el motor de búsqueda. Requisitos El crawler se desarrollará en C#, y se deberá implementar una librería con toda la funcionalidad. Esta librería será utilizada posteriormente por el motor de búsqueda. Las URLs que el crawler visita deberán almacenarse con un mecanismo de persistencia. Para ello, se utilizará una base de datos SQLite en la que almacenar toda la información relevante de crawling. Además, el contenido de las páginas deberá almacenarse en un directorio del disco duro. Recursos Se entrega una solución de Visual Studio 2005 con el código base, en el cual habrá que rellenar varios huecos. La solución contiene un proyecto llamado RAI.Crawler, que es la librería con toda la funcionalidad del crawler, y otro proyecto llamado Run, que se usará simplemente para instanciar un crawler e iniciarlo. Además, se incluye también la librería System.Data.SQLite, para trabajar con la base de datos desde el crawler. También, se entrega una pequeña herramienta con GUI para manipular manualmente el contenido de la base de datos. Estructura La estructura del crawler se muestra a continuación: 1 Recuperación y Acceso a la Información La clase Uri2 nos servirá como contenedor de las URLs que se visiten, conteniendo cierta información de control necesaria para el proceso. Cada instancia deberá corresponderse con una fila de la base de datos. Tanto las instancias como las filas de la base de datos contienen: Id: identificador de la URL (clave primaria en la tabla de la base de datos). Uri: URL correspondiente. Parent: identificador de la URL de la página que contenía el enlace a esta página. Las URLs semilla no tienen Parent. Level: nivel de la página en el correspondiente árbol de enlaces (siempre uno más que el padre). El nivel de las URL semilla será 1. Cache: path del archivo local que contiene el contenido de la página. Success: estado de la URL. Null si no se ha procesado, y true o false dependiendo de si se procesó bien o hubo algún error. La clase Uri2 provee 3 formas de construir instancias: CreateSeed, para crear URLs que se usarán como semilla en el proceso de crawling. CreateFromDatabase, para instanciar las URLs que se lean de la base de datos. CreateCustom, para crear URLs nuevas sacadas de los enlaces de alguna página. IUriProvider especifica las operaciones del mecanismo de persistencia a usar. Habrá que inicializarlo primeramente, y después se le podrán pedir URLs para procesar con GetUri(), se podrán añadir URLs nuevas con AddUri(Uri2) y se actualizarán después de ser procesadas con Update(Uri2). En nuestro caso, SQLiteUriProvider será la clase encargada de ofrecer persistencia con la base de datos. Contiene una conexión a la misma y tres comandos SQL, uno para cada operación. La clase Settings se usa para encapsular la configuración del crawler, que se guarda en un archivo XML. Contiene 3 parámetros: DBFile: path al archivo de la base de datos SQLite. DownloadAttempts: número máximo de intentos para bajar una página. MaxLevels: número máximo de niveles por los que navegar en el árbol de enlaces. La clase Crawler es la encargada de juntar todo lo anterior. Contiene una configuración en _settings, y un proveedor de URLs en _uriProvider. Deberá instanciarse con el path del archivo de configuración. Se podrá usar para añadir semillas con la operación AddSeed(string), y se iniciará el proceso de crawling con el método Start(). Además, internamente, ofrece la operación GetCacheFilename(), que se encargará de devolver un path único para guardar el contenido de la página correspondiente. La estructura local de archivos estará formada por 100 directorios, del 00 al 99. Dentro de cada uno, estarán los archivos correspondientes a las caches de las páginas. Cuando se pida un nombre de archivo con el método GetCacheFilename(), se escogerá uno de los directorios aleatoriamente y después un archivo con nombre único dentro del mismo. Partes a Completar Se deberán completar los tres constructores de Uri2. También la operación Update(Uri2) del SQLiteUriProvider. Dentro de la clase Crawler, debe inicializarse el _uriProvider en el constructor, e implementar las operaciones AddSeed(string), Start() y GetCacheFilename(). 2 Recuperación y Acceso a la Información Discusión e Implementación de Mejoras Uso del Crawler en la Asignatura El crawler que se desarrolla se usará para obtener documentos y crear un repositorio local que servirá de base para el motor de búsqueda. Como se trata de una prueba de concepto, y no de un sistema a gran escala, las semillas que se darán al crawler serán páginas que nos proporcionen documentos relevantes, en principio, para nuestros propósitos. El motor de búsqueda de las siguientes prácticas se evaluará con una serie de consultas. La forma que vamos a tener de obtener documentos relevantes es utilizar Google como ayuda. De esta forma, al crawler se le dará una semilla por cada una de las consultas que se realizarán sobre el motor de búsqueda, y estas semillas serán simplemente consultas sobre Google que nos den documentos relativos a esa consulta. Por ejemplo: si una de nuestras consultas es relativa a la influencia del vino tinto sobre la probabilidad de sufrir un ataque al corazón, la semilla correspondiente podría ser una consulta a Google con los términos vino tinto ataque corazón. Es decir, la semilla para el crawler sería http://www.google.es/search?q=vino+tinto+ataque+corazón. A partir de ella, el crawler irá navegado por las páginas de resultados que, potencialmente, serán relevantes a la hora de realizar la consulta sobre nuestro motor de búsqueda. Este proceso se repetiría por cada consulta que vayamos a evaluar en la asignatura. Posibles Mejoras De todas las mejoras que se muestran a continuación, cada grupo deberá implementar al menos una en su crawler, a elegir entre: 1. 2. 3. 4. 5. 6. 7. 8. Captura de enlaces. Muchas páginas no siguen el estándar XHTML, por lo que la sintaxis seguida para los enlaces no es necesariamente <a href="http://...">...</a>. Algunas páginas usan comillas simples (<a href='http://...'>...</a>). Otras no usan delimitadores (<a href=http://...>...</a>). Además, es posible que las URLs de interés estén incrustadas dentro de otras (por ejemplo con Google): <a href="http://origen.es/redirige.php?destino=http://destino.es/pagina.html">...</a>. Incluso, puede que la etiqueta esté en mayúscula: <A Href="...">...</a>. Comprobación de tipo de documento. No todos los enlaces que aparezcan serán páginas HTML, muchos serán imágenes, documentos PDF, etc. Se podría comprobar cuál es el tipo MIME (campo content-type de la cabecera HTTP), y guardar el documento sólo si es una página HTML. Podría incluso implementarse un pequeño filtro para asegurarse, porque muchos servidores Web no devuelven el tipo MIME correcto. Limitación del tamaño del documento. Puede darse el caso en que una página sea demasiado grande (incluso del orden de gigabytes). Se podría limitar el tamaño de los archivos a bajar (campo content-length de la cabecera HTTP). Almacenamiento de archivos en memoria. Para no dar demasiado trabajo al disco duro, en lugar de descargar todos los archivos a disco, y después borrarlos si no nos interesa el documento (ver mejora 2), podría almacenarse en un buffer de memoria y guardarlo en disco sólo si es de interés, incluso considerando límites de tamaño en el buffer (ver mejora 3). Tratamiento específico de semillas. Dado que las semillas serán consultas a Google, se podrían tratar de forma específica para capturar enlaces a documentos, ignorar los enlaces a búsquedas en otros servicios (News, Books, Youtube, etc), navegar por las páginas de resultados, etc. Peticiones avanzadas. Para evitar que los sites se den cuenta de que les está visitando un crawler, se pueden hacer peticiones más avanzadas. Por ejemplo, incluir el campo user-agent con información del sistema del cliente (sistema operativo, navegador, etc.), uso del campo referer, utilización de las cookies del servidor, etc. También se pueden controlar las redirecciones que un servidor intente hacer ante una petición, o hacer peticiones cada cierto tiempo, para evitar sobrecargar los servidores. Uso de varios protocolos. Puede haber resultados con protocolos diferentes a HTTP, por ejemplo FTP o HTTPS. Se podrían incluir estos documentos también, incluso permitiendo que el certificado de un site HTTPS no sea válido. Control de duplicados. Puede que un mismo documento corresponda a URLs diferentes. Podría controlarse esto mediante algún campo UNIQUE en la base de datos que no permita insertar duplicados (esto ya se hace con el campo Uri). Por ejemplo, se podría calcular el hash MD5 de los documentos. 3 Recuperación y Acceso a la Información 9. Normalización de URLs. Podrían tratarse como idénticas URLs que cambien sólo en el orden de sus parámetros. Por ejemplo http://dev.es/show.php?id=34&order=author es equivalente a http://dev.es/show.php?order=author&id=34. También es posible que aparezcan caracteres de escape en los enlaces: http://es.wikipedia.org/wiki/España se escaparía como http://es.wikipedia.org/wiki/Espa%C3%B1a, pero son la misma URL. 10. Tratamiento inteligente de niveles. Si una URL ya está en la base de datos con el nivel máximo permitido, ya no seguirá utilizándose para obtener nuevos enlaces. Sin embargo, si aparece repetida, por venir de otra página padre (ver mejoras 8 y 9) podría ser con nivel menor. En ese caso, se podría actualizar el nivel en la base de datos para seguir navegando por ella. 11. Distribución del proceso. Lo normal es que un crawler funcione en varias máquinas y con varios threads. Ello implica implementar ciertos mecanismos de concurrencia para evitar que distintos threads procesen las mismas páginas, intenten guardarlas en el mismo archivo local, etc. Sugerencias Para controlar más las cabeceras y protocolos, se pueden utilizar las clases System.Net.WebRequest y System.Net.WebResponse, que tienen una propiedad Headers. Para utilizar buffers en memoria se puede utilizar la clase System.IO.MemoryStream. Para calcular hashes consultar la clase System.Security.Cryptography.MD5. Para trabajar con certificados de HTTPS, consultar la propiedad ServerCertificateValidationCallback de la clase System.Net.ServicePointManager. Para utilizar threads consultar System.Threading.Thread y la construcción lock the C# para sincronización. Para posibles valores del user-agent, mirar http://www.user-agents.org. Para posibles valores del tipo MIME mirar http://www.webmaster-toolkit.com/mime-types.shtml. Entrega Deberá entregarse la solución de Visual Studio con el código del crawler de cada grupo, además de una pequeña memoria explicando la mejora implementada. La práctica deberá entregarse por Aula Global. El día de la entrega se procederá a la corrección presencial en clase, por lo que todos los grupos deberán asegurarse de que su crawler funciona en los ordenadores del aula. Evaluación La primera parte (crawler básico) se evaluará hasta un máximo de 7 puntos, considerando la corrección de la solución implementada. La segunda parte (mejoras) se evaluará hasta un máximo de 3 puntos, teniendo en cuenta el número de mejoras y su complejidad. 4