Ver todo el portafolio

Visor de arriendos

Sistema que consolida a diario la oferta de arriendos de tres portales, la deduplica, la geocodifica y la publica en un visor con filtros, métricas e historial.

Año
2026
Estado
En producción, se actualiza a diario
Herramientas
Python, Playwright, Leaflet, OpenStreetMap, launchd, GitHub Pages
Visor de arriendos

El problema

El requerimiento era concreto. Con mi compañero de casa decidimos mudarnos y teníamos definido lo que buscábamos: tres dormitorios (dos para dormir y uno de oficina), cerca de Parque Bustamante, Lastarria, Barrio Italia o Manuel Montt, y un tope de $800.000 al mes con gastos comunes incluidos.

Hacerlo a mano tiene varios problemas:

  • Los avisos están repartidos en varios portales y el mismo departamento aparece dos o tres veces.
  • Casi nadie publica los gastos comunes, así que un arriendo de $700.000 puede terminar costando $850.000.
  • Unos precios vienen en UF y otros en pesos.
  • Ningún buscador entiende «cerca del metro» ni «un dos dormitorios grande que sirva de tres».

Después de una semana de búsquedas manuales convertí el requerimiento en un problema de datos y construí el pipeline que lo resuelve.

Lo que encontró esta mañana

Estas cifras se leen del visor real cada vez que se publica este sitio.

861avisos revisados
438nuevos desde la corrida anterior
30días de historia guardados
4match perfecto

Actualizado 2026-10-01 11:34

Es la versión real. Prueba el botón «Match perfecto» y la pestaña de ofertas.

Un pipeline de punta a punta

El diseño es simple a propósito. Cada pieza hace una sola cosa y se puede correr por separado.

  1. 01
    ScrapersUn archivo por portal, todos con la misma salida. scrapers/*.py
  2. 02
    ConsolidarUne fuentes, elimina duplicados y calcula el total con gastos comunes. consolidate.py
  3. 03
    GeocodificarDirecciones a coordenadas con OpenStreetMap y caché. geocode.py
  4. 04
    EnriquecerPlaywright abre las fichas importantes para sacar gastos reales, antigüedad y mascotas. enrich_pi.py
  5. 05
    Visor estáticoHTML, CSS y JavaScript con Leaflet. Sin servidor ni base de datos. viewer/
  6. 06
    Todos los díaslaunchd lo corre a las 10:00, guarda la foto del día y publica en GitHub Pages.

La decisión más importante fue definir un esquema común antes de escribir el primer scraper. Cada portal tiene su HTML, pero todos devuelven la misma estructura. Agregar un portal nuevo es escribir un archivo que devuelva una lista de avisos y nada más.

Scraping: cada portal es un mundo

PortalTécnicaResultado
Portal Inmobiliariorequests + HTMLFuente principal, por barrio
Chilepropiedadesrequests + HTMLRápido y completo
YapoScrapling / CamoufoxBloquea bots, unos 40 s por página
Facebook MarketplaceCSV manualScrapearlo viola sus términos

Un clásico chileno: los precios llegan como «$ 750.000», «UF 13,5» o «13,5 UF». La conversión usa el valor de la UF del día desde mindicador.cl, con un respaldo si la API no responde.

def parse_precio(texto: str) -> int:
    t = texto.upper().replace("\xa0", " ")
    num = re.sub(r"[^\d.,]", "", t)   # solo dígitos, comas y puntos
    if "UF" in t:                      # la UF usa coma decimal
        return round(float(num.replace(".", "").replace(",", ".")) * uf_hoy())
    return int(num.replace(".", ""))

Entre los arriendos se colaban avisos de venta. La regla para detectarlos fue simple: ningún arriendo mensual cuesta 158 millones de pesos.

Duplicados y geocodificación

Con más de mil avisos de varias fuentes, los duplicados son inevitables. La deduplicación va en dos pasos: primero un id estable (el hash de la URL) y después una clave de dirección normalizada más precio, que atrapa el mismo aviso publicado por dos corredoras.

Para el mapa hacían falta coordenadas. Nominatim, de OpenStreetMap, es gratis pero acepta una consulta por segundo. Por eso todo pasa por un caché que solo consulta direcciones nuevas. Si una dirección no se encuentra, cae en cascada al centro del barrio y luego al de la comuna, con un pequeño desplazamiento para que los pins no se apilen. Esos avisos quedan marcados como ubicación aproximada.

Y «cerca del metro» no requirió scraping. Con las coordenadas de cada aviso y de 23 estaciones, la fórmula de haversine da la distancia. Cada tarjeta dice, por ejemplo, «Santa Isabel, 257 m».

Codificar lo que de verdad buscábamos

No bastaba con «tres dormitorios bajo $800.000». Un dos dormitorios de 68 m² o más también servía. Esa búsqueda humana se volvió una regla:

# un 2D amplio sirve como oficina y también califica
sirve = dorm >= 3 or (dorm >= 2 and m2 >= 68)
match_perfecto = sirve and total_con_gastos <= 800_000 and en_barrio_objetivo

El resultado es un embudo. Así se veía el 15 de septiembre de 2026:

Cada aviso recibe además un puntaje de 0 a 100 que pondera presupuesto, dormitorios, barrio y metro. Lo más prometedor siempre queda arriba.

¿Está caro o es una oferta?

Un precio solo es caro comparado con algo parecido. El pipeline compara cada arriendo con la mediana de su grupo, y si el grupo tiene menos de cinco avisos, sube a uno más amplio:

  1. Barrio+ dormitorios + baños + m²
  2. Barrio+ dormitorios + m²
  3. Comuna+ dormitorios + m²
  4. Comuna+ dormitorios

Un aviso 10% bajo la mediana de su grupo se marca como oferta y tiene su propia pestaña. Ahí el visor dejó de mostrar solo qué hay y empezó a mostrar qué conviene.

Actualización automática e historial

Un LaunchAgent de macOS corre todo cada mañana y hace push a GitHub Pages. Cada corrida guarda una foto completa del mercado, y con 30 días de fotos el visor sabe qué avisos son nuevos, cuáles bajaron de precio y cuáles desaparecieron (probablemente se arrendaron). Hay un selector para ver el mercado de cualquier día del último mes.

De más de mil avisos diarios a un par de docenas de candidatos concretos cada mañana.

Aprendizajes técnicos

  • Esquema común primero, scrapers después. Sumar fuentes pasa a ser trivial.
  • Caché en todo lo caro. Volver a correr es barato y amable con los servicios externos.
  • Respaldos en cascada. El pipeline no se cae por un dato faltante.
  • Salida estática. Hosting gratis y cero mantención.
  • Automatizar desde el día uno. Los datos se mantienen frescos sin acordarse.

¿Lo quieres para tu búsqueda? Todos los criterios viven en config.py: cambia comunas, presupuesto y barrios, corre python run_all.py y tienes tu propio visor.