Problema
Desde hace varios meses cada domingo envió más de 250 SMS vía Orange a distintos celulares de diferentes compañías telefónicas, Orange tienen una aplicación web que me permite agregar contactos y a la vez con estos agregarlo a listas de contactos para poder enviarle lo mensajes, sucede que los números que no pertenecen a Orange no se pueden enviar y a la vez se quedan en los estados “Numero Invalido”, “Pendiente”, “Expirado” y “Fallido”, hasta aquí vamos bien, la aplicación web me provee una opción para ver el histórico de mensajes enviado, el problema consiste en que el histórico no me provee una opción para filtrar los números que están en los estados “Numero Invalido”, “Pendiente”, “Expirado” y “Fallido”.
Solución
Dada esta problemática decidí por mi cuenta poder hacer consultas al html que genera la aplicación
Tome solamente del código html el table donde están los campos que necesito para poder hacerle más adelante consulta con Linq y lo guarde en el disco C: con el nombre geeksPhones.html
Entendiendo el esquema del archivo guardado
El archivo guardado tiene el siguiente esquema para la primera fila:
Esta primera fila realmente no nos interesa la que si nos va a interesar es a partir de la segunda fila en adelante que tendrá el mismo código repetitivo de los 1546 mensajes enviados:
Diseño de nuestra aplicacion
Como todos sabemos HTML se deriva del GML (Standard Generalized Markup Language – Lenguaje de Marcación Estándar Generalizada), que tambien xml se deriva del GML, entonces decidi convertir el HTML en XML y esto se hizo posible usando el componente htmlagilitypack y la vez parsiando ese html con mi Extension Methods ToXMLDocument();
public static XDocument ToXMLDocument(this HtmlDocument doc) { using (StringWriter strWriter = new StringWriter()) { doc.OptionOutputAsXml = true; doc.Save(strWriter); return XDocument.Parse(strWriter.GetStringBuilder().ToString()); } } |
Tenemos tambien la clase Phone:
public class Phones { public string SendDate { get; set; } public string Phone { get; set; } public string EndDate { get; set; } public Status Status { get; set; } } |
Y finalmente el Enum Status:
public enum Status { None = 0, //Siempre uso este como valor Default Recibido = 1, Pendiente = 2, NumeroInvalido = 3, Fallido = 4, Expirado = 5 } |
Definido ya estos elementos entonces use el siguiente Linq Query para poder leer el html y convertirlo en una Lista genérica de tipo Phone:
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.Load(@"C:geeksphones.html"); XDocument xmlDoc = doc.ToXMLDocument(); List<Phones> query = (from d in xmlDoc.Descendants("table") let tbody = d.Element("tbody") let tr = tbody.Element("tr") select new { SendDate = (from s in tbody.Descendants("td") select s into tempsenddate where tempsenddate.Attribute("width").Value == "5%" && |
Explicación del Query
Básicamente lo que queremos de nuestro HTML es saber la fecha de envió, el número de teléfono, el estatus del mensaje y la fecha de recepción, entonces para obtenerlo correctamente tenemos que:
SendDate: solamente se llenara cuando el elemento (td) tenga el atributo width en 5%, no tenga más elementos hijos y la longitud del valor del elemento sea igual a 19 caracteres.
Status: para que pueda tener los datos correctos se debe cumplir la condición de que el elemento (td) tenga el atributo width = 3%, el elemento no tenga más elementos hijos y la longitud del valor del elemento este en un rango de 3 hasta 15 caracteres.
PhoneNumber: para obtener los números correctamente se debe cumplir las condiciones de que el elemento (td) tenga el atributo width = 5%, el elemento no tenga más elementos hijos y la longitud del valor del elemento sea igual a 10 caracteres debido a que los números en mi país tiene un total de 10 caracteres.
ReceiveDate: para obtener la fecha de recibido correctamente se debe cumplir las condiciones de que el elemento (td) tenga el atributo width=3%, el elemento no tenga más elemento hijos y la longitud del elemento sea igual a 18 caracteres o a 3 caracteres ya que cuando el mensaje se queda en el estatus expirado la fecha sale con los caracteres “—“.
Descargas
Codigo Linq To HTML aqui.
Archivo geekPhones.html aqui. (este archivo es necesario ponerlo en el disco C:)
Plus
Dentro de la aplicación también encontraran un Tab llamado leer imágenes el cual tiene la funcionalidad de leer las imágenes de una dirección web dada y mostrar el largo y el ancho de la imagen.
y el Codigo para esto es:
HtmlAgilityPack.HtmlWeb hweb = new HtmlAgilityPack.HtmlWeb(); string strUrl = @textBox1.Text; Uri url = new Uri(strUrl); HtmlAgilityPack.HtmlDocument doc = hweb.Load(strUrl); var xdoc = doc.ToXMLDocument(); var imgs = from x in xdoc.Descendants() let width = int.Parse((x.Attribute("width") ?? new XAttribute(XName.Get("width"), "0")).Value.TrimEnd('%')) let height = int.Parse((x.Attribute("height") ?? new XAttribute(XName.Get("height"), "0")).Value.TrimEnd('%')) let metrica = Math.Sqrt(width * height) where x.Name.LocalName == "img" orderby metrica descending select new { Src = x.Attribute(XName.Get("src")).Value, Width = width, Height = height }; |
Espero que le haya sido de utilidad.
hola soy ilio, el de los videos, me gusta tu idea, quieres que te firme un contrato para que hagas unos videos de C# y LINQ
Saludos espinete, bueno el costo de envio de 250 sms via orange es un peso dominicano (RD$1.00) y ahora mismo en nuestro pais el Euro esta la compra a 49 pesos por euro, asi que puedes sacar tus calculos, con relacion a la pregunta que si el HTML debe estar bien formado, en principio podriamos decir que si, pero una de las particularidades que tiene HtmlAgilityPack es que al convertirla como XML puede ser que cierre algunos tags que talvez no se guardaron.
Fabramafioso puedes probarlo con WXP ya que lo importante es .net Framework 3.5 que lo trae VS 2008.
Saludos
Señor, cómo consigo la clase XDocument, yel Pack de HtmlAgilityPack
using HtmlAgilityPack;
Dónde lo puedo descargar ??
Gracias, saludos.
Saludos espinete puedes bajarlo desde aqui
http://www.codeplex.com/htmlagilitypack/
y XDocument esta en System.Xml.Linq;
Oh, its great!
Mi hermano te la comiste con el articulo jejeje.