Un crawler, también llamado robot o araña de la Web (spider), es un programa diseñado para explorar de manera automática la Web, descargando las páginas que visita.
Para ello, comienza a partir de un subconjunto de páginas dadas como semilla (seed), y que será el conjunto de páginas que deberá visitar el crawler.
De estas páginas, el crawler recupera los enlaces que contienen a otras páginas, añadiéndolos al conjunto de páginas por visitar (crawl frontier) para recorrerlas en su proceso.
Este proceso será indefinido (ad nauseum, ad infinitum) o hasta una condición de parada, dependiendo del algoritmo implementado.
La forma de construir un sencillo crawler es seguir el algoritmo básico descrito en su definición, y refinarlo con las necesidades particulares. A continuación se muestra paso a paso cómo construir un sencillo crawler en C#:
Paso 1: Referencias y globales
Lo primero será añadir las librerías a utilizar, que vienen por defecto referenciadas en el Visual Studio:
using System.IO;
using System.Net;
Y un par de variables globales a la clase. En primer lugar una tabla hash que nos permita saber si una Url a visitar ha sido previamente visitada:
Hashtable visitedUrls = new Hashtable();
Y por otro lado un dataset que nos permita almacenar las Urls visitadas con el Html recuperado:
DataSet mUrls = new DataSet();
Podríamos haber creado un dataset tipado o crearle manualmente la tabla y columnas, que es por lo que se ha optado ya que no es el objetivo del artículo:
mUrls.Tables.Add("TABLE_URLS");
mUrls.Tables["TABLE_URLS"].Columns.Add("COLUMN_URL");
mUrls.Tables["TABLE_URLS"].Columns.Add("COLUMN_HTML");
Paso 2: Obtener Html
A continuación nos creamos un método para obtener el Html a partir de una Url:
public String GetHtml(String url)
{
try
{
String sHtml = String.Empty;
HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(url);
HttpWebResponse resp =
(HttpWebResponse)req.GetResponse();
if (resp.ContentType.ToLower().IndexOf("text/html")
> -1)
{
Stream istrm = resp.GetResponseStream();
StreamReader rdr = new StreamReader(istrm);
sHtml = rdr.ReadToEnd();
}
resp.Close();
return sHtml;
}
catch (Exception ex)
{
return "ERROR: " + ex.ToString();
}
}
Es muy importante que notéis que estamos únicamente obteniendo páginas Html (
if (resp.ContentType.ToLower().IndexOf("text/html") > -1)) Si deseásemos obtener cualquier otro tipo de página (pe. un Kml de Google Maps, una imagen o un pdf), deberíamos ver qué tipo Mime nos devuelve (pe. desde un navegador, viendo código fuente), y ponerlo aquí.
Paso 3: Obtener enlaces
Ya tenemos cómo obtener el Html de una página, ahora debemos obtener los enlaces que contiene. Para ello tenemos dos opciones, nos podemos construir una expresión regular y aplicarla al código, o podemos utilizar las librerías Microsoft.mshtml para tratamiento del html. A continuación se presentan ambos métodos:
Método 1: Expresión regular
Añadiremos una referencia a la librería de manejo de expresiones regulares que utilizaremos para poder obtener las urls contenidas en los elementos a href:
using System.Text.RegularExpressions;
Nos creamos una expresión regular que empareje con la definición de la etiqueta href de Html:
private Regex hrefRegex = new Regex
("href\\s*=\\s*(?:(?:\\\"(?[^\\\"]*)\\\")|(? [^\\s]* ))");
Y un método para obtener las Urls y devolverlas al flujo principal del crawler:
public String[] GetLinks(String html, System.Uri baseUri)
{
//baseUri es necesario ya que se obtiene del href la ruta
//del enlace, que puede ser absoluta o relativa
MatchCollection hrefmatches = hrefRegex.Matches(html);
String[] sLinks = new String[hrefmatches.Count];
Int32 i = 0;
foreach (Match match in hrefmatches)
{
string newURL = match.Groups["url"].Value;
if (baseUri != null)
{
Uri newUri = new Uri(baseUri, newURL);
sLinks[i] =
new String(newUri.ToString().ToCharArray());
}
else
sLinks[i] = new String(newURL.ToCharArray());
i++;
}
return sLinks;
}
Método 2: Microsoft.mshtml
En primer lugar añadiremos la referencia a la librería:
using mshtml;
Y a continuación, y con el mismo interfaz anterior, construimos el método para obtener las urls:
public static String[] GetLinks(String html,
System.Uri baseUri)
{
//baseUri no es necesario ya que se nos devuelven las
//rutas absolutas de los enlaces
String[] Links = null;
html = html.ToLower();
IHTMLDocument2 oDoc = GetHtmlDoc(html);
Int32 iNLinks = oDoc.links.length;
Links = new String[iNLinks];
Int32 i = 0;
foreach (HTMLAnchorElement oAnchor in oDoc.links)
Links[i++] = oAnchor.href;
return Links;
}
Paso 4: Almacenar Urls y Html
Aquí creamos un método auxiliar que nos permita almacenar el Html recuperado de la Url visitada actualmente. Hemos optado por almacenarlo en un DataSet, pero se podría almacenar directamente a BBDD o cualquier cosa que necesitemos:
private void AddUrlToDataSet(String url, String html)
{
url = url.ToLower().Trim();
html = html.ToLower().Trim();
if ((mUrls.Tables[TABLE_URLS].Select("URL='" +
url + "'").Length == 0))
{
DataRow oRow = mUrls.Tables[TABLE_URLS].NewRow();
oRow[COLUMN_URL] = url;
oRow[COLUMN_HTML] = html;
mUrls.Tables[TABLE_URLS].Rows.Add(oRow);
}
}
Paso 5: Algoritmo de Crawling
Una vez tenemos los métodos necesarios, podemos escribir el algoritmo principal del crawler. Aquí vamos a mostrar un algoritmo sencillo que a partir de una Url inicial (semilla o seed), retornaría todas las páginas del dominio de la Url dada y hasta un nivel máximo de profundidad proporcionado como argumento.
private void Crawl(String baseUrl, String currentUrl,
Int32 iCurrentDepth, Int32 iMaxDepth)
{
Uri uriBase = new Uri(baseUrl);
if (iCurrentDepth <= iMaxDepth)
{
if (!visitedUrls.ContainsKey(currentUrl))
{
visitedUrls.Add(currentUrl, 1);
String shtml = String.Empty;
Uri uriCurrent = new Uri(currentUrl);
if ((uriBase.Host == uriCurrent.Host) &&
(currentUrl.StartsWith(baseUrl)))
{
shtml = GetHtml(currentUrl);
if (shtml != String.Empty)
AddUrlToDataSet(currentUrl,
shtml.ToLower());
}
if (shtml != String.Empty)
{
String[] sLinks = HtmlMngr.GetLinks(shtml);
for (Int32 i = 0; i < sLinks.Length; i++)
{
if (sLinks[i] != null)
Crawl(baseUrl, sLinks[i], urlClass,
iCurrentDepth + 1);
}
}
}
}
}
Paso 6: Consideraciones finales
La construcción de un crawler es sencilla, pero su uso intensivo y de manera eficiente requiere tener en consideración una serie de retos y cuestiones claves que se expondrán en detalle en otro artículo, pero principalmente se debería hacer especial énfasis en el uso ético y responsable del mismo, siguiendo las políticas de privacidad dictaminadas en los servidores para cada una de las páginas contenidas y que posiblemente visitaremos, y que vienen definidas en el fichero robots
En cualquier caso deseamos que el ejemplo aquí expuesto sea de su interés y utilidad, y haga un uso responsable del mismo.
No hay comentarios:
Publicar un comentario