Open Data API

Este proyecto trata de poner en práctica lo aprendido en el curso de Google App Engine. Actualmente existe una iniciativa Open Data llevada a cabo por el Gobierno Vasco (http://opendata.euskadi.net/w79-home/es) que ofrece cierta información pública. El problema de este servicio es que ofrece archivos estáticos, lo cual lo hace poco útil de cara a integrarlo en aplicaciones sencillas: para terminales móviles, widgets en javascript… 

Este proyecto intenta crear un servicio que permita acceder a dicha información a través de una API que permita explotar la información de forma más 

sencilla y a través de peticiones HTTP.

Para cualquier duda añadir un comentario o poneos en contacto conmigo: Iker Perez de Albeniz

PASO 1: Peticiones HTTP mediante GAE

Para poder explotar los datos lo primero que tenemos que hacer es conectarnos a la fuente de los datos. En nuestro caso, analizando la pagine de OpenData del GV, vemos que pueden ser, XMLs, ZIPs, RDFs... por tanto lo primero que tenemos que ver es como acceder a esa información, para luego tratarla y almacenarla en local. La forma que tenemos de acceder a esa información es estableciendo una conexión HTTP con dicho archivo. A continuación es muestran diferentes formas de establecer conexiones HTTP en GAE.



Mediente urlfetch

Importamos la librería urlfectch y llamamos a la función urlfetch.fetc() que realiza una conexión HTTP con la pagina y nos devuelve su código html. El objeto result, además del el cuerpo de la pagina, tiene una propiedad status_code que contiene el resultado de la petición HTTP: 200 si es todo correcto, 400 si existe algún error.. etc

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.api import urlfetch


class MainHandler(webapp.RequestHandler):
    def get(self):
        url = "http://www.google.com/"
        result = urlfetch.fetch(url)
        if result.status_code == 200:
            self.response.out.write(result.content)


def main():
    application = webapp.WSGIApplication([('/', MainHandler)],
                                         debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()

Mediente urllib2
Si no queremos usar la librería urlfetch que nos ofrece google, podemos usar la librería urlib2 que es la utilizada comúnmente en python. Con lo que el ejemplo anterior nos quedaría asi:
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
import urllib2


class MainHandler(webapp.RequestHandler):
    def get(self):
        url = "http://www.google.com/"
        try:
            result = urllib2.urlopen(url)
            self.response.out.write(result)
        except urllib2.URLError, e:
            self.response.out.write(e)

def main():
    application = webapp.WSGIApplication([('/', MainHandler)],
                                         debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()

Apéndice: Para los que desarrollan detrás de un proxy
Si desarrolláis detrás de un proxy,  seguramente el código anterior os de error, por tanto si queréis hacer pruebas deberéis usar la librería urlliburlib2 y configurar un proxy para poder establecer la conexión.
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
import urllib2
import urllib

class MainHandler(webapp.RequestHandler):
    def get(self):
        url = "http://www.google.com/"
        try:
            proxy_info = {
                'user' : 'user',
                'pass' : 'pass',
                'host' : "192.168.0.1",
                'port' : 80
                }

            proxy_support = urllib2.ProxyHandler({"http" : "http://%(user)s:%(pass)s@%(host)s:%(port)d" % proxy_info})
            opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
            urllib2.install_opener(opener)

            request = urllib2.Request(url)
            opener2 = urllib2.build_opener()
            request.get_full_url()
            result = opener2.open(request).read()
            self.response.out.write(result)
        except urllib2.URLError, e:
            self.response.out.write(e)


def main():
    application = webapp.WSGIApplication([('/', MainHandler)],
                                         debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()


PASO 2: Recuperación de datos y tratamiento de los XML

Para obtener los datos, podemos utilizar cualquiera de los métodos que hemos mostrado en el PASO 1. En mi caso voy a utilizar urlib2 para realizar la conexión ya que en caso de necesitar hacer algo “diferente” existe mucha documentación al respecto. Por nato vamos a empezar con un ejemplo real:

Vamos a descargar el XML que podemos obtener de http://www.trafikoa.net/servicios/IncidenciasTDT/IncidenciasTrafikoTDT y que nos da un listado de las incidencias de trafico. Por tanto una vez obtenido el XML deberemos recorrerlo para así almacenarlo en una base de datos que crearemos. En el PASO 2, por ahora solo nos vamos a centrar en como recoger el XML y tratarlo.

Existen diferentes librerías para manejar XMLs, pero como comenté en el curso, es posible usar librerías de terceros, si estas están puramente en python y usan librerías que están disponibles en GAE. En mi caso he buscado una librería que me convierta de XML a una estructura de datos y me he encontrado esta librería:

http://code.activestate.com/recipes/534109-xml-to-python-data-structure/

Es una librería que usa las librerías re (regula expresions) y xml.sax para recorrer XMLs y generar una estructura de datos. Descargo el script y lo renombro como xml2obj.py y lo guardo en el mismo directorio donde esta el main.py.

Para usar esta librería en main.py solo tendré que hacer from <nombrearchivopy> import <clasesaimportar>

from xml2obj import xml2obj

En nuestro caso el xml es:

	
<raiz>
	<incidencia>
		<tipo>Puertos de montaña</tipo> 
		<autonomia>Euskadi</autonomia> 
		<provincia>Alava-Araba</provincia> 
		<matricula>VI</matricula> 
		<causa>Desconocida</causa> 
		<poblacion /> 
		<fechahora_ini>2011-02-21 07:02:39.734</fechahora_ini> 
		<nivel>T: Abierto C: Abierto A: Abierto</nivel> 
		<carretera>A-3600</carretera> 
		<pk_inicial>16.60</pk_inicial> 
		 <pk_final>16.60</pk_final> 
		<sentido>ALTUBE</sentido> 
		<nombre>AIURDIN</nombre>
	</incidencia>
	..
	..
	..
</raiz>

Por tanto tendremos un a estructura raiz con un array incidencia de n elementos, que finalmente cada elemento tendrá las propiedades tipo,atonomia... etc. En nuestro ejemplo vamos a recorrer el array de incidencias y convertir el string fechahora_ini en una estructura datetime y sacarlo por pantalla.

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
import urllib2
from xml2obj import xml2obj
from datetime import datetime, date, time


class MainHandler(webapp.RequestHandler):
    def get(self):
        url = "http://www.trafikoa.net/servicios/IncidenciasTDT/IncidenciasTrafikoTDT"
        try:
            result = urllib2.urlopen(url)
            raiz = xml2obj(result)
            for incidencia in raiz.incidencia:
                struct_time = datetime.strptime(incidencia.fechahora_ini.split(".")[0],"%Y-%m-%d %H:%M:%S")
                self.response.out.write(struct_time)
                
        except urllib2.URLError, e:
            self.response.out.write(e)

def main():
    application = webapp.WSGIApplication([('/', MainHandler)],
                                         debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()

PASO 3: Conversion de los datos a otras estructuras

Antes de ponernos con el almacenamiento de datos y su posterior análisis vamos a suponer que nuestros servicio GAE solo lo vamos a usar para adaptar los datos a un formato mas manejable por una aplicación cliente. En nuestro caso vamos a convertir los datos a JSON y a PROTOCOL BUFFERS.

JSON

Convertir la estructura raiz a JSON es muy sencillo, tan solo tenemos que importar la librería simplejson:

from django.utils import simplejson

Y utilizar la función dump para convertir los datos:

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
import urllib2
from xml2obj import xml2obj
from datetime import datetime, date, time
from django.utils import simplejson

class MainHandler(webapp.RequestHandler):
    def get(self):
        url = "http://www.trafikoa.net/servicios/IncidenciasTDT/IncidenciasTrafikoTDT"
        try:
            result = urllib2.urlopen(url)
            raiz = xml2obj(result)
            self.response.out.write(simplejson.dump(raiz ))  
        except urllib2.URLError, e:
            self.response.out.write(e)

def main():
    application = webapp.WSGIApplication([('/', MainHandler)],
                                         debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()

PROTOCOL BUFFERS

Para usar protocol buffers, primero descargaremos el compilador protoc para poder generar nuestras clases proto. Como vamos a interactuar con GAE y hay algunos problemas con las versiones de las librerías protobuf vamos u usar la versión 2.0.2 de protocol buffers ya que existe un hack para GAE que funciona con la version 2.0.2.

Compilador protoc: windows y otros

Hack protobuf gae: http://protobuf-gae-hack.googlecode.com/files/googlepb-2.0.2-hack.tar.bz2

Lo primero que tenemos que hacer es generar nuestro esquema proto equivalente al xml que obtenemos de OpenData euskadi:

package OpenData;

message Trafico{
	message Incidencia{
		optional string  tipo = 1;
		optional string  autonomia = 2;
		optional string provincia = 3;
		optional string matricula = 4;
		optional string causa = 5;
		optional string poblacion  = 6;
		optional string fechahora_ini = 7;
		optional string nivel = 8;
		optional string carretera = 9;
		optional float pk_inicial = 10;
		optional float pk_final = 11;
		optional string sentido = 12;
		optional string nombre = 13;
	}
	repeated Incidencia incidencia = 1;
		
}

He definido una estructura Trafico, con N (repeated) elementos Incidencia. Cada elemento Incidencia tiene diferentes propiedades que he definido como optional ya que en algunos casos no esta definidas. Para mas información sobre como definir vuestro prototipo podéis consultarlo aquí.

Guardamos le estructura como trafico.proto y la compilamos con el siguiente comando:

protoc.exe --proto_path=src --python_out=out src/trafico.proto

NOTA: Hemos guardado los proto en un directorio src y le indicamos a protoc que genere las classes python en el directorio out.
NOTA2: Para generar la clase de Java (para el cliente Android) en vez de --python_out indicariamos --java_out.

Ya tenemos nuestra clase trafico_pb2.py que deberemos usar para generar las estructuras de datos protobuff.

[CONTINUARA.....]

Comments