Twitter: conversaciones gráficas con Python (IV)

Dentro de Twitter: estructuras de datos

Esta es la cuarta entrada de este blog, dedicado a usar el API de Twitter y las librerías de Python para generar una imagen de la conversación que tenga un formato gráfico:

nolla

Tengo que recuperar los tuits con el API de Twitter y usar Python para filtrar la información, crear una lista ordenada y generar un gráfico con los nodos.

En esta entrada examinaremos la estructura de los datos que devuelve el API de Twitter, y cómo los manipulamos con Python. Supongo cierta familiaridad con Python; en caso contrario, hay todo tipo de tutoriales en la red y seguro que alguno se adapta a tu nivel. En caso de duda, escribe un comentario o un tuit e intentaré ayudarte.

La línea de tiempo (según el API de Twitter)

Si llegaste a ejecutar el último comando que te proponía en el post anterior, tuviste que recibir un gran flujo de datos parecido al que ves a continuación(vuelvo a mostrar todos los comandos. Recuerda que las claves que uses han de ser las tuyas):

>>> import twitter
>>> token = "93826987-b17gKPaq4a5sNtbu3PfXPHdBcCm4ff533PSlMKGMV"
>>> token_key = "wZZ5VUOPzOy1QABHiA4gUQEfrRoMxn4qlewNTJ1yxhZem"
>>> con_secret = "gsIT85KKCwn96YZdEm5i3uGtq"
>>> con_secret_key = "SvWhbZNUZIx9Kz3sMuMaditk3phMyhw2tLHrrxXLX7SaYm9Ntm"
>>> auth = twitter.OAuth(token, token_key, con_secret, con_secret_key)
>>> t = twitter.Twitter(auth=auth)
>>> t.statuses.home_timeline()
 [{'retweet_count': 1, 'coordinates': None, 'retweeted': False, 'id_str': '501334102357073921', 'favorite_count': 1, 'favorited': False, 'id': 501334102357073921, 'user': {'profile_banner_url': 'https://pbs.twimg.com/profile_banners/100731315/1401451804', 'listed_count': 5080, 'profile_image_url_https': 'https://pbs.twimg.com/profile_images/1563598007/rt_sepa_mas_normal.png', 'verified': True, 'time_zone': 'Moscow', 'profile_link_color': '5CBA1D', 'url': 'http://t.co/D4csDmdhn8', 'created_at': 'Thu Dec 31 09:33:16 +0000 2009', 'statuses_count': 175603, 'contributors_enabled': False, 'profile_sidebar_fill_color': '042107', 'profile_background_color': '005E15', 'profile_text_color': 'FFFFFF', 'id': 100731315, 'is_translation_enabled': False,  ... (abreviado)

Esta información está en un formato llamado JSON. JSON permite guardar datos estructurados de una forma muy sencilla. Se pueden usar dos tipos de estructuras:

  • Las listas. Son colecciones de valores separados por comas y entre paréntesis cuadrados [ ]. Esta lista podría ser la información de inventario de un producto:
['2014-08-18', 250.99, 'Reloj de oro', 9, 'En stock']
  • Los diccionarios. Los dicionarios son colecciones de parejas de valores, del estilo código-valor. La sintaxis usa corchetes { } para definir el diccionario y los dos puntos para separar la clave del valor. La lista anterior podría reescribirse como diccionario, si precedemos cada elemento con el nombre del concepto que representa:
{'fecha': '2014-08-18', 
 'precio': 250.99, 
 'descripcion: 'Reloj de oro', 
 'cantidad': 9, 
 'estado': 'En stock'}

El problema con el JSON que hemos recibido es que no es muy legible. A continuación he reorganizado el texto para que se vea más claro.

[ {'retweet_count': 1, 
   'coordinates': None, 
   'retweeted': False, 
   'id_str': '501334102357073921', 
   'favorite_count': 1, 
   'favorited': False, 
   'id': 501334102357073921, 
   'user': {
       'profile_banner_url': 'https://pbs.twimg.com/profile_banners/100731315/1401451804',
       'listed_count': 5080, 
       'profile_image_url_https': 'https://pbs.twimg.com/profile_images/1563598007/rt_sepa_mas_normal.png' ...

Puesto que empieza con un paréntesis cuadrado, lo primero que debería quedar claro es que esta secuencia contiene una lista con uno o más elementos.

El segundo signo es un corchete, por tanto el primer elemento de la lista es un diccionario. Las claves de este diccionario son: ‘retweet_count’, ‘coordinates’, ‘retweeted’… Algunas de estas claves tienen un significado bastante explícito para el usuario de Twitter, mientras que hay otros más oscuros. Una de las cosas que tendremos que hacer es examinar estas estructuras para comprender cómo modelaron las cosas los ingenieros de Twitter.

La facilidad de JSON para anidar objetos dentro de otros objetos es lo que lo hace tan práctico para estructurar datos complejos. Además, se necesita muy poco formato extra para cumplir con la sintaxis, es un formato muy económico. Esto es una ventaja cuando se trata de mover datos por la red.

Listas y diccionarios en Python

Una buena noticia: Python es idéntico a JSON en lo que se refiere a su sintaxis de listas y diccionarios. De hecho, listas y diccionarios son tipos de datos nativos, igual que los números o las cadenas. Crear variables de tipo lista o diccionario es fácil:

>>> mi_lista = ['Diego Buendia', 'dbuendiab', '2009-12-01']
>>> print(mi_lista)
['Diego Buendia', 'dbuendiab', '2009-12-01']
>>> mi_diccionario = {'name': 'Diego Buendia', 'screen_name': 'dbuendiab', 'created_at': '2009-12-01'}
>>> print(mi_diccionario)
{'name': 'Diego Buendia', 'screen_name': 'dbuendiab', 'created_at': '2009-12-01'}

Explorando listas y diccionarios

El acceso a los elementos de listas y diccionarios se hace mediante una sintaxis de paréntesis cuadrados: variable[<indice-o-clave>].

Las listas indexan sus elementos por números consecutivos, empezando por el cero, y los diccionarios lo hacen mediante las claves que lo componen.

>>> mi_lista[0]
 'Diego Buendia'

Los diccionarios, en cambio, lo hacen por claves:

>>> mi_diccionario['created_at']
 '2009-12-01'

Una forma muy de Python de averigüar el tipo de dato en una variable es asumir que es una lista. Si no da error, es que lo era:

>>> mi_diccionario[0]
 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 KeyError: 0

Un error KeyError significa que esa clave no existe en el diccionario. Desafortunadamente, puede que estemos tratando realmente con un diccionario:

>>> d = {1: 'Francia', 2: 'Italia', 5: 'Alemania'}
>>> d[2]
 'Italia'
>>> d[0]
 Traceback (most recent call last):
 File "", line 1, in 
 KeyError: 0

¿Qué diferencia hay entre este diccionario y una lista? Bueno, la diferencia está en que los índices de una lista empiezan forzosamente por cero, mientras que el diccionario podría tenerlos arbitrarios (como el de Alemania, que es 5).

¿Hay una forma de saber si una variable contiene un diccionario o una lista sin examinarla? Pues sí. La función isinstance(<objeto>, <tipo>):

 >>> isinstance(d, list)
 False
 >>> isinstance(d, dict)
 True

También nos interesa saber cómo recuperar todas las claves, o valores, de un diccionario:

 >>> d.keys()
 [1, 2, 5]
 >>> d.values()
 ['Francia', 'Italia', 'Alemania']

Observarás que estas funciones tienen una sintaxis diferente a len() o dir(). En éstas, el argumento va dentro de los paréntesis, y en cambio keys() y values() están asociadas al objeto d mediante la notación ‘.’ (punto). Todo en Python deriva de una clase Object, y las clases son estructuras que contienen atributos (digamos variables asociadas) y métodos (funciones asociadas). La notación ‘.’ explicita esta dependencia.

Con estos recursos, es posible navegar y conocer poco a poco las estructuras de nuestro programa. Lo peor que puede pasar, y que de hecho pasa, es que al intentar mostrar un elemento inadvertidamente resulte ser un elemento de tamaño descomunal y se produzca un pantallazo continuo de datos. La solución es teclear pronto un <Ctrl>-C para interrumpir la salida.

La estructura del objeto Tweet de Twitter

Para terminar, vamos a recuperar el código con el que iniciábamos el presente post, pero ahora, para poder examinarlo con calma, guardaremos el timeline recuperado en una variable:

>>> import twitter
>>> token = "93826987-b17gKPaq4a5sNtbu3PfXPHdBcCm4ff533PSlMKGMV"
>>> token_key = "wZZ5VUOPzOy1QABHiA4gUQEfrRoMxn4qlewNTJ1yxhZem"
>>> con_secret = "gsIT85KKCwn96YZdEm5i3uGtq"
>>> con_secret_key = "SvWhbZNUZIx9Kz3sMuMaditk3phMyhw2tLHrrxXLX7SaYm9Ntm"
>>> auth = twitter.OAuth(token, token_key, con_secret, con_secret_key)
>>> t = twitter.Twitter(auth=auth)
>>> tl = t.statuses.home_timeline()

Ya vimos que la variable tl tiene aspecto de lista. Puedes comprobar que tiene 20 elementos (es lo que devuelve el API de Twitter por defecto, si no se le dice nada al respecto).

 >>> len(tl)
 20

Extrae el primer elemento a una variable, para examinarla con más calma. Recordarás que los elementos de esta lista son diccionarios, que corresponden a tuits. Puedes mirar las claves del diccionario con keys(): lo que ves son los atributos (públicos) de un tuit, según el API de Twitter. Uso el bucle for de Python (for elem in lista:) para presentar los atributos con más pulcritud.

 >>> tuit = tl[0]
 >>> for atributo in tuit.keys():
 ...     print(atributo)
 ...
 retweeted
 in_reply_to_status_id
 favorite_count
 entities
 possibly_sensitive
 contributors
 in_reply_to_user_id
 geo
 favorited
 truncated
 in_reply_to_screen_name
 retweet_count
 id
 text
 id_str
 in_reply_to_status_id_str
 lang
 source
 retweeted_status
 place
 in_reply_to_user_id_str
 extended_entities
 user
 created_at
 coordinates

He marcado en color aquellos campos que en mi opinión son más relevantes, para comentarlos a continuación. Si quieres conocer con más detalle esta y otras estructuras, puedes consultar la documentación de objetos del API de Twitter.

Campos esenciales
  • id: campo numérico que identifica el tuit de una forma única. Este (y otros campos numéricos) tiene una versión en formato cadena (id_str) para evitar problemas en aquellos lenguajes que tienen problemas con los números muy grandes. No es el caso de Python, así que en adelante ignoraré las versiones _str.
  • text: el contenido del tuit. O sea, lo que lee la gente, propiamente dicho.
  • created_at: la fecha en que se publicó el tuit.
  •  user: user es un diccionario que corresponde al objeto User de Twitter. Contiene toda la información del usuario
Campos reply

Existen siempre (menos el último), pero sólo se rellenan cuando el tuit es contestación a otro previo. En caso contrario, contienen el valor None. Si es tuit de contestación, estos campos se rellenan con información del tuit al que ha contestado.

  • in_reply_to_status_id: es el id del tuit original al que contestamos.
  • in_reply_to_user_id: el id del usuario que creó el tuit original.
  • in_reply_to_screen_name: el nombre Twitter de dicho usuario.
  • retweeted_status: la copia del tuit original. Este campo no existe si el tuit no es contestación de otro.
Campos estadísticos

Corresponden al número de retweets y favorites que tenga el tuit.

  • favorite_count: número de veces que fue marcado como favorito.
  • retweet_count: número de veces que fue retuiteado.

La estructura del  objeto User de Twitter

Selecciona el elemento user del tuit y guárdalo en una variable. Busca las keys(), como antes:

>>> user = tuit['user']
>>> for atributo in user:
...     print(atributo)
...
contributors_enabled
profile_background_tile
url
profile_link_color
is_translation_enabled
screen_name
entities
time_zone
utc_offset
geo_enabled
profile_background_image_url_https
listed_count
name
protected
verified
location
profile_background_image_url
profile_sidebar_border_color
profile_text_color
default_profile_image
id
profile_use_background_image
follow_request_sent
following
profile_image_url_https
lang
profile_sidebar_fill_color
is_translator
id_str
description
statuses_count
favourites_count
friends_count
notifications
followers_count
profile_background_color
profile_image_url
profile_banner_url
created_at
default_profile
Campos esenciales
  • id: código del usuario. Su DNI en Twitter.
  • screen_name: el nombre que aparece en las menciones.
  • name: el nombre que aparece en grande junto a la foto.
  • created_at: fecha en la que el usuario se dio de alta en Twitter. Este campo no se ve en la web, a pesar de que es muy interesante (en mi opinión).
  • description: texto que el usuario publica de sí mismo y que aparece en su perfil.
Campos estadísticos
  • statuses_count: número de tuits publicados por este usuario.
  • favourites_count: número de tuits marcados como favoritos por este usuario.
  • friends_count: número de personas a las que este usuario sigue.
  • followers_count: número de personas que siguen a este usuario.
  • listed_count: número de listas en las que está presente este usuario.

Conclusión

Con esto termino el post de hoy. Resumo su contenido:

  • Hemos examinado lo que nos devuelve el API de Twitter (en un caso concreto).
  • Hemos hablado del formato JSON.
  • Hemos visto las listas y diccionarios de Python, y cómo explorarlos.
  • Hemos analizado las estructuras Tweet y User.

Ahora ya tienes unas nociones de las estructuras de datos que tiene Twitter, y de cómo se manejan desde Python. El trabajo del próximo día será estudiar el API de Twitter y su uso a través de la librería Python correspondiente. Hasta mañana.

Ah! Me gustaría pedirte tu opinión sobre este blog. En caso de que tengas alguna sugerencia o crítica me gustaría mucho conocerla.

Anuncios

2 comentarios en “Twitter: conversaciones gráficas con Python (IV)

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s