>Actualizar anuncios automáticamente en Clasipar.

>

Todos sabemos que clasipar es el sitio número 1 para la compra/venta de lo que sea en Paraguay.

Cuando uno trata de vender algo le dedica tiempo a buscar compradores o a actualizar sus anuncios de modo a maximizar sus posibilidades y es justamente acá donde entré a improvisar una vez más.

Definición de la problemática.

  • Tengo varios anuncios ofreciendo productos
  • Tengo que entrar a actualizar diariamente mis anuncios pero son varios y me hacen perder mucho tiempo
  • Muchas veces me olvido y pierdo oportunidades

Es un quilombo, pero tengo la suerte de ser informático (he’i) y las computadoras son mi área de expertise por lo puedo abusar sin remordimiento de ellas (de las computadoras).

El programa

Se trata de un Internet bot que recorre clasipar como su fuera un humano. El programa recibe las instrucciones desde un archivo de texto de donde lee qué anuncio(s) actualizar, se conecta a Internet, navega clasipar como si fuera un browser normal (Internet Explorer o Firefox por ejemplo), explora el panel de control del usuario, busca cuál anuncio actualizar y genera un HTTP request con los datos necesarios.

Está hecho en C# (me encanta), corriendo sobre Linux (aunque también funciona en Windows y MacOS) y funcionando sin problemas desde hace más de un mes.

El programa es relativamente corto en líneas de código pero requiere de un background decente sobre el protocolo HTTP. Me tomé mi tiempo snifeando el tráfico de mi browser conectado a clasipar para averiguar qué datos necesitaba y bajo qué formato se hacía la transmisión. Wireshark es un espectáculo para esto y pronto pude hacer reverse engineering de lo que el sitio esperaba. El siguiente paso fue identificar las cookies y las variables POST que cambiaban (y a veces no) en cada petición. Los datos enviados venían de hidden input boxes en algunos casos por lo que no me pude salvar del pattern matching usando expresiones regulares. La basura puesta por google analytics en el request fue especialmente rompebolas y mi falta de experiencia analizando su tráfico me hizo perder tiempo pues no sabía si estos datos eran necesarios y lo peor, de dónde venían. Finalmente, leyendo el fuente HTML de respuesta pude terminar el análisis y reunir lo necesario para construir el programa.
El código

Los espacios de nombre importados hablan de las porciones del framework que usé

using System;using System.IO;using System.Net;using System.Web;using System.Text;using System.Text.RegularExpressions;
[csharp]
using HtmlAgilityPack;

using ClasiparBot.Utils;
[/csharp]
El entry pointsolo contiene lo siguiente ya que la lógica está contenida dentro de la clase ClasiBot.

[csharp]
public static void Main (string[] args){if (args.Length != 1){ Debug.PrintError(“MainClass.Main(): Invalid command line parameter(s)”); return;}

if (!File.Exists(args[0])){ Debug.PrintError(“MainClass.Main(): Configuration file is missing”); return;}

List <botparameters > botParams = ParseConfigurationFile(args[0]);

foreach(BotParameters botParameter in botParams) Debug.Notify(“MainClass.Main(): Error free? “, new ClasiBot(botParameter).StartBot().ToString().ToUpper());}
[/csharp]

El método StartBot()

[csharp]
ServicePointManager.Expect100Continue = false;

HttpWebRequest request = (HttpWebRequest) WebRequest.Create(_parameter.ManagementLink);request.UserAgent = “Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1.1 Firefox/3.5.9rn”;

request.CookieContainer = new CookieContainer();

HttpWebResponse response = (HttpWebResponse) request.GetResponse();StreamReader reader = new StreamReader(response.GetResponseStream());

HtmlDocument htmlDoc = new HtmlDocument();htmlDoc.LoadHtml(reader.ReadToEnd());

string hashID = ParseHashID(_parameter.ManagementLink.ToString()).Trim();string authToken = htmlDoc.DocumentNode.SelectSingleNode(“//input[@name=’authenticity_token’]”).Attributes[“value”].Value;

Debug.Notify(“ClasiBot.StartBot(): HashID is”, hashID);Debug.Notify(“ClasiBot.StartBot(): Authenticity token is”, authToken);

string output = AccessDashboard(request.CookieContainer, hashID, authToken);output = PerformAnnouncementOperation(request.CookieContainer);Debug.Notify(“ClasiBot.StartBot(): Operation result is ‘”, ParseResult(output), “‘”);
[/csharp]

Este método genera el URI dependiendo de la decisión de actualizar, concretar o eliminar el anuncio.
[csharp]
string BuildURIByOperation(Uri mainUri, AnnouncementOperation operation, Uri announcementUri){string operationUri = “http://” + mainUri.Host + “/ad/”;

switch(operation){case AnnouncementOperation.Refresh:operationUri += “refresh_ad”;break;case AnnouncementOperation.Delete:operationUri += “delete_ad”;break;case AnnouncementOperation.MarkAsSold:operationUri += “mark_as_sol”;break;}

operationUri += announcementUri.AbsolutePath.Replace(“.html”, “”);

return operationUri;

}
[/csharp]

Este método determina el resultado de la operación

[csharp]
public string ParseResult(string html){Match match = Regex.Match(html, @”(?.+)”);}

if (match.Success)return match.Groups[“message”].Value;

match = Regex.Match(html, @”(?.+)”);

if (!match.Success)throw new Exception(“Unable to match result in output html”);

return match.Groups[“message”].Value;}

[/csharp]

La salida

Faltan algunas otras cosas pero esto explica cómo funciona el programa para el que sabe cómo leerlo.

No publico todo el fuente porque aunque lo que hago no es ilegal, puede perjudicar su negocio y lo último que yo quiero es dañar a la industria paraguaya.

Este programa es más bien una h erramienta personal y este post pretende ser un artículo científico para gente que esté interesada en el funcionamiento de los bots.