Proyecto opensource que facilita la creación de webscrapper en .NET. Está construido sobre HTMLAgilityPack usando el patrón productor/consumidor, en el cual se puede establecer la cantidad máxima de conexiones simultáneas para no saturar el servidor web.
Fire Horse es una clase estática, que implementa N ConcurrentQueue por cada dominio al cual se realizarán consultas; y se implementan tantos hilos como dominios existan, los cuales implementan el patrón Productor/Consumidor
Este complemento puede ser instalado vía Nuget Install-Package FireHorse
Requiere contar con framework 4.5.2 o superior
Primero, se deben definir los métodos o eventos que serán invocados al procesar el elemento. Actualmente se soportan tres eventos: OnDequeue, OnDataArrived y OnExceptio; de los cuales OnDequeue y OnException son opcionales, mientras que OnDataArrived es obligatorio.
Es invocado cada vez que un elemento es removido de la cola para ser procesado. Un mismo elemento puede ser removido de la cola varias veces, dado a las políticas de reintento que se implementan en el proceso. La firma es la siguiente:
private void OnDequeue(string url, IDictionary<string, string> optionalArguments)
{
//Implementar lógica.
}
Se retorna la URL que será leída, así como una lista de clave-valor opcionales, útiles para personalizar el proceso de extracción.
Es invocado cuando se produce un error y la política de reintentos establecidas fue superada. Por ejemplo, si se establece una política de tres reintentos, los primeros tres errores no gatillarán este evento; recién el cuarto error será notificado. La firma es la siguiente
private static void OnException(string url, IDictionary<string, string> optionalArguments, Exception ex)
{
}
Invocado cada vez que se retorna satisfactoriamente la información desde el servidor. La firma es la siguiente:
private static void OnDataArrived(string url, IDictionary<string, string> optionalArguments, HtmlDocument htmlDocument)
{
}
Para agregar un nuevo elemento a la cola, se debe instanciar un nuevo ScraperData, el cual contiene información de la url que se analizará, así como punteros a los eventos mencionado en los puntos anteriores. Además, contiene un diccionario clave-valor, el cual es útil para agrupar distintos trozos de información que conceptualmente pertenecen a uno solo.
var item = new ScraperData();
item.Url = url;
item.OnDequeue = OnDequeue;
item.OnDataArrived = OnDataArrived;
item.OnThrownException = OnException;
FireHorseManager.Enqueue(item);
Para esperar a que el proceso concluya, se puede implementar un while con sleep, de la siguiente manera
while (!FireHorseManager.IsEnded && !FireHorseManager.IsActive)
{
Thread.Sleep(2000);
}
Además, se puede configurar la cantidad máxima de consultas realizadas a un mismo dominio. Por defecto, el valor es 40 y puede ser actualizado de la siguiente manera:
FireHorseManager.MaxRunningElementsByDomain = 5;
FireHorse implementa algunas propiedades de solo lectura, que permiten conocer el estado del proceso
Retorna un entero que informa la cantidad de elementos que están siendo consultados al servidor.
int size = FireHorseManager.CurrentRunningSize;
Retorna un entero que informa la cantidad de elementos que existen en todas las colas
int size = FireHorseManager.CurrentQueueSize;
Retorna un diccionario (Dictionary<string, int>)
que contiene por cada dominio, la cantidad de elementos que están siendo consultados al servidor
foreach (var item in FireHorseManager.CurrentRunningSizeByDomain)
{
Console.WriteLine("Dominio:{0}, Cantidad:{1}", item.Key, item.Value);
}
Por defecto, el sistema se iniciará de forma automática, y no se detendrá sin importar si hay elementos en la cola o no. Para detener el proceso manualmente, se puede utilizar el método Stop()
. La llamada de este método puede tardar un par de segundos en completar, dado que internamente esperará a que los elementos que actualmente están en el estado de "Running", terminen su ejecución.
FireHorseManager.Stop();
Una vez que el proceso ha sido detenido manualmente, sin importar si existen elementos en la cola o no, quedará en ese estado hasta que manualmente sea iniciado con Start()
.
FireHorseManager.Start();