Automatización de la descarga de datos de delitos en Costa Rica

En este post aprenderemos cómo automatizar desde R la descarga, unificación y limpieza de datos sobre delitos en Costa Rica, obtenidos desde la plataforma del Poder Judicial.
Autor/a
Afiliación

Carlos Agüero B

Fecha de publicación

4 de marzo de 2025

Fecha de modificación

6 de marzo de 2025

Introducción: ¿Por qué hacer esto?

Todos los días escuchamos en las noticias acerca del incremento en los homicidios y la inseguridad en Costa Rica. Por un lado, se dice que la inseguridad está fatal, por otro, que “es solo percepción” y que “se matan entre ellos” (refiriéndose a los delincuentes). Entre dimes y diretes, creo que este extracto del libro la sociedad del cansancio representa cómo nos podemos sentir.

En la era digital, el exceso de información nos satura hasta el punto de la apatía; el bombardeo constante de datos nos impide distinguir lo esencial y nos sumerge en un estado de desconfianza generalizada. - Byung-Chul Han, la sociedad del cansancio (2012).

Veamos algunas de esas noticias:






Creo que es fundamental que, como ciudadanos, tengamos acceso a la información para que podamos comprobar de manera independiente los datos que nos bombardean desde todas partes. No me malentiendan, no se trata de negar las instituciones ni de estar en contra de la prensa, para nada, sino de poder, dentro de lo posible, acceder a las fuentes y echar un vistazo a lo que pasa en el país, incluso más como un ejercicio de empatía que de análisis de datos.

Con esa idea en mente, surge este mini tutorial, en el cual veremos, paso a paso, cómo utilizar R para descargar y unificar la información proporcionada por el Poder Judicial.

Automatizar la descarga

El primer problema aparecio al ver que la plataforma del poder judicial sólo permite descargar los datos de un año por consulta (o al menos a mí me da error si intento descargar varios años juntos). Hacerlo manualmente para 10 años no era mi opción preferida, así que vamos a automatizarlo.

Para lograr esto, vamos a utilizar los paquetes selenider y chromote, una combinación que he venido probando desde hace poco con muy buenos resultados, nos permite hacer web scraping de forma sencilla y resuelve muchos de los problemas que teníamos al trabajar con RSelenium.

Selenider en 1 minuto:

  1. Levantas una sesión que controla el navegador por defecto, Chrome.
library(selenider)

session <- selenider_session(
  "chromote",
  timeout = 10,
  options = chromote_options(headless = FALSE)
)
  1. Abrir una url
open_url("https://www.r-project.org/")
  1. Seleccionas elementos de la web usando CSS o XPath.
# selecciona los elementos con el id css rStudioHeader
header <- s("#rStudioHeader") 
  1. Realizas acciones como clic, llenar información, hacer scroll, etc.
s(".claseBoton") |>
  elem_click()
  1. elem_click()

Realiza un clic en el elemento especificado.

  1. elem_right_click()

Ejecuta un clic derecho sobre el elemento.

  1. elem_double_click()

Realiza un doble clic en el elemento.

  1. elem_hover()

Posiciona el cursor sobre el elemento, simulando la acción de pasar el mouse por encima.

  1. elem_scroll_to()

Desplaza la vista hasta el elemento indicado, lo cual es útil si el elemento no está visible antes de realizar un clic o interacción.

Veamos cómo es el proceso manual para identificar qué es lo que debemos automatizar:

  1. Indicar las fechas de inicio y fin.

  1. Indicar el lugar del suceso.

  1. Seleccionar la categoría delictiva.

  1. Indicar si se trata de víctima policial.

  1. Hacer clic en el botón de descargar Excel.

Acá me quedé pegado en un buen rato intentando como explicar el código, y llegué a la conclusión de que lo más razonable es un video corto.



Puedes ver el código completo acá.
library(rvest)
library(selenider)
library(lubridate)
library(fs)
library(purrr)
library(janitor)
library(glue)
library(chromote)
library(dplyr)
library(readr)
library(stringr)

wait_for_download <- function(download_dir, filename, timeout = 60) {
  start_time <- Sys.time()
  file_path <- file.path(download_dir, filename)
  while (
    as.numeric(difftime(Sys.time(), start_time, units = "secs")) <= timeout
  ) {
    if (
      file.exists(file_path) &&
        length(list.files(download_dir, pattern = "\\.crdownload$")) == 0
    ) {
      return(TRUE)
    }
    Sys.sleep(1)
  }
  cli::cli_abort("Se acabó el tiempo de espera")
}

download_dataset_pj <- function(
  date = lubridate::today(),
  download_dir = "data",
  filename = "Estadisticas.xls"
) {
  dir.create(download_dir, showWarnings = FALSE, recursive = TRUE)
  full_path <- file.path(download_dir, filename)

  session <- selenider_session(
    "chromote",
    timeout = 10,
    options = chromote_options(headless = TRUE)
  )
  session$driver$Browser$setDownloadBehavior(
    behavior = "allow",
    downloadPath = normalizePath(download_dir)
  )

  open_url("https://pjenlinea3.poder-judicial.go.cr/estadisticasoij/")

  start_date <- format(date, "%d/%m/%Y")
  end_date <- update(date, month = 12, day = 31)
  end_date <- if_else(end_date > today(), today(), end_date)
  end_date <- format(end_date, "%d/%m/%Y")

  execute_js_expr(
    "document.getElementById('txtFechaFinal').removeAttribute('readonly',0);"
  )
  execute_js_expr(glue(
    "document.getElementById('txtFechaFinal').value = '{end_date}';",
  ))

  execute_js_expr(
    "document.getElementById('txtFechaInicio').removeAttribute('readonly',0);"
  )
  execute_js_expr(glue(
    "document.getElementById('txtFechaInicio').value = '{start_date}';",
  ))

  execute_js_expr("document.getElementById('chbTodoPais').click();")
  execute_js_expr("document.getElementById('chbTodoDelitos').click();")
  execute_js_expr("document.getElementById('chbTodaVictima').click();")

  execute_js_expr("document.getElementById('btnExcel').click();")

  wait_for_download(download_dir, filename)

  new_filename <- paste0("delitos_", format(date, "%Y-%m-%d"), ".html")
  new_path <- file.path(download_dir, new_filename)
  fs::file_move(full_path, new_path)

  return(new_path)
}

.years <- ymd("2025-01-01") - years(0:11)

df <- map(
  .years,
  \(x) {
    .file <- download_dataset_pj(date = x)
    df <- read_html(.file) |>
      html_table()
    df <- df[[1]]
    return(df)
  },
  .progress = TRUE
) |>
  list_rbind()

df |>
  clean_names() |>
  mutate(
    across(where(is.character), str_to_title),
    fecha = ymd(fecha)
  ) |>
  readr::write_rds(glue("data/delitos_{today()}.rds"))

Problemas con el archivo descargado

Despues de descargar los datos apareció un problema. al intentar abrir el archivo desde R, aparece un error, el archivo parecía corrupto o mal formateado. Raro, porque si abría el archivo directamente desde Excel, todo funcionaba bien.

Revisando el código fuente de la web, pude encontrar que el archivo se genera a partir de una tabla HTML que se codifica en Base64, asignándole la extensión .xls (Excel viejito) y un MIME type de Excel. Esto quiere decir que, aunque aparenta ser un archivo de Excel, el contenido real es HTML y no el formato binario nativo de Excel (xls). Excel, que siempre que puede resuelve, logra detectar el contenido y mostrar la tabla sin problema, pero el paquete readxl, que utiliza la librería libxls, necesita el formato binario de Excel para poder funcionar. Por eso, al intentar abrirlo en R se produce el error libxls error: Unable to open file, porque, sorpresa, ¡no es un archivo de Excel!

La solución fue sencilla, si es un archivo HTML, basta con cambiarle la extensión de .xls a .html y usar la función read_html del paquete rvest. Una vez resuelto eso, queda completamente automatizada la descarga de 10 años de registros en un clic.

Limpieza y normalización de los datos

A partir de aquí, el trabajo de limpieza es muy corto. Se normalizan los nombres de las columnas y se ajustan las variables de tipo texto para evitar tener todo en mayúsculas.

Aquí, alguien podría preguntarse: ¿por qué, entonces, no descargar directamente el archivo .csv en lugar del excel? Pasa que, por algún motivo, al archivo CSV le falta la columna distrito. Asumo que está relacionado con la forma en que se genera a partir de la tabla HTML original, pero, la verdad, no lo investigué.

Puedes descargar los datos en el siguiente enlace descargar datos

¿Y ahora qué?

Esta es la primera parte del trabajo. Ahora que tenemos los datos, estamos listos para realizar un análisis. En el siguiente post hablaremos sobre el tema de los homicidios en Costa Rica.

Finalmente, un reconocimiento especial al Poder Judicial por el esfuerzo y la transparencia de poner estos datos a disposición de todos.

Cómo citar

BibTeX
@online{agüero_b2025,
  author = {Agüero B, Carlos},
  title = {Automatización de la descarga de datos de delitos en Costa
    Rica},
  date = {2025-03-04},
  url = {https://aprendetidyverse.com/posts/003_descargar_delitos_costarica.html},
  langid = {es}
}
Por favor, cita este trabajo como:
Agüero B, Carlos. 2025. “Automatización de la descarga de datos de delitos en Costa Rica.” March 4, 2025. https://aprendetidyverse.com/posts/003_descargar_delitos_costarica.html.