Google+ Seguidores

martes, 23 de enero de 2018

Web Scraping - Extrayendo Información de Paginas Webs

Introducción:

¿Que hacemos cuando nos gusta el diseño de una pagina web? Nos vamos directo a "Inspeccionar" ¿Verdad?. Lo mismo cuando queremos pedazos de código de scripts, o queremos saber como hicieron esa animación css tan espectacular. O queremos robarles el código hexodecimal de esos colores que nos encantan.

Sí, es muy fácil, solo click derecho e inspeccionar. ¿Pero como haríamos si quisiéramos obtener todas las imágenes de una pagina en concreto, o quisiéramos todos los párrafos de una pagina? Hacer eso a mano no es muy agradable ¿Cierto? .

Supongamos que debemos cumplir una misión llamada "Topo.com", la cual consiste en escribir un script que sea capaz  de guardar en un archivo de texto todas las imágenes y links de una pagina web.

Muy bien, como lograríamos cumplir este objetivo. ¿Lo harías a mano, usando el inspeccionador de elementos?. Si dijiste que sí, no tienes mucha imaginación.

Para problemas como estos se requieren de métodos especiales, como lo es el Web Scraping.

En el día de hoy les hablare sobre que es el web scraping, como funciona, sus usos, tecnicas, y al final realizaremos un software que resuelva el problema que plantee hace unos momentos.

¿Están listos para trabajar en la misión "Topo.com"? Pues comencemos.

Imagen relacionada

Web Scraping - Extrayendo información de la web:

¿Que es Web Scraping? El web scraping, es un proceso que nos permite extraer y recopilar información de una paginas web de forma automatizada. Estos software simulan la navegación de un usuario en la World Wide Web (WWW), ya sea utilizando el protocolo HTTP (Protocolo de Transferencia de Hipertexto), o incrustando navegadores web en aplicaciones.

El web scraping, se enfoca en trasformar datos sin estructurar que se encuentran en la web (como los documentos HTML), en datos estructurados que se pueden almacenar en una base de datos, o en alguna otra fuente de almacenamiento.

El web scraping tiene muchos usos, entre ello: Comparación de precios en tiendas, la monitorización de datos relacionados al clima de cierta región, la detección de cambios en un sitio web, entre otros.

Técnicas para realizar web scraping:

Una de las técnicas para realizar web scraping es conocida como "Copiar y Pegar", la cual es hecha por una persona. En muchas ocasiones es útil, ya que ciertas paginas colocan barreras contra los scrapers, impidiendo que los software sigan excavando la web, así que en estos casos, una persona hará un mejor trabajo.

La otra técnica, la cual es la más usada por que simula a un navegador web, es enviando peticiones HTTP a la pagina para luego procesar la respuesta. La respuesta se puede procesar utilizando expresiones regulares para extraer la información que deseamos del cuerpo HTML de la pagina.

Medidas para detener a los scrapers:

Si eres administrador web, puedes tomar unas medidas preventivas contra los scrapers, a continuación te mostrare una lista proporcionada por nuestros amigos de Wikipedia:

  1. Añadir entradas al fichero robots.txtGoogle y otros bots pueden ser detenidos de esta forma.
  2. Bloquear la dirección IP. Esto también bloqueará todos los accesos desde esa misma IP, por lo que los usuarios no podrán navegar por el sitio web si acceden desde esta.
  3. Deshabilitar cualquier interfaz de programación de aplicaciones que el sitio web pudiera estar brindando.
  4. Los bots o scrapers algunas veces declaran quienes son, y gracias a esto pueden ser bloqueados. «Googlebot» es un ejemplo. Algunos scrapers no hacen distinción entre ellos y un navegador común.
  5. Monitorear el exceso de tráfico proveniente de cierta IP.
  6. Añadir un captcha u otro sistema de verificación manual al sitio web. No se garantiza el completo bloqueo de los scrapers, pero mediante esta técnica se dificulta el acceso de los mismos a los sitios webs.
  7. Servicios comerciales antibots: algunas empresas ofrecen servicios antibots y antiscraping.
  8. Incrementar el uso de JavaScript y AJAX. De esta forma es más difícil para los scrapers simular las peticiones como si fueran un navegador común.

Herramientas útiles:

Existen varios software que nos permiten realizar web scraping, entre ellos:

Proyecto Scraped:

Perfecto, vemos que existen muchos software para el web scraping, pero como programadores que somos, realizaremos nuestro propio software a la medida y que cumpla el objetivo de hoy. Así que llego el momento de iniciar el "Proyecto Scraped".

Muy bien, analicemos el problema: Debemos extraer el cuerpo de la pagina web (Documento HTML) que se le proporcione al software, para que luego este pueda extraer todas las etiquetas img y a que se encuentren dentro del documento HTML, para ello se debe procesar el contenido del documento ya que no queremos toda la etiqueta si no simplemente la URL de las imágenes y lo mismo con los links. Luego, guardaremos las imágenes y links dentro de un archivo de texto.

Todo este procedimiento lo programaremos en un simple scripts:


En esta primera parte del script creamos una conexión con cualquier pagina web, yo escogí a pythondiario.com pero pueden escoger la que sea de su preferencia. Luego de crear la conexión, hacemos una petición GET, pidiendo el archivo "index.html". Una vez recibido el recurso solicitado, parseamos el contenido para que sea más fácil obtener los datos deseados.

Si no sabes mucho sobre las peticiones HTTP, te recomiendo leer el siguiente articulo: El nacimiento de la World Wide Web y los Fundamentos de la web (HTTP y HTML).

Muy bien, ya tenemos el cuerpo de la web, ahora debemos "excavar", y obtener todas las etiquetas img y a que se encuentren en  la pagina.


En esta parte del script, creamos nuestro archivo de texto, y en el escribimos los datos extraídos de la web.

El documento generado, debería quedar así:

 
Como pueden observar, el archivo es extremadamente largo.

¿Que les pareció? ha sido un ejemplo sencillo, pero esto es solo una minúscula parte de lo que se puede lograr con web scraping. Con el web scraping se pueden lograr cosas increíbles, queda de tu parte experimentar y probar.

Y como dijo mi tío ben un día, "un gran poder, conlleva a una gran responsabilidad", así que espero que uses estos conocimiento de manera adecuada.

Bueno, eso es todo de mi parte. ¿Que esperas? Crear tu software de web scraping y envíanoslo, queremos ver que tal te quedo.

Descarga el codigo completo del ejemplo de hoy desde GitHub: Web Scraping (Topo.com).py.

Mi nombre es Luis, y fue un placer compartir mis conocimientos con todos ustedes :D.
Seguir Leyendo

lunes, 22 de enero de 2018

Números a Cadenas - Proyectos Python

Introducción:

Hola, en este articulo tiene como objetivo desarrollar un script o modulo capaz de devolver la cantidad de un numero pero de forma escrita.

captire

Antes de profundizar en este script debes de saber obviamente python.

requisito:

  • Python2 ó Python3(Recomendable).
  • Saber de recursión(No es necesario).
  • Saber Programación orientada a objetos(No es necesario).

Librerias:

  • sys (usaremos solo el modulo "argv")
  • termcolor (Para imprimir la salida con algunos colores.) No es soportado en win32

Ventajas y desventajas

La creación de este script es poder sumergirnos en el tema de la recursividad y la programación orientada a objetos.
Pero cuales son las ventajas y las desventajas de desarrollar este script, bueno son las siguientes:


Ventajas:


 - Lograr entender la recursividad.
 - Lograr entender la programación orientada a objetos


Desventajas:


 - Al utilizar la reucrsividad como una opcion para el desarrollo de este script, 
lamentablemente el script consumirá mucha memoria y se tornara lento en algunas 
ocasiones.

¿Qué es la recursividad?

Es la forma en la cual se especifica un proceso basado en su propia definición. Osea cuando una función es llamada así mismo.
Recursión - Wikipedia

def f(start, stop):
    """
       Esto es un ejemplo simple de recursividad, la funcion se llamara a si misma hasta completar lo que el usuario quiere. Esta funcion tiene como objetivo imprimir los numero desde una cantidad inicial hasta llegar al numero de parada.
    """
    
    print(start)
    start += 1
    if start == stop:
        return start 
    return f(start, stop)  """ Es necesario retorna en esta parte porque si, a la   llamda siguiente, a funcion no recivira ningun dato y la funcion dejara de         ejecutarse al momento de indicarle que retirne la llamada de la funcion se entra
 en un bucle y hasto se le conoce como recursividad. """                  f(1, 10) 

Comienzo de la lógica del algoritmo.

Para comenzar con el desarrollo debemos analizar el problema en este caso solo 
necesitamos como poder ordenar el numero para que podamos trabajar de una forma 
muy sencilla a la hora de hacer la sustitución.

Primeros pasos:

Antes de empezar lo ideal seria saber que debemos hacer en estes es decir familiarizarnos con el funcionamiento del problema, bueno, lo que se espera es retornar la cantidad de un numero, facil pero como hacer que sea de forma ordenada y sencilla, "sustitucion".
Lo que debemos hacer es descomponer el numero ingresado en sub-grupos, y clasificaros, ejemplo: input>>>10000; en ese ejemplo podemos ver que hay dos sub-grupos ya que en si ese numero seria diez mil, pero a simple vista no se podría separar o saber los dos sub-grupos por esto se tienen que añadir las (,), para poder separarlos de forma mas sencilla.
Sabiendo eso ya tenemos la primera parte lista, solo falta añadirles el valor correspondiente y seria asi:
- el sub-grupo mayor siempre sera el primero. eje: 1,000 -> 1 pertenece a (**mil**).
entonces ese es el mayor.
- para ser mas ordenados siempre se iniciara desde el sub-grupo mayor.
Sabiendo esto solo falta los datos para representar cada valor de los números y de los sub-grupos.
Hay entra el archivo numeros_.py ese archivo contiene dos diccionarios, los cuales son:
- LISTA_NUMEROS
- NUMEROS_RAROS

Funcionan de la siguiene manera:
El diccionario LISTA_NUMEROS se contiene las llaves 1, 2, 3, y 4, cada una representan el largo del sub-grupo y el largo del carácter: ej: si el numero es 100, buscara el diccionario con el largo de ese numero en este caso (3), LISTA_NUMEROS["3"], y dependiendo del largo de los sub-grupos se obtiene el nivel. en este caso seria asi LISTA_NUMEROS["3"][1], ya que se hace un for en el sub-grupo, si es cero el numero entonces seguirá sin sustituir. Con esta idea ya tenemos el script terminado solo le hacen falta algunos retoques, validar si ya se recorrió el numero, validad si el numero es raro osea pertenece a (once, doce, trece...), saber si el numero es un cero, saber si el numero debe contener la preposicion y etc...
Se utilizo la programación orientada a objeto en el desarrollo de este script, porque facilitaba el uso de los datos a la hora de modificarlos, ya que se usa la recursibidad y es un poco cansoso definir variables globales

explicación del código

capture0
Se importan las librerías y los diccionarios
 Capture2

Bueno, los primero es vizualizar cada una de las variebles y luego decir su utilidad
  • self.result: es la variable que sele añadian los datos del numero.
  • self.number_copy: se retornara para indicar que fue el valor ingresado "1,000"
  • self.number: en esta variable de guradara, una lista con los sub-grupos. "['1', '000']"
  • self.len: guardara el largo de el total de sub-grupos. "2", luego se aplica un replace para eliminar los espacios.
  • self.LN: es el diccionario que contiene los valores.
  • self.vueltas: No es usado! "no es necasario definirlo".
  • self.len_grpup: se guardara el largo de cada sub-grupo.
capture3
el metodo find se enargara de hacer todo el trabajo, se dividira en doferentes areas o pasos y cada uno tedra su explicacion.

Explicación

  • Paso 0 (gris): en esa condicion se verifica que si el largo total de todos los sub-grupos, es cero retorne el self.result, cuando es cero quiere decir que ya no hay sub-grupos porque lo recorrió todos. no se utiliza la self.len para verificar porque el valor se actualiza una vez termine el bucl for, por lo que si se utiliza hay se produciría un error.
  • Paso 1(rojo): aqui se toma el largo del sub-grupo que se encuentre en la primera posicion de la lista, y con este largo se pretender saber a que categoria pertenece ese sub-grupo, entonces dependiendo de ese largo se busca en LISTA_NUMEROS, ej: el numero es "100", en la lista seria ["100"], si tomamos el sub-grupo de la primera posicion seria ["100"][0] -> "100", tomamos el largo seria -> 3 y si buscamos en LISTA_NUMERO[3] devolveria algo asi: ss y estaria en lo cierto ya que "100" pertenece a esa categoria, para eso es la declaracion de self.len y para eso se le asigna el alargo del sub-grupo.
  • Paso 2(violeta): se utiliza este bucle para recorer los valores del sub-grupo, se usa enumerate() porque retorna el valor y el indice de dicho valor, el indice nos ayudara a poder predecir el valor futuro del elemento siguiente.
  • Paso 3(amarillo): en este paso se indica que si el largo del sub-grupo es de 2 y si se une al numero futuro y pertenece a los NUMEROS_RAROS, devolvera el valor y se parara la ejecución, para comenzar de nuevo. los numeros raros son los números que no funcionan con la preposición y, ejemplo "treinta y cuatro" -> no es raror; pero, "once" -> si es raro, porque no es diez y uno. en si lo que hace es buscar en la tupla NUMEROS_RAROS, dependiendo del valor, si es el "11", buscaría en la posición 11-1, que seria la posición 10-"once".
  • Paso 4(verde): este paso se cumple si el paso 3(amarillo) no se cumple, aqui solo pasaran los numeros que no son raros, se verifica de que el numero sea mayor a cero si lo es entonces se buscara el valor del numero, buscando primero su categoria y luego su valor, luego se verifica de que si el largo del subgrupo es dos, y el numero siguiente es diferente a 0(cero), entonces, se le añadira la preposicion y. ejemplo, el numero es "42", busca en LISTA_NUMERO["2"][int(4-1)] -> retornando "cuarenta", luego se le añade el "y ", devido a que el largo es 2 y el numero siguiente no es cero, porque es 2, luego al hacer eso denuevo, LISTA_NUMERO["1"][int(2-1)] -> "cuarenta y dos".
  • Paso 5(morado): aqui se le resta 1 al largo del grupo actual, porque ya se sustituyo el valor del numero obtenido, esto hace que se adapte al cambio haciendo que sea mas preciso eso explica el porque en el caso del 2 en el punto anterior su largo fue 1.
  • Paso 6(azul): aqui se verifica de que si el largo de los sub-grupos en total es mayor a 1 y si el valor de ese sub-grupo es mayor a 0, entonces, se le añadira su categoria dependiendo del largo, si el largo al cual se le resta 2, y de esa forma se devolvera su categoria. ej: si el numero es "1,000" entonces el largo es 2, al entrar al condicional pasa, ya que el largo es mayor a 1 y el subgrupo 0 es 1, luego se busca en el diccionario LISTA_NUMEROS["4"], la llave por defecto es "4" y, al restarle - 2 al largo quedaria 0,por lo que pertenece a mil.
    n1g
  • Paso 7(rosado): aqui se le resta un valor a self.len de esta forma de actualiza el largo del sub-grupo, self.vueltas no es necesario, y luego se elimina el sub-grupo que se encuentre en la primera posicion osea, se elimina el subgrupo ya recorido, de esta forma cuando se recorrarn todos los sub-grupos al llegar, el largo llegara a 0 y se retornara self.return, luego se llama de nuevo a la misma funcion para que logre recorer el sub-grupo siguiente, esto es la recursibidad. de esta forma no es necesario recorrer cada sub-grupo de forma individual osea que por cada sub grupo se defina una funcion, ya que solo serviria para escribir mas codigo, y esto es una ventaja que nos brinda la recursibidad.
  • Paso 8(gris): este es el ultimo paso y el mas importante porque si no estubiera solo se lograria ver como se sustituyen los valores y no manipular el resultado, se utiliza el retorn, porque en si la funcion aunque no necesita recibir un valor pero si nesetita retornarlo, si no se usara este retorn, entonces el metodo, no retornaria nada. pero entonces para que sirve paso 0(gris), bueno es si ese paso solo funciona para detener la ejecucion de dicho metodo, porque si no se detuviera puede que un error lo haga o solo se sature el sistema.
Se utilizo el try, porque si ingresan un numero erróneo eso saltaria un error y lo que hice fue capturar ese error para mostrar el help, que imprime como funciona el script o modulo.

Código del programa: https://github.com/DarkCode01/number_to_string/.

Bueno de estas maneras se pudo crear este script, ahora intenta desarrollar otro de una forma diferente o mas optimizada :)
ddd


[GITHUB: Darkcoder01](https://github.com/DarkCode01)
Seguir Leyendo

El nacimiento de la World Wide Web y los Fundamentos de la web (HTTP y HTML)

Introducción:

¿Que es la Web? Cualquiera puede responder, es la red informática mundial. Pero como curiosos que somos, queremos saber más. ¿Que es la Wed? ¿Donde nació la Web? ¿Quien creo la web? ¿Cuales son los fundamentos de la Web? ¿Que es HTTP? ¿Que es WWW? ¿Quien es Santa Claus?. En el día de hoy, resolveremos estas interrogantes.

Una breve historia sobre la WWW:

La WWW (World Wide Web) o, la web, se a convertido, junto al correo electrónico, en el caballo de batalla de Internet. Ésta ha dejado de ser una inmensa biblioteca de paginas estáticas para convertirse en un servicio que nos permite a acceder a una gran multitud de funciones, así como una gran infinidad de servicios, programas, tiendas, aplicaciones, y muchos más.

En el año 89, Tim Berners-Lee,mientras trabajaba en el CERN (Centro Europeo de Investigación Nuclear), empezó a realizar un sistema que hiciera más accesible la información del CERN. Este sistema, utilizaba el hipertexto para estructurar una red de enlaces entre los documentos. Una vez obtenido la aprobación de continuar con el proyecto, nació el primer navegador web llamado "WorldWideWeb".

Resultado de imagen para world wide web

Para 1992 el sistema se había extendido fuera del CERN. El numero de servidores "estable" había alcanzado la sorprendente cifra de 26. A partir de aquí, el crecimiento es espectacular.


En 1993, nace Mosaic, un navegador web para Windows, que posteriormente se convertiría en Netscape el cual fue un factor clave para la popularización de la web. En 1994 se fundó la WWW Consortium que se convertiría en el motor de desarrollo de los estándares predominantes de la web. partir de ese momento, el crecimiento ya fue constante, convirtiéndose hacia finales de los noventa en el servicio insignia de Internet y dando lugar al crecimiento imparable de los servicios en línea que estamos experimentado actualmente.

Resultado de imagen para mosaic web

Los ingredientes secretos de la WWW:


¿Cuales fueron los ingredientes que permitieron el éxito de la web? El éxito de la web se basa en dos puntuales fundamentales: el protocolo HTTP y el lenguaje HTML. Uno permite una implementación simple y sencilla de un sistema de comunicaciones que nos permite enviar cualquier tipo de ficheros de una forma fácil, simplificando el funcionamiento del servidor y permitiendo que servidores poco potentes atiendan miles de peticiones y reduzcan los costes de despliegue. El otro nos proporciona un mecanismo de composición de páginas enlazadas simple y fácil, altamente eficiente y de uso muy simple.

El protocolo HTTP:

El protocolo HTTP (Protocolo de Transferencia de Hipertexto) es el protocolo base de la WWW. Es un protocolo simple orientado a conexión y sin estado. La razón de que este orientado a conexión es que emplea para su funcionamiento un protocolo de comunicación, TCP (Protocolo de Control de Transmisión), de modo conectado. El protocolo TCP, estable un canal de comunicaciones de extremo a extremo (entre el cliente y el servidor) por donde pasan el conjunto de bytes que constituyen los datos que hay que transferir. El protocolo HTTP no mantiene estado, es decir, cada tranferencia de datos es una conexión independiente de la anterior, sin relación alguna entre ellas, hasta el punto de que para transferir una pagina web tenemos que enviar el código HTML, así como las imágenes que la componen.

De manera esquemática, el funcionamiento de HTTP es el siguiente: El cliente establece una conexión TCP hacia el servidor, hacia el puerto HTTP, envia un comando HTTP de petición de un recurso (junto con algunas cabeceras informativas) y por la misma conexión el servidor responde con los recursos solicitados y algunas cabeceras informativas.
Imagen relacionada




Existe una variante de HTTP llamada HTTPS (Protocolo Seguro de Transferencia de Hipertexto) el cual usa el protocolo de seguridad SSL (Secure Socket Layer) para cifrar y autenticar el trafico web entre el cliente y el servidor, siendo esta muy usado por los servidores web de comercio electrónico, así como por aquellos que contiene información personal o confidencial.

Resultado de imagen para HTTPS




A continuación describiré algunas directivas de petición que define HTTP 1.1:
  1. GET: Petición de recurso.
  2. POST: Petición de recurso pasando parámetros.
  3. HEAD: Petición de cabeceras informativas.
  4. PUT: Creación o envió de recursos.
  5. DELETE: Eliminación de recurso.
  6. TRACE: Devuelve el origen de la petición tal como se ha recibido en el receptor, para depurar errores.
  7. OPTIONS: Sirve para comprobar las capacidades del servidor.
  8. CONNECT: Reservado para uso en servidores intermedios capaces de funcionar como túneles.

Peticiones en HTTP - GET y POST:

Las peticiones en HTTP pueden realizarse utilizando dos métodos. El método GET en caso de enviar parámetros junto a la petición, las enviaría codificadas en la URL. El método POST en caso de enviarlos, lo haría como parte del cuerpo de la petición.


En la imagen anterior, podemos presenciar una petición GET.

La primera linea es conocida como la linea de petición, la cual contiene:
  1. Método: En este caso GET.
  2. Identificador de Recurso: URL del recurso solicitado ("/index.html").
  3. Versión del protocolo: solicitada para la respuesta.

Las cabeceras de petición, contiene información que pueden ayudar al servidar a procesar adecuadamente nuestra petición.

La información se proporciona de la forma: identificar: valor.

  1. Host: nombre del servidor solicitado.
  2. User-agent: nombre del navegador o programa usado para acceder al recurso.
  3. Accept: algunos formatos de texto e imágenes aceptados por el cliente.
  4. Accept-language: idiomas soportados por el cliente.

Respuesta en HTTP:

Las respuestas en HTTP son similares a las peticiones que realizamos. La respuesta a la petición mostrada anteriormente, se vería algo así:



En ella podemos observar que en la primera linea nos responde con la versión del protocolo empleado para enviarnos la pagina, seguida de un código de retorno y una frase de retorno. El código de retorno puede adoptar una de los siguientes valores:


  1. 1xx: Petición recibida, continua en proceso.
  2. 2xx: Correcta. Petición procesada correctamente.
  3. 3xx: Redirección. La petición debe repetirse o redirigirse.
  4. 4xx: Error de cliente. No se puede realizar la petición porque esta es incorrecta.
  5. 5xx: Error de servidor: El servidor ha fallado al tratar de procesar la petición, que a priori es correcta.

El lenguaje HTML:

El otro ingrediente, que aseguro el éxito de la web, es el lenguaje HTML (Lenguaje de Marcado de Hipertexto). HTML es un lenguaje de marcado que nos permite representar de forma rica el contenido y también referenciar otros recursos (imágenes, entre otros), enlaces a otros documentos (la característica más destacada de la WWW), mostrar formularios para posteriormente procesarlos, entre muchas cosas más.

Resultado de imagen para HTML

HTML es un lenguaje que cualquiera con conocimientos de programación, lo conoce, aun sin uno ser desarrollador web, ya que en muchas ocasiones, nos toparemos con el.

Bueno, creo que es suficiente por hoy, ¿Que te pareció? interesante, verdad.

¿Que esperas? Es tu turno de crear tu propio navegador web.

Mi nombre es Luis, y fue un placer compartir mis conocimientos con todos ustedes :D.
Seguir Leyendo

sábado, 20 de enero de 2018

Creando Calculadoras del Tiempo - Proyecto "Intant-Date"

El proyecto "Intant-Date" es un software desarrollado por Antonio Alfonso Martinez. Dicho software nos permite calcular la distancia que hay entre dos instantes del tiempo en horas, minutos y segundos.

  

 Antonio, realizo una serie de artículos en donde nos explica el desarrollo del software. Lo hizo de una manera clara y precisa.

Esta es una lista con todos los artículo de la creación del proyecto "Intant-Date":

  1. Creando Calculadoras del Tiempo #1 - Una breve introducción al proyecto.
  2. Creando Calculadoras del Tiempo #2 - El funcionamiento de VALID.py.
  3. Creando Calculadoras del Tiempo #3 - El programa principal ("Intant-Date").
  4. Creando Calculadoras del Tiempo #4 - Los cálculos finales

Puedes descargar el código de todos los scripts desde el perfil de Github de Antonio: https://github.com/antonioam82/Timer.
Seguir Leyendo

Creando Calculadoras del Tiempo #4 - Los cálculos finales

Articulo escrito por Antonio Alfonso Martinez.
Continuamos con la serie de artículos dedicados a la creación de nuestra calculadora de tiempo, la cual, una vez terminada, va a ser capaz de calcularnos, en horas, minutos y segundos, la distancia existente entre dos instantes de tiempo, ubicados en cualquier fecha entre el 1 de Enero del año 1 y el 31 de Diciembre de 9999.
En el artículo anterior, empezamos ya a realizar nuestros primeros cálculos, los cuales efectuábamos sobre instantes ubicados en un mismo día del año. En el presente artículo, vamos a proceder a ver como logramos hacer ese mismo tipo de cálculos, esta vez, entre instantes pertenecientes a distintas fechas.
Como recordarán, en el anterior artículo, empezábamos explicando la manera en la que pedíamos al usuario de nuestro programa, los datos necesarios para hacer los correspondientes cálculos y de las medidas que empleábamos (mediante el uso de varias funciones) para asegurarnos de la idoneidad de dichos datos introducidos. Ni que decir que no vamos a volver a explicar esa parte en tanto que las operaciones que vamos a explicar a continuación, trabajarán, también, con esos mismos datos.
De ese modo, para comenzar la explicación, partiremos del código que empleamos para instantes acaecidos en un mismo día:
Así teníamos que en el caso de que el año, mes y día coincidieran, pasábamos a emplear el código que ya vimos en el artículo anterior. Por su parte con “else:” estamos diciendo que, de no ser así (por no darse tales todas esas coincidencias), pasaremos a ejecutar la parte del código que vamos a explicar.
Para empezar, diremos que debido a que en esta ocasión no hay coincidencia de fechas, las diferencias que haya con respecto al año, mes y días van a ser determinantes para el resultado final. Por ello, en esta ocasión, si vamos a hacer un mayor uso del modulo “datetime” el cual a importamos en su momento.
Debido a que vamos a hacer uso de una nueva función (la función “datetime”) para calcular la distancia entre instantes ubicados en fechas distintas, vamos a proceder a poner un sencillo ejemplo de su funcionamiento.
Como se puede ver, hemos utilizado la formula “datetime.datetime”, primero para definir los instantes de tiempo y luego efectuar la diferencia entre los mismos. En este ejemplo queremos calcular la distancia existente entre la hora de inicio del 2017 y las 4 horas del día 3 de Febrero de 2018. Para ello, hemos introducido (para los dos instantes, “I1” y “I2”), de izquierda a derecha, el año, mes, día, hora, minuto y segundos correspondientes. En la variable “difer” hemos almacenado la diferencia entre ambos instantes. Esto nos ha dado un resultado de 398 días y 4 horas. Lo cual tiene sentido ya que a los 365 días que hay entre 2017 y 2018, se le suman los 30 días correspondientes al mes de Enero del 2018 y los 3 primeros días de Febrero (ya que el instante segundo se situaba en el día 3 de dicho mes). A esto, finalmente, se le añaden las 4 horas especificadas para el segundo instante.
Con esta función ya tenemos la base para hacer nuestros cálculos. Sin embargo, podemos observar en el ejemplo, como los periodos completos de 24 horas, se han agrupado en días, cuando, nosotros lo que queremos es obtener el resultado en horas, minutos y segundos totales (sin que se produzca agrupaciones en otras medidas de tiempo). Es por ello, que sobre este tipo de resultado tendremos que seleccionar, solo, algunos campos, y realizar algunas modificaciones sobre el formato de modo que obtengamos el resultado deseado.
Para aplica el método “datetime.datetime” a nuestro caso concreto, lo primero que tenemos que hacer, naturalmente, es aplicar este método a los datos que el usuario introdujo, ya, en su momento. Lo cual haremos con una función que aplicaremos a cada uno de los instantes entre los que queremos efectuar el cálculo de la distancia, tomando como argumento, cada uno de los campos que componen la definición de cada instante (año, mes, día, hora, minuto y segundo). De ese modo las variables (a las que hemos dado el nombre de “D1” y “D2”) que emplearemos para efectuar el cálculo de la distancia quedaría definidas del modo siguiente:


En donde “D1” y “D2” son nuestros instantes de tiempo y “entering_date” la función que se encargará de definirlas mediante el método “datetime.datetime”, cuyo código sería el siguiente:
Como se puede apreciar, hemos creado una función que lo que va a hacer es tomar los datos (año, mes, día, hora, minuto y segundo) correspondientes a cada uno de nuestros instantes, y convertirlos a un formato que permita a la computadora trabajar con ellos como fechas.
A continuación, una vez que tenemos nuestros instantes de tiempo en el formato adecuado para fechas, es el momento de efectuar el cálculo de la diferencia entre ambas. En este punto debemos recordar que para nuestro propósito de expresar el resultado solo en horas, minutos y segundos, vamos a emplear como dato principal la parte del resultado correspondiente al número de días, por una parte, y al número de horas, por otra. Por ello debemos encontrar un modo de “separar” las partes del resultado que vamos a utilizar para hacer el cálculo final. El método que emplearemos lo podemos ilustrar con la siguiente secuencia de fases:
Partimos de una situación en la que hemos establecido los instantes “I1” e “I2” mediante “datetime.datetime”, cuya diferencia hemos almacenado en la variable “diferencia”, dándonos como resultado 576 días, 23 horas, 22 minutos y 7 segundos. Para poder trabajar con los días y las horas por separado, tenemos que empezar efectuando una primera división de los mismos:
Como se ve, hemos efectuado una primera separación del resultado “diferencia”, creando una lista con dos elementos: Por una parte, el referente al número de días (“576 days”) y por otra, el referente a las horas, minutos y segundos (“23:22:07”). Obsérvese que para ello hemos utilizado la función “.split” la cual ha separado usando, en este caso, la coma (“,”) como criterio separador (nótese también que el resultado “diferencia” ha tenido que ser pasado al formato “string” para realizar dicha operación). Sin embrago, con cada uno de estos elementos no podemos trabajar de un modo directo, todavía. Así el elemento referente a los días, contiene la palabra “days” con la cual no podremos trabajar. Es por ello que se hace necesario efectuar una división entre “576” y la palabra “days”.
Como se ve, con “sep2” hemos realizado una segunda separación (esta vez, usando como criterio separador, el espacio en blanco) dentro del primer elemento (posición 0 del la lista “sep1”), entre la parte numérica (aún en formato “string”) y la palabra “days”. Ahora solo nos falta “traducir” la parte numérica al formato de número entero:
Así hemos, finalmente, creado una variable (“num_dias”) la cual consiste en la conversión a entero (con “int”) del elemento ubicado en la posición 0, de la lista “sep2”. Con este conjunto de 4 pasos hemos logrado extraer la parte del output proporcionado por “datetime.datetime”, que nos interesa para hacer nuestro cálculo final del número de horas entre nuestros instantes. Este mismo proceso tendremos que hacer con la parte referente a las horas, minutos y segundos (de donde extraeremos el entero del número de horas).
Con estos cuatro pasos hemos desglosado la lógica empleada para la obtención del número de días y horas, a partir de la diferencia original. Ahora aplicaremos este método a nuestro programa. Sin embargo para no hacerlo excesivamente largo, vamos a resumir un poco el modo de hacerlo (incluyendo más de un paso en una misma línea).
Aquí tenemos como la variable (a la que he dado el nombre de “difer”) es igual a la separación, mediante la coma y en una lista, de las dos partes (la referente a los días y la referente a las horas, minutos y segundos) de las que consta el resultado obtenido de restar los dos instantes obtenidos a su vez, mediante “datetime.datetime”.
Finalmente hemos creado la variable “numero_dias”, la cual es igual a la separación (esta vez, mediante el espacio en blanco) del elemento, (ubicado en la posición 0), de la lista “difer” (recordemos que con esta última separación estábamos separando la parte numérica del string “days”) quedando, únicamente la parte numérica. En el caso de la variable “numero_horas” hemos “extraído” el elemento ubicado en la posición 0 (las horas) del elemento ubicado en la posición 1 (dedicado a las horas, minutos y segundos) de la lista “difer”.
Con este largo (y algo tortuoso) proceso hemos, finalmente, logrado obtener el número de días (almacenado en la variable “numero_dias”) y las horas “sobrantes” que no se habían integrado en el resultado correspondiente a los días, al ser menos de 24 (almacenadas en la variable “numero_horas”). Ahora ya solo nos queda hacer los cálculos finales del número total de horas, minutos y segundos, haciendo uso del mismo tipo de operaciones matemáticas y de corrección que vimos en el artículo anterior (cuando calculamos dichos parámetros para el caso en que nuestros instantes estuviesen ubicados en el mismo día). De ese modo tenemos:
Aquí hacemos, primero un cálculo “provisional” (al que llamamos “Total_horasProv”) del número total de horas (lo cual hacemos multiplicando el número de días por 24 y sumándole las horas “sobrantes” no incluidas en el resultado de los días). Sin embargo, este cálculo es aún insuficiente, ya que no tiene en cuenta los minutos y segundos de inicio y final, los cuales, quedaron establecidos en los datos de entrada “minuto1”-“segundo1” del primer instante y “minuto2”-“segundo2” del segundo instante. De ese modo, para calcular los minutos totales (“Total_minutos”), hemos multiplicado el número “provisional” de horas por 60, calculo al que a continuación le hemos restado el “minuto1” del instante primero, y le sumamos el minuto2, correspondiente al instante segundo (explico el motivo de estas operaciones, de forma más detallada, en mi anterior articulo). El mismo tipo de operación (solo que usando segundos) es el que realizamos para calcular los segundos totales (“Total_segundos”). Finalmente haremos el cálculo definitivo de las horas totales (“Total_horas”) utilizando, esta vez desde el principio, la cifra total de segundos, la cual, dividiremos por 3600 (los segundos de los que consta una hora) para obtener un resultado más exacto.
Hasta aquí hemos explicado el modo de calcular la cantidad total de horas, minutos y segundos, existentes entre dos instantes de tiempo, habiendo explicado, dicho calculo, para el caso de instantes pertenecientes a un mismo día (lo cual hicimos en el artículo anterior), y para el caso de instantes ubicados en días distintos (lo cual hemos hecho en el presente artículo).
Sin embargo aún nos queda un pequeño detalle por tratar, para ello vamos a volver a la parte del código referente a la introducción de los datos de partida, por parte del usuario de nuestro programa.

Como recordaremos, dividíamos, la introducción de datos en dos bloques (el relativo al primer suceso y el relativo a segundo suceso). De modo que el usuario tenía que introducir, primero, los datos relativos al año, mes, día, hora, minuto y segundo del suceso desde el cual se tenía que iniciar el cómputo del tiempo, y luego, debía introducir los datos del suceso en el que tenía que finalizar dicho cómputo (hasta aquí todo bien). Pero cabe preguntarnos ¿qué sucedería si el usuario (por el motivo que fuera) invirtiese dicho orden a la hora de introducir los datos (de modo que introdujese, para el primer instante, los datos correspondientes al segundo y viceversa)?
Para ilustrar el problema, vamos a realizar una pequeña simulación, ejecutando nuestro programa, en primer lugar, en el caso de que no hubiéramos previsto esa posible eventualidad.
Así empezaremos viendo el supuesto en el que el usuario introdujese los datos en el orden correcto. Así tenemos:



En nuestra simulación, hemos hecho que nuestro hipotético usuario introduzca los datos, referentes a los dos instantes, en el orden “correcto”, dándonos unos resultados coherentes y acertados para el cálculo de la distancia entre ambos instantes. A continuación, siguiendo con nuestra simulación, vamos a volver a introducir los mismos datos, pero de modo invertido.
Vemos que en esta ocasión, hemos obtenido unos resultados ligeramente distintos a los anteriores (lo cual no tiene sentido, debido a que el periodo de tiempo comprendido entre ambos instantes es siempre el mismo, con independencia del orden en el que se introduzcan los datos). Este fallo se ha debido, principalmente a los pares de datos, “minuto1”-“minuto2” por una parte y “segundo1”-“segundo2” por otra (esto es así, en la medida que en el momento de efectuarse el computo de los minutos y segundos totales, lo que se tenía que restar se ha sumado y lo que se tenía que sumar se ha restado). Por ello va a ser necesario que elaboremos un procedimiento que, ante tal caso, “reordene” los datos de tal modo que el resultado sea siendo el correcto.
Para ello, lo primero que tenemos que hacer es definir el tipo de situación en la que queremos que se produzca tal reordenación. Así, debajo del código orientado a la introducción de datos escribiremos lo siguiente:
Con esta larga condición, estamos contemplando todos los casos en los que el instante introducido en primer lugar, pueda ser posterior al introducido en segundo lugar (cuando, para que se calcule correctamente, debe ser al revés). Concretamente, estamos diciendo “si el -año2 es menor que el año1 o, siendo ambos años iguales, el mes1 es mayor que el mes2 o, siendo ambos meses iguales, el dia1 es mayor que el dia2  o, siendo ambos días iguales, la hora1 es mayor que la hora2 o siendo ambas horas iguales, el minuto1 es mayor que el minuto2 o, siendo ambos minutos iguales, el segundo1 es mayor que el segundo2, ejecutar este bloque de código”. Con esta larga sentencia hemos intentado prever todos los casos en los que el instante primero puede llegar a ser mayor que el instante segundo.
Como ya hemos dicho, en estos casos en los que los datos del instante primero son posteriores a los del instante segundo, lo que vamos a hacer es reordenar los datos introducidos por el usuario. Es decir, vamos a realizar un intercambio, de los datos referentes al año, mes, día, hora, minuto y segundo, entre los dos instantes. Para ello hemos de buscar un modo de hacerlo.
En la foto se muestra un intento fallido de realizar un intercambio de tales características, entre dos variables: “a” (cuyo valor es 123) y “b” (cuyo valor es 345). Una vez intentado el intercambio vemos que ambas variables han adoptado el valor de 345 (lo que supone que no hemos logrado el intercambio, ya que queríamos que “a” fuese 345 y “b” fuese 123). El fallo ha sido debido a que primero hemos hecho “a=b”, con lo que “a” ha dejado de ser 123 (para ser 345) y a continuación, cuando hemos querido que “b” fuese 123 (haciendo “b=a”) la operación ha salido mal ya “a” ya se había convertido en 345. Con lo que al final “a” y “b” han acabado siendo iguales a 345.
Así tenemos que lo que nos ha pasado, ha sido que se ha producido una “interferencia” en la asignación de valores, entre las dos variables. Por lo que se nos ha ocurrido otro modo de hacerlo:
En este segundo intento de intercambio, hemos creado dos variables “a1” y “b1” las cuales hemos hecho igual a “a” y “b”, respectivamente. Con ello lo que hemos logrado es dejar a salvo los dos valores, durante la acción de intercambio (se podría decir que las variables “a1” y “b1” han actuado como “puentes” entre “a” y “b”, evitando el problema de la interferencia de datos). Esta solución es la que nos ha permitido intercambiar con éxito (como puede verse) los valores de ambas variables.
Una vez que hemos ensayado este método (con éxito), es hora de aplicarlo al caso de nuestro programa.
De ese modo, conseguimos que se efectúe el intercambio de datos entre los dos instantes de tiempo. De modo que cuando se introduzcan los datos de forma que se asignen al instante primero los datos correspondientes al instante segundo (y viceversa) esta situación sea “detectada” por el programa el cual se encargará (por sí mismo) de corregir tal error.
Una vez aplicada la solución a nuestro programa, vamos a hacer el mismo simulacro que empleamos al principio y utilizando los mismos datos de entrada pero de modo invertido (poniendo la fecha posterior primero):

Ahora si comparamos estos resultados con los que obtuvimos en el primer simulacro, vemos que ahora si coinciden a la perfección. De este modo hemos conseguido que nuestro programa nos proporcione el mismo resultado con total independencia del orden en el que introduzcamos los instantes de tiempo entre los que queremos efectuar el cálculo.
Y aquí concluye mi exposición (que ha abarcado tres artículos) acerca del modo en que he creado esta calculadora capaz de calcular la distancia (en horas, minutos y segundos) entre dos instantes de tiempo cualesquiera y que espero que haya sido de vuestro agrado.
El código de los programas referidos en este artículo puede verse, en mi perfil de GitHub, en la siguiente dirección: https://github.com/antonioam82/Timer

Seguir Leyendo

Entradas más recientes

Powered by Blogger .