El pasado año, escribí un ejemplo de web crawler usando mensajes, detalles en los posts:
Distributed Web Crawler using AjMessages
Web Crawler distribuido usando AjMessages
Antes, escribí otros ejemplos usando DSS/CCR, tecnologías incluidas en Microsoft Robotics Developer Studio:
Distributed Agents using DSS/VPL
Web Crawler example using DSS (Decentralized Software Services)
Aplicaciones Distribuidas con AjMessages usando DSS/CCR
Ejemplo de Web Crawler usando DSS
Ahora, escribí un ejemplo local (no distribuido) usando agentes en AjSharp. Recordemos, cada agente se ejecuta en su propio thread, y sus invocaciones son encoladas, y ejecutadas de una en una. Más sobre agentes en AjSharp en:
Agents in AjSharp (Part 1)
Agentes en AjSharp (Parte 1)
Agents in AjSharp (Part 2)
Agentes en AjSharp (Parte 2)
Hace dos días, tomé el código de mis anteriores ejemplos, y lo reensamblé en agentes de AjSharp. Este es el primer resultado de ese experimiento. Primero, definí un objeto para armara una red de agentes que se ocupen de visitar un sitio, y lanzar el proceso:
// Build and launch agents
object WebCrawler
{
sub Process(url, fn)
{
uri = new System.Uri(url);
downloader = new Downloader();
harvester = new Harvester();
resolver = new Resolver(uri,5);
processor = new Processor(fn);
downloader.Harvester = harvester;
downloader.Processor = processor;
harvester.Resolver = resolver;
resolver.Downloader = downloader;
downloader.Process(uri, 0);
}
}
El Downloader toma una URI, baja su contenido y envía el resultado a dos agentes asociados: Processor y Harvester. El parámetro Depth indica la profundidad de esta página en el proceso de crawling del sitio:
// Downloads a page
agent Downloader
{
sub Process(uri,depth)
{
client = new System.Net.WebClient();
content = client.DownloadString(uri);
PrintLine("Downloaded: " + uri);
this.Harvester.Process(uri,depth,content);
this.Processor.Process(uri, content);
}
}
The Processor executes a user function/routine, receiving the URI and its retrieved content:
// Process the content retrieved
agent Processor
{
function Processor(fn)
{
this.fn = fn; // function to invoke
}
sub Process(uri, content)
{
// Add your logic
this.fn(uri, content);
}
}
El Harvester detecta otros enlaces en el contenido, y los envía, uno a uno, a otro agente, el Resolver:
// Get links from page
agent Harvester
{
sub Process(uri,depth,content)
{
matches = System.Text.RegularExpressions.Regex.Matches(content, "href=\\s*\"([^&\"]*)\"");
results = new List();
foreach (match in matches) {
value = match.Groups[1].Value;
if (!results.Contains(value))
results.Add(value);
}
foreach (result in results)
if (result.StartsWith("http"))
this.Resolver.Process(new System.Uri(result), depth+1);
}
}
El Resolver mantiene una lista de URIs procesados, y filtra aquellas que no se encuentran en el host original. El proceso se limita entonces hasta una cierta profundidad de exploración y dentro del sitio original:
// Filter invalid or already processed links
agent Resolver
{
var processed = new List();
function Resolver(uri,maxdepth)
{
this.host = uri.Host;
this.maxdepth = maxdepth;
}
sub Process(uri,depth)
{
if (depth > this.maxdepth)
return;
if (uri.Host != this.host)
return;
if (uri.Scheme != System.Uri.UriSchemeHttp && uri.Scheme != System.Uri.UriSchemeHttps)
return;
if (processed.Contains(uri))
return;
processed.Add(uri);
PrintLine("New Link: " + uri);
this.Downloader.Process(uri,depth);
}
}
Estos son ejemplos de uso, creando dos redes de agentes, dedicados a obtener el contenido de dos sitios:
// Example
WebCrawler.Process("http://ajlopez.wordpress.com", function(uri,content) { PrintLine("From ajlopez.wordpress "+uri);});
WebCrawler.Process("http://ajlopez.zoomblog.com", function(uri,content) { PrintLine("From ajlopez.zoomblog "+uri);});
Pueden bajar el AjSharp desde el trunk:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
El código de web crawler está en Examples/WebCrawler.ajs dentro del proyecto AjSharp.Console. Despues de compilar a este programa de consola, pueden lanzar:
AjSharp.Console Examples/WebCrawler.ajs
Esta es una salida parcial:
Pueden ver el ejemplo completo en Pastie http://pastie.org/835926
Próximo paso: usar agentes distribuidos. Hay dos caminos a explorar: uno, declarar que algunos agentes sean distribuidos usando código adicional o configuración, pero sin cambiar el código original. Dos, hacer la distribución explícita en el código.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez