Periodismo de datos. Capítulo 01

Artículo publicado en utero.pe

Periodismo de datos. Capítulo 01

Esta es la primera entrega de un tema que puede ser de tu interés.

Clasificación

Según la Global Editors Network, el periodismo de datos se puede dividir en varias categorías, las cuales no son necesariamente excluyentes ya que usan métodos y protocolos comunes y tienden a ser complementarias.

Estas son las categorías:

Según Wikipedia, el periodismo investigativo de datos (lo que llamaremos «periodismo de datos» por simplicidad) es un proceso periodístico que se basa en el análisis y filtrado de grandes bases de datos con el propósito de crear una noticia periodística.

Me voy a concentrar en esta categoría ya que exiten varios reportajes de periodismo de datos en esta línea que han ganado concursos internacionales:

  • El diario La Nación (Argentina): Luego que el senado argentino publicara los reportes de sus gastos desde el 2004 en forma de archivos PDF, La Nación consiguió extraer, transformar, normalizar, tabular y estructurar los datos. Esto permitió encontrar gastos sospechosos e inusuales que terminaron en una investigación judicial del vice-presidente de la república Amado Boudou.
  • Los periodistas Giannina Segnini y Ernesto Rivera analizaron las declaraciones juradas de los ministros del gobierno de Laura Chinchilla, Costa Rica. Encontraron que los ministros habían subvalorado el valor de sus casas para pagar menos impuestos. Luego que la investigación viera la luz, 7 ministros se apresuraron en corregir el valor de sus inmuebles.

Obtención de datos lenta

Si bien una investigación periodística inicia con un dateo ya sea de un «garganta profunda» o un Edward Snowden, a veces es necesario obtener los datos de manera independiente. A veces los datos son facilitados por el datero pero muchas veces los datos están disponibles en los portales de instituciones estatales, registros públicos, etc.

A veces la obtención de datos es forzosamente lenta. Por ejemplo cuando uno pide datos en la SUNARP. Otras veces los datos se pueden obtener a por montones y rápidamente si se tiene la ayuda de un hacker (un hacker ético, claro está; si no tienes hacker, consíguete un geek). Por ejemplo el Ministerio de Justicia tiene en su web todas las resoluciones ministeriales emitidas en formato PDF. Bajarse cada PDF implicaría hacer una búsqueda en su portal, seleccionar la resolución del día que te quieres bajar y finalmente hacer click en «download». Durante el segundo gobierno aprista se emitieron 2,184 resoluciones y bajar todos estos PDFs manualmente, uno por uno, demoraría una eternidad.

geek promedio

geek promedio

Obtención de datos veloz

Pero lo bueno es que los archivos están almacenados de manera consistente. El nombre de cada archivo PDF consiste en la fecha en que se emitió la resolución (ddmmyy, osea día, mes y año):

http://spij.minjus.gob.pe/Normas/textos/ddmmyyT.pdf

Este trabajo es demasiado fácil para un hacker ético. Solo basta escribir un programita de 9 líneas de código para bajarse TODAS las resoluciones:


## Lista de links de las Normas del Ministerio de Justicia del 2006 al 2011
# Bajar fechas 00-01-06 hasta 39-09-09
curl -o '#1#2-0#3-0#4.pdf' 'http://spij.minjus.gob.pe/Normas/textos/%5B0-1%5D%5B0-1%5D0%5B1-1%5D0%5B6-6%5DT.pdf'
sleep $[ ( RANDOM % 10 ) + 1]m
# Bajar fechas 00-01-06 hasta 39-09-09
curl -o '#1#2-0#3-0#4.pdf' 'http://spij.minjus.gob.pe/Normas/textos/%5B0-3%5D%5B0-9%5D0%5B1-9%5D0%5B6-9%5DT.pdf'
sleep $[ ( RANDOM % 10 ) + 1]m
# Bajar fechas 00-01-10 hasta 39-09-11
curl -o '#1#2-0#3-#4.pdf' 'http://spij.minjus.gob.pe/Normas/textos/%5B0-3%5D%5B0-9%5D0%5B1-9%5D%5B10-11%5DT.pdf'
sleep $[ ( RANDOM % 10 ) + 1]m
# Bajar fechas 00-10-06 hasta 39-12-09
curl -o '#1#2-1#3-0#4.pdf' 'http://spij.minjus.gob.pe/Normas/textos/%5B0-3%5D%5B0-9%5D1%5B0-2%5D0%5B6-9%5DT.pdf'
sleep $[ ( RANDOM % 10 ) + 1]m
# Bajar fechas 00-10-10 hasta 39-12-11
curl -o '#1#2-1#3-#4.pdf' 'http://spij.minjus.gob.pe/Normas/textos/%5B0-3%5D%5B0-9%5D1%5B0-2%5D%5B10-11%5DT.pdf'

Una vez que el programa empieza a correr irá bajando cada PDF, un por uno, ya que el nombre de los archivos se puede constuir usando las fechas de un calendario. Este programa terminará su trabajo en unas cuantas horas sin necesidad que el periodista y/o hacker realicen actividad manual alguna.

Los geeks tienen muchas herramientas open-source disponibles para realizar sus actividades. Una herramienta que se usa mucho para descargar contenido desde las web se conoce como curl. Si se usa correctamente, este programa puede aparentar ser un usuario humano, ya que puede suministar nombre de usuario y contraseña a las páginas que lo requieran, puede lidiar con cookies, usar certificados para autenticación, usar proxies y muchas cosas más. Es algo así como la navaja suiza de los geeks para asuntos de descarga de datos.

Como ves, es muy ventajoso para un periodista de investigación estar asociado a uno o más hackers, o geeks, o nerds. Uno de los aportes principales de los hackers éticos al periodismo de datos es la rapidez. Al aprovechar de sus habilidades tecnológicas es posible acelerar la investigación sobre todo cuando hay que realizar actividades repetitivas. En el mundo digital, las actividades repetitivas deben ser ejecutadas por computadoras, no por humanos. Las computadoras son buenas para ejecutar tareas repetitivas ya que son infinitamente más rápidas que un humano.

Esas tareas repetitivas pueden ser automatizadas y ejecutadas por los hackers y sus computadoras. La labor del periodista es otra, es analizar qué datos son importantes de ser cosechados, qué otro tipo de datos deben ser asociados con el fin de obtener una historia. La labor analítica y de pensamiento crítico debe ser realizada por el periodista. Para esto es de vital importancia la experiencia e intuición de periodista.

TL;DR

Amigo, amiga periodista, si necesitas bajar cientos, miles de PDFs, imágenes o páginas de un sitio web, contáctate con tu geek más cercano. Hay altas probabilidades que el geek pueda usar sus habilidades para bajar lo que necesitas en un santiamén.

Cómo vota Perú en el TPP («tratado anti-internet»)?

Hace un par de semanas, el portal Wikileaks publicó el documento secreto correspondiente al borrador de las negociaciones del capítulo de propiedad intelectual del TPP (Trans-Pacific Partnership).

Ya han habido varias alertas acerca de las implicancias de estas negociaciones (una introducción al tema aquí). Además de ser negociaciones que se realizan de manera secreta, preocupa que esté en riesgo el libre acceso a Internet y el componente de propiedad intelectual que pueda dificultar el acceso a medicamentos.

Se supone que este tratado está apunto de ser firmado por el Perú antes de fin de año y gracias a Wikileaks recién podemos darnos cuenta de lo que realmente se ha estado negociando a puerta cerrada.

El documento filtrado al público tiene 96 páginas (si se bajan el PDF) y está disponible aquí. A primera vista se pueden ver los temas que se han discutido y la manera de cómo ha votado cada país. Algunas propuestas parecen provenir de ciertos países mientras que otros votan a favor o se oponen (ver figura 1).

Figura 1. Algunos países votan a favor, otros se oponen.

Figura 1. Algunos países votan a favor, otros se oponen.

Podemos dar una lectura a todo el PDF para enterarnos «cómo es la cosa». Pero, siendo este un blog nerd, podemos hacer un «data-mining» rudimentario para rápidamente poder averiguar algunos detalles:

  1. Cuántas veces Perú vota como país de manera opuesta a Estados Unidos?
  2. Cuántas veces Perú vota igual que Estados Unidos?
  3. Podemos averiguar como vota algún país vecino? digamos Chile, vota de manera diferente a Perú?
  4. Chile se opone o vota de manera similar que Estados Unidos?
  5. En qué puntos específicos hay discrepancias en las votaciones

Podría ponerme a contar la votaciones una por una pero me iba a demorar una enternidad. Entonces escribí un script in Python para hacer este data-mining (script completo en la sección geek al final de este post).

Lo bueno es que Wikileaks publicó el documento como PDF conteniendo texto (no como imágenes). Entonces, es bien fácil convertir el PDF a TXT y proceder con el minado de datos.

pdftotext Wikileaks-secret-TPP-treaty-IP-chapter.pdf texto.txt

Mi script funciona de la siguiente manera:

  1. Lee el documento TXT línea por línea
  2. Si encuentra una línea que contenga la palabra Article se pone alerta y se fija si hay alguna línea con las iniciales de los países PE, CL, US (osea Perú, Chile y Estados Unidos).
  3. La línea de texto que indica la votación tiene un patrón consistente: países oppose/propose más países.
  4. Entonces el script divide la lista de países en dos bandos, los que están a la derecha e izquierda de las palabras clave oppose/propose
  5. Una vez divididos los bandos, solo es cuestión de contar cuántas veces se repiten las iniciales y llevar la cuenta el bando.

Estos son los resultados:

Conteo de votos de PE versus US
propose_together 26
oppose_together 6
oppose_each_other 23

---------------------

Conteo de votos de PE versus CL
propose_together 36
oppose_together 22
oppose_each_other 8

---------------------

Conteo de votos de US versus CL
propose_together 16
oppose_together 2
oppose_each_other 32

Se supone que ambos países, Perú y Chile, tenían la intención hacer propuestas alternativas a las que figuran en el TPP (ver aquí y aquí).

Pero al parecer, esto puede haber quedado en intenciones, al menos viendo la manera cómo ha estado votando Perú.

Perú ha votado igualito que Estados Unidos 32 veces y se ha opuesto (votado diferente) sólo 23 veces. Mientras que Chile ha votado igual que EEUU solo 18 veces y se ha opuesto 32 veces.

Parece que Chile se opone mucho a las propuestas apoyadas por EEUU mientras que Perú vota en tándem. Mi script me dice que:

Chile se opone a US y PE 9 veces

y se opone en estos artículos del TPP:

* Article QQ.C.2: {Collective and Certification Marks}
    [US/PE/MX41/SG propose; AU/NZ/ VN/BN/MY/CL/CA oppose: 2. Pursuant to
* Article QQ.D.11: [CL/SG/BN/VN/MX propose82; AU/PE/US/NZ/CA/JP oppose:
* Article QQ.D.12: {Homonymous Geographical Indications}
    [NZ/CL/VN/MY/BN/SG/MX propose84; PE/US/AU oppose: 1. Each Party may
    [CL propose; AU/US/PE/NZ/VN/SG/MY/BN/MX/CA/JP oppose: 2. The Parties
    [CL/SG/BN/MX propose; AU/PE/US/NZ/CA/JP oppose: Annex […] Lists of
* Article QQ.E.9: [US/PE/AU propose; 101 CL/VN/MY/BN/NZ/CA/SG/MX oppose:
* Article QQ.H.7: {Criminal Procedures and Remedies / Criminal Enforcement}
    2. [US/AU/SG/PE propose; CL/VN/MY/NZ/CA/BN/MX oppose: Willful
* Article QQ.I.1:267 {Internet Service Provider Liability}
    280 [US/PE/SG/AU propose; CL/NZ/VN oppose: A Party may request consultations with the other Parties to

Ahora que tenemos una idea a ojo de buen cubero cómo van las votaciones de Perú, Chile y EEUU, además de los temas potencialmente picantes. Podemos leer mejor el documento filtrado por Wikileaks.

** Spoiler ** (Uno de esos temas tiene que ver con la denominación de origen del Pisco. Chile propone, Perú y EEUU se oponen).

PD. este post se inció a sugerencia de un tuitero amixer.

Sección geek

El script corre de la siguiente manera:

python leeme_votaciones.py texto.txt


import re
import sys
f = open(sys.argv[1].strip(), "r")
lines = f.readlines()
f.close()
def clean_line(line):
propose = ""
oppose = ""
if "propose" in line or "oppose" in line:
try:
propose = re.search("(([A-Z]{2}[0-9]*\/*\s*)+\spropose)", line).groups()[0]
propose = re.sub("[0-9]+", "", propose)
except:
pass
try:
oppose = re.search("(([A-Z]{2}[0-9]*\/*\s*)+\soppose)", line).groups()[0]
oppose = re.sub("[0-9]+", "", oppose)
except:
pass
return [propose, oppose]
def count_countries(pais1, pais2, lines):
propose_together = 0
oppose_together = 0
oppose_each_other = 0
for line in lines:
# propose together
if pais1 in line[0] and pais2 in line[0]:
propose_together += 1
elif pais1 in line[1] and pais2 in line[1]:
oppose_together += 1
elif pais1 in line[0] and pais2 in line[1]:
oppose_each_other += 1
elif pais2 in line[0] and pais1 in line[1]:
oppose_each_other += 1
print "Conteo de votos de %s versus %s" % (pais1, pais2,)
print "propose_together %i" % propose_together
print "oppose_together %i" % oppose_together
print "oppose_each_other %i" % oppose_each_other
new_lines = []
chile_se_opone = 0
for line in lines:
line = re.sub("\s+", " ", line)
line = line.strip()
if line.startswith("Article"):
article_line = line
if "PE" in line and "CL" in line and "US" in line:
tmp = clean_line(line)
if "PE" in tmp[0] and "US" in tmp[0] and "CL" in tmp[1]:
chile_se_opone = chile_se_opone + 1
print "* %s" % article_line
print line
elif "PE" in tmp[1] and "US" in tmp[1] and "CL" in tmp[0]:
chile_se_opone = chile_se_opone + 1
print "* %s" % article_line
print line
if line.startswith("["):
new_lines.append(clean_line(line))
print "Chile se opone a US y PE %i veces" % chile_se_opone
print "Hay %i lineas de informacion" % len(new_lines)
count_countries("PE", "US", new_lines)
print "\n———————\n"
count_countries("PE", "CL", new_lines)
print "\n———————\n"
count_countries("US", "CL", new_lines)