La Raya · backend (7): una sección de gastronomía de cero — OSM, datos abiertos y el baile de la deduplicación

Publicado el 20/06/2026

El objetivo era simple de enunciar: poblar la gastronomía de cada localidad y sus alrededores con datos objetivos (nombre, tipo, dirección, horario), sin reseñas ni valoraciones, y sin colar sitios a cientos de kilómetros. Tardé varias fuentes y varias iteraciones de deduplicación en dejarlo decente.

OpenStreetMap y el doble candado de proximidad

Empecé con OpenStreetMap vía Overpass API: gratis, legal y sin opiniones. La precisión geográfica la garanticé con un doble candado: consulto en un radio alrededor de cada localidad y luego asigno cada local a la localidad más cercana por distancia Haversine, descartando lo que supere un máximo. Así no entra ni un Toledo ni una Lisboa.

El problema: OSM está casi vacío en los pueblos pequeños. En Valencia de Alcántara no había ni un bar mapeado, y el usuario me lo hizo notar con nombres concretos de locales que faltaban. OSM cubre ciudades; los pueblos, no.

Enriquecer sin mentir (y un desvío de pago)

Los locales venían con poco más que el nombre. Para añadir descripción e imagen sin caer en reseñas: una frase factual generada de los tags de OSM (cocina, terraza, para llevar…), fusionada con la descripción de OSM y la meta de la web propia; y de imagen, la og:image de su web o, si no, Wikidata/Commons.

Aquí un callejón honesto: evalué Foursquare para tener más fotos. Su API nueva resultó cobrar justo lo que quería (fotos y descripción son premium, de pago desde la primera llamada); lo gratuito eran datos básicos que ya tenía. Lo retiré. Y TripAdvisor lo descarté de raíz: su contenido es reseñas (lo que no quiero) y su scraping choca con el derecho sui generis de bases de datos en la UE. Otro detalle de calidad: los horarios de OSM vienen en sintaxis cruda (Mo-Fr 09:00-22:00; Su off) y los paso a legible en español ("Lun-Vie 09:00-22:00; Dom cerrado").

La fuente que de verdad llenó los pueblos

Para la cobertura rural busqué datos oficiales. La Junta de Extremadura publica como datos abiertos (CC-BY) el registro de establecimientos de restauración: miles de locales en todos los municipios, pueblos incluidos. Un ingestor descarga el CSV, lo normaliza por municipio y lo integra. Aquella localidad pasó de 0 a decenas de locales reales, los que faltaban incluidos.

Para Portugal no existe equivalente abierto (los registros oficiales de turismo excluyen restauración), así que se scrapea un directorio público uniforme que cubre el Alto Alentejo y la Beira, respetando su robots.txt y tomando solo datos objetivos.

El baile de la deduplicación (otra vez)

Combinar cuatro fuentes trae duplicados, y aquí iteré varias veces, equivocándome en público:

  • Nombre idéntico (OSM "El Cafetín" vs registro "El Cafetin"): un campo nombre_norm indexado (sin acentos ni signos) lo resuelve. Borrado seguro.
  • Primero intenté detectar duplicados por dirección… error: en plazas y pueblos hay locales DISTINTOS en la misma dirección. Generaba cientos de falsos positivos.
  • Lo sustituí por contención de nombre ("Asador Los Canchales" / "Los Canchales")… que también daba falsos ("La Mar" dentro de "La Marina", "Café Bar" dentro de medio listado).
  • Quedó en una regla precisa: pares que difieren solo en una palabra de categoría (Restaurante, Bar, Mesón…) o sufijo legal. Y un informe, nunca un borrado automático.
  • Para el caso imposible (mismo local, nombre nuevo tras cambiar de dueño), una casilla "excluido" que el scraper respeta: aunque vuelva a encontrarlo, no lo reactiva.

Rendimiento: lo que no hace falta, fuera

La primera carga tardaba ~80 minutos. El cuello era geocodificar miles de direcciones contra Nominatim a 1 petición/segundo. La conclusión fue que no hacían falta coordenadas exactas (la app lista por localidad y "Cómo llegar" usa la dirección), así que eliminé la geocodificación: la carga pasó a un par de minutos. La dedup también se optimizó a una única consulta .exists().

Lo que me llevo

  • Ninguna fuente lo cubre todo. OSM en ciudades, datos abiertos para el detalle rural, scraping donde no hay datos abiertos. La gracia está en combinarlas y deduplicar.
  • No todo "gratis" es viable, y no todo "público" es reutilizable. Foursquare cobra lo bueno; TripAdvisor choca con la ley. Elegir bien la fuente es la mitad del trabajo.
  • La deduplicación perfecta no existe. Hay casos (cambio de dueño) que ninguna heurística distingue de dos bares vecinos. Para esos, una palanca manual honesta vale más que un algoritmo que "la lía".
  • Quita lo que no aporta. La geocodificación era cara y prescindible; eliminarla fue la mejor optimización.

Faltaba una pieza: dar voz a los usuarios sin montar un sistema de cuentas. La última entrada del backend va de eso — un formulario de contacto pensado para no guardar datos personales.