Andaba yo buscando una fuente de datos para una visualización que quiero montar y me veía, como en aquella gráfica que hice de películas de James Bond, tirando de tablas en la Wikipedia y extrayendo información utilizando alguna librería de scraping.

El caso es que tenía en la cabeza desde hace tiempo echarle un ojo a Wikidata. Wikidata es un proyecto similar a la Wikipedia, pero en lugar de tener entradas en una enciclopedia tiene datos que puede leer fácilmente tanto una máquina como un ser humano. Para entendernos, en lugar de tener Mateo Morral tiene Q3047716. Esta página contiene más información sobre la estructura de datos empleada.

La gracia de todo esto es que Wikidata, a través de esta interfaz (o mediante algún lenguaje de programación que otro), acepta peticiones SPARQL. Aquí puede encontrarse una guía con instrucciones y varios ejemplos. Durante estos días pasados he estado trasteando con esto y aquí van algunas chapucillas que he conseguido hacer funcionar (o no).

Alcaldes de Montreal desde los 90

Aquí va el primer ejemplo que conseguí echar a andar:

SELECT ?mayor ?mayorLabel ?start_term ?end_term ?partyLabel WHERE {
  ?mayor (wdt:P31/wdt:P279*) wd:Q5.  # look for human beings
  ?mayor wdt:P39 wd:Q177645.         # that list "Mayor of Montreal" as occupation
  ?mayor p:P39 ?statement.           # and from their term
  ?statement pq:P580 ?start_term.    # obtain the start date
  OPTIONAL {?statement pq:P582 ?end_term}.   # and the end date, if present
  OPTIONAL {?mayor p:P102 [ps:P102 ?party]}. # and the party, if present
  FILTER(year(?start_term) >= 1990 || year(?end_term) >= 1990) # and just take people around 1990 onwards

  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}
ORDER BY ?start_term

Recapitulando (todo esto está en la guía que he enlazado allí arriba, pero para vagos):

  • Las queries se forman mediante tripletes: qué, de qué tipo, cómo lo vamos a llamar (no necesariamente en ese orden).
  • Las variables se llaman ?var.
  • La última línea de la cláusula WHERE (SERVICE ...) activa el uso de variables especiales, ?varLabel, que imprimen el valor de una variable en algún lenguaje humano.
  • Un Q5 es un humano. Un Q177645 es un alcalde de Montreal. Esta query inspecciona seres humanos que han sido (P39, ocupación) alcaldes de Montreal en algún momento.
  • Usando OPTIONAL podemos asignar variables que igual no están ahí. Es el equivalente de un LEFT JOIN en SQL de toda la vida.
  • Aún estoy intentando comprender completamente cuándo usar cada modificador (wdt:, wd:, p:, ps: y pq:). De momento estoy componiendo las queries hacia atrás: voy a un objecto que sé que tiene que estar en el resultado final y voy inspeccionando de qué tipo es cada elemento que necesito filtrar.

El resultado:

                                    mayor        mayorLabel           start_term             end_term                  partyLabel
  http://www.wikidata.org/entity/Q3171790         Jean Doré 1986-01-01T00:00:00Z 1994-01-01T00:00:00Z Montreal Citizens' Movement
  http://www.wikidata.org/entity/Q3384158    Pierre Bourque 1994-01-01T00:00:00Z 2001-01-01T00:00:00Z             Vision Montreal
  http://www.wikidata.org/entity/Q1558936   Gérald Tremblay 2002-01-01T00:00:00Z 2012-11-05T00:00:00Z        Quebec Liberal Party
  http://www.wikidata.org/entity/Q3307998 Michael Applebaum 2012-11-16T00:00:00Z 2013-06-18T00:00:00Z              Union Montreal
  http://www.wikidata.org/entity/Q3219045 Laurent Blanchard 2013-06-25T00:00:00Z 2013-11-03T00:00:00Z             Vision Montreal
  http://www.wikidata.org/entity/Q3022603     Denis Coderre 2013-11-03T00:00:00Z 2017-11-15T00:00:00Z     Liberal Party of Canada
 http://www.wikidata.org/entity/Q27959212    Valérie Plante 2017-11-16T00:00:00Z                                                 

Esto es correcto a grandes rasgos, pero tiene sus cositas:

  • No aparece el partido de la alcaldesa actual, que debería ser Project Montréal. Su página de la Wikipedia sí lo refleja, pero los datos de Wikidata, si lo he entendido bien, se rellenan de forma colaborativa e independiente, así que puede haber discrepancias (de hecho, hay discrepancias).
  • Denis Coderre aparece como miembro del Partido Liberal. Aunque perteneció a ese partido, su campaña municipal la llevaba un partido llamado, directamente, Équipe Denis Coderre. Sin embargo, su nodo en Wikidata, Q3022603, lista únicamente el Partido Liberal.

Alcaldes de Madrid desde los 90

A ver si lo mismo funcionase, cambiando únicamente Q177645 por Q17078548 ("Alcaldes de Madrid"), y especificando que ahora queremos el idioma de las etiquetas en castellano:

                                    mayor                     mayorLabel           start_term             end_term                        partyLabel
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1979-04-03T00:00:00Z 2003-06-14T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1979-04-03T00:00:00Z 2003-06-14T00:00:00Z       Unión de Centro Democrático
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 1987-06-10T00:00:00Z 2003-05-25T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2827364      Agustín Rodríguez Sahagún 1987-06-30T00:00:00Z 1991-07-05T00:00:00Z       Unión de Centro Democrático
  http://www.wikidata.org/entity/Q2827364      Agustín Rodríguez Sahagún 1987-06-30T00:00:00Z 1991-07-05T00:00:00Z       Centro Democrático y Social
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 1987-07-28T00:00:00Z 1995-07-04T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1989-01-01T00:00:00Z 1991-01-01T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1989-01-01T00:00:00Z 1991-01-01T00:00:00Z       Unión de Centro Democrático
  http://www.wikidata.org/entity/Q2827364      Agustín Rodríguez Sahagún 1989-06-29T00:00:00Z 1991-07-05T00:00:00Z       Unión de Centro Democrático
  http://www.wikidata.org/entity/Q2827364      Agustín Rodríguez Sahagún 1989-06-29T00:00:00Z 1991-07-05T00:00:00Z       Centro Democrático y Social
  http://www.wikidata.org/entity/Q9025210               Luis María Huete 1991-01-01T00:00:00Z 1995-01-01T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1991-07-05T00:00:00Z 2003-06-14T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1991-07-05T00:00:00Z 2003-06-14T00:00:00Z       Unión de Centro Democrático
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 1995-01-01T00:00:00Z 2003-01-01T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q9025210               Luis María Huete 1995-07-06T00:00:00Z 1999-07-14T00:00:00Z                   Partido Popular
 http://www.wikidata.org/entity/Q19592761                Manuela Carmena 1996-07-26T00:00:00Z 2001-11-07T00:00:00Z                      Ahora Madrid
    http://www.wikidata.org/entity/Q41266                    Ana Botella 2003-06-14T00:00:00Z 2015-06-13T00:00:00Z                   Alianza Popular
    http://www.wikidata.org/entity/Q41266                    Ana Botella 2003-06-14T00:00:00Z 2015-06-13T00:00:00Z                   Partido Popular
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 2003-06-14T00:00:00Z 2011-12-22T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2733245                  Juan Barranco 2008-03-25T00:00:00Z 2011-06-06T00:00:00Z Partido Socialista Obrero Español
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 2011-11-30T00:00:00Z 2014-09-24T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q5992655               Manuel Cobo Vega 2011-12-22T00:00:00Z 2011-12-27T00:00:00Z                   Partido Popular
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 2011-12-22T00:00:00Z 2014-09-23T00:00:00Z                   Partido Popular
    http://www.wikidata.org/entity/Q41266                    Ana Botella 2011-12-27T00:00:00Z 2015-06-13T00:00:00Z                   Alianza Popular
    http://www.wikidata.org/entity/Q41266                    Ana Botella 2011-12-27T00:00:00Z 2015-06-13T00:00:00Z                   Partido Popular
 http://www.wikidata.org/entity/Q19592761                Manuela Carmena 2015-06-13T00:00:00Z                                           Ahora Madrid
 http://www.wikidata.org/entity/Q19592761                Manuela Carmena 2015-06-13T00:00:00Z                                           Ahora Madrid

Esto, esto... esto es muy feo por varios motivos, pero vamos a ceñirnos a la salida de la query. Están pasando varias cosas:

  • Hay gente que estuvo en varios partidos y esto hace que aparezcan duplicados (o casi duplicados). Vamos a pensar en un JOIN mal filtrado.
  • De igual forma, hay gente que fue alcalde / alcaldesa de Madrid, pero también tuvo otros cargos, bien antes, bien después. Lo mismo que en el punto anterior.

Dio la casualidad de que la lista de alcaldes de Montreal sale limpia porque no tiene estos problemas, pero aquí hay que afinar un poco más. Tenemos que asegurarnos de que:

  • El cargo que estamos obteniendo es la alcaldía (el objeto ?statement que filtramos).
  • El partido que estamos obteniendo es aquél al que pertenecieron mientras ejercían el cargo.

El primer punto es fácil. El segundo es imposible, o al menos no he encontrado por ninguna parte la fecha de las distintas afiliaciones. Por ejemplo, podemos acudir a Álvarez del Manzano (Q2748778) para ver que esa información no aparece. De todas formas, si arreglamos el primer punto y ampliamos la lista a alcaldes desde los 80 en adelante, pasan otras cosas:

SELECT ?mayor ?mayorLabel ?start_term ?end_term ?partyLabel WHERE {
  ?mayor (wdt:P31/wdt:P279*) wd:Q5.  # look for human beings
  ?mayor wdt:P39 wd:Q17078548.       # that list "Mayor of Madrid" as occupation
  ?mayor p:P39 ?statement.           # and from their term
  ?statement ps:P39 wd:Q17078548.    # **and only for the term as "Mayor of Madrid"**
  ?statement pq:P580 ?start_term.    # obtain the start date
  OPTIONAL {?statement pq:P582 ?end_term}.   # and the end date if present
  OPTIONAL {?mayor p:P102 [ps:P102 ?party]}. # and the party if present
  FILTER(year(?start_term) >= 1980 || year(?end_term) >= 1980) # and just take people around 1980 onwards

  SERVICE wikibase:label { bd:serviceParam wikibase:language "es". }
}
ORDER BY ?start_term
                                    mayor                     mayorLabel           start_term             end_term                        partyLabel
  http://www.wikidata.org/entity/Q1344166          Enrique Tierno Galván 1979-04-19T00:00:00Z 1986-01-19T00:00:00Z Partido Socialista Obrero Español
  http://www.wikidata.org/entity/Q2827364      Agustín Rodríguez Sahagún 1989-06-29T00:00:00Z 1991-07-05T00:00:00Z       Unión de Centro Democrático
  http://www.wikidata.org/entity/Q2827364      Agustín Rodríguez Sahagún 1989-06-29T00:00:00Z 1991-07-05T00:00:00Z       Centro Democrático y Social
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1991-07-05T00:00:00Z 2003-06-14T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q2748778 José María Álvarez del Manzano 1991-07-05T00:00:00Z 2003-06-14T00:00:00Z       Unión de Centro Democrático
   http://www.wikidata.org/entity/Q467829         Alberto Ruiz-Gallardón 2003-06-14T00:00:00Z 2011-12-22T00:00:00Z                   Partido Popular
  http://www.wikidata.org/entity/Q5992655               Manuel Cobo Vega 2011-12-22T00:00:00Z 2011-12-27T00:00:00Z                   Partido Popular
    http://www.wikidata.org/entity/Q41266                    Ana Botella 2011-12-27T00:00:00Z 2015-06-13T00:00:00Z                   Alianza Popular
    http://www.wikidata.org/entity/Q41266                    Ana Botella 2011-12-27T00:00:00Z 2015-06-13T00:00:00Z                   Partido Popular
 http://www.wikidata.org/entity/Q19592761                Manuela Carmena 2015-06-13T00:00:00Z                                           Ahora Madrid

Seguimos teniendo gente duplicada (por pertenencia a varios partidos), pero vamos a ignorar eso, que hay otros asuntos. Resulta que ahora pasamos de Tierno Galván a Rodríguez Sahagún directamente. ¿Por dónde se nos ha caído Juan Barranco? Resulta que Juan Barranco, alias Q2733245, aparece listado como Alcalde de Madrid pero sin fecha de inicio ni fin. Como estamos pidiendo que la fecha de inicio sea obligatoria, su nodo no aparece en el resultado final.

Películas de James Bond

Supongamos que quiero replicar el artículo de James Bond que enlacé antes. Necesito una tabla con las siguientes columnas:

  • Título de la película.
  • Año de estreno.
  • Actor que interpretó a Bond.
  • Recaudación en taquilla.

Como he comentado antes, lo más sencillo es partir de una entrada que sabemos que tiene que aparecer en el resultado final y trabajar hacia atrás. Por ejemplo: Skyfall.

Esto es lo que he conseguido:

SELECT ?film ?filmLabel (MIN(?publication_date) AS ?first_pub_date) ?actor_nameLabel ?box_office
WHERE {
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
  # Select films of the James Bond series published in the past
  ?film (wdt:P31/wdt:P279*) wd:Q11424.      # films
  ?film wdt:P179 wd:Q2484680.               # of the James Bond series
  ?film p:P577 [ps:P577 ?publication_date]. # get the publication date
  FILTER (year(?publication_date) <= 2018)  # remove "Bond 25" from the list
  # Cast member: get who played James Bond
  ?film p:P161 ?actorStatement.             # actors in the movie
  ?actorStatement pq:P453 wd:Q2009573.      # but only the one playing Bond
  ?actorStatement ps:P161 ?actor_name.
  # Box office
  ?film p:P2142 ?box_officeStatement.
  ?box_officeStatement ps:P2142 ?box_office
}
GROUP BY ?film ?filmLabel ?actor_nameLabel ?box_office
ORDER BY ?first_pub_date
                                     film               filmLabel       first_pub_date actor_nameLabel box_office
   http://www.wikidata.org/entity/Q107724             Thunderball 1965-01-01T00:00:00Z    Sean Connery  205000000
   http://www.wikidata.org/entity/Q334780               Moonraker 1979-06-26T00:00:00Z     Roger Moore  210300000
   http://www.wikidata.org/entity/Q212145 The World Is Not Enough 1999-11-08T00:00:00Z  Pierce Brosnan  361832400
    http://www.wikidata.org/entity/Q30931         Die Another Day 2002-11-22T00:00:00Z  Pierce Brosnan  432000000
     http://www.wikidata.org/entity/Q4941                 Skyfall 2012-10-23T00:00:00Z    Daniel Craig 1109000000
 http://www.wikidata.org/entity/Q18602670                 Spectre 2015-10-26T00:00:00Z    Daniel Craig  880674609

Aprovecho esta query para enseñar que aquí también se puede meter un GROUP BY: hay películas que tienen varias fechas de estreno --no se estrenan a la vez en todo el mundo--, así que aquí estoy cogiendo la primera. El problema ahora es que, de todas las películas de James Bond, solamente 6 tienen toda la información necesaria; a todas las demás les falta la recaudación en taquilla. No solamente eso, sino que desconozco exactamente en qué unidad está el resultado: sé que son dólares americanos, pero no sé si son del año del estreno o están normalizados de alguna forma.

Conclusiones

Wikidata es prometedor. La idea de tener un equivalente wikipedístico al que preguntarle datos directamente para procesarlos después, sin tener que pasar por el tedio de analizar código HTML, es cañera. Aunque escribir las queries es complicado inicialmente, después de un par de días de ensayo y error la mayoría de las cosas van saliendo solas, pero el problema principal no es ese: son los datos incompletos, contradictorios o erróneos. Si he entendido bien el sentido del proyecto, en algún momento la propia Wikipedia utilizará parcialmente información de Wikidata, pero todavía no estamos ahí.