jcalbornoz commited on
Commit
1747576
·
verified ·
1 Parent(s): e7b2987

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -36
app.py CHANGED
@@ -20,9 +20,8 @@ except ImportError:
20
  subprocess.run(["pip", "install", "fake-useragent"], check=True)
21
  from fake_useragent import UserAgent
22
 
23
- # --- 1. GENERADOR DE URLS (LLAVE MAESTRA) ---
24
- def construir_urls_final(barrio, ciudad, tipo, hab, ban, park, antiguedad):
25
- # Mapeo exacto de antigüedad (Con "anos")
26
  mapa_ant = {
27
  "Menos de 1 año": "de-0-a-1-anos",
28
  "1 a 8 años": "de-1-a-8-anos",
@@ -33,44 +32,52 @@ def construir_urls_final(barrio, ciudad, tipo, hab, ban, park, antiguedad):
33
  slug_ant = mapa_ant.get(antiguedad, "de-1-a-8-anos")
34
  slug_park = f"{int(park)}-parqueadero" if int(park) == 1 else f"{int(park)}-parqueaderos"
35
 
36
- # Limpieza de zona
37
  b_slug = barrio.lower().strip().replace(" ", "-")
38
  c_slug = ciudad.lower().strip().replace(" ", "-")
 
39
 
40
- # Manejo de tipos de inmueble (Agrupación descubierta por el usuario)
41
  tipo_slug = tipo.lower().strip()
42
  if tipo_slug in ["apartamento", "casa"]:
43
  tipo_fr = "casas-y-apartamentos-y-apartaestudios"
44
  else:
45
- tipo_fr = tipo_slug + "s" # bodegas, lotes, oficinas
46
 
47
- # --- URL FINCA RAÍZ EXACTA ---
48
- if tipo_slug in ["bodega", "lote", "oficina"]:
49
- url_fr = f"https://www.fincaraiz.com.co/arriendo/{tipo_fr}/{b_slug}/{c_slug}/{slug_park}/{slug_ant}"
50
- else:
51
- url_fr = f"https://www.fincaraiz.com.co/arriendo/{tipo_fr}/{b_slug}/{c_slug}/{int(hab)}-o-mas-habitaciones/{int(ban)}-o-mas-banos/{slug_park}/{slug_ant}"
 
52
 
 
 
53
  # --- URL METROCUADRADO ---
54
- url_mc = f"https://www.metrocuadrado.com/{tipo_slug}-casa-oficina/arriendo/{c_slug}/{b_slug}/{int(ban)}-banos-{int(hab)}-habitaciones/?search=form"
 
55
 
56
  return url_fr, url_mc
57
 
58
- # --- 2. EXTRACTOR DE PRECIO INTELIGENTE (REGEX) ---
59
- def extraer_precio_regex(texto):
60
  patron = r'\$\s?(\d{1,3}(?:[.,]\d{3})*)'
61
  coincidencias = re.findall(patron, texto)
62
  if coincidencias:
63
  precios = [int(p.replace('.', '').replace(',', '')) for p in coincidencias]
64
- # Filtro: Ignorar precios de venta (> 40 millones)
65
- precios_validos = [p for p in precios if 600000 <= p <= 40000000]
 
 
 
 
 
66
  if precios_validos:
67
  return precios_validos[0]
68
  return 0
69
 
70
- # --- 3. MOTOR DE EXTRACCIÓN ROBUSTO ---
71
- def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad):
72
  resultados = []
73
- url_fr, url_mc = construir_urls_final(barrio, ciudad, tipo, hab, ban, park, antiguedad)
74
 
75
  log_visible = f"✅ URLs GENERADAS (AUDITORÍA):\nFR: {url_fr}\nMC: {url_mc}\n\n"
76
  ua = UserAgent()
@@ -91,7 +98,7 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
91
  # --- FINCA RAÍZ ---
92
  try:
93
  page = context.new_page()
94
- log_visible += "🔄 Escaneando Finca Raíz...\n"
95
  page.goto(url_fr, wait_until="domcontentloaded", timeout=60000)
96
 
97
  page.mouse.move(random.randint(100, 500), random.randint(100, 500))
@@ -107,7 +114,7 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
107
  txt = el.inner_text()
108
 
109
  if "$" in txt:
110
- precio = extraer_precio_regex(txt)
111
  if precio > 0:
112
  href = el.get_attribute("href")
113
  full_url = f"https://www.fincaraiz.com.co{href}" if href and href.startswith("/") else href
@@ -128,7 +135,7 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
128
  # --- METROCUADRADO ---
129
  try:
130
  page = context.new_page()
131
- log_visible += "🔄 Escaneando Metrocuadrado...\n"
132
  page.goto(url_mc, wait_until="domcontentloaded", timeout=60000)
133
 
134
  for _ in range(5):
@@ -143,7 +150,7 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
143
  txt = card.inner_text()
144
 
145
  if "$" in txt:
146
- precio = extraer_precio_regex(txt)
147
  if precio > 0:
148
  enlace = card.query_selector("a")
149
  if enlace:
@@ -184,7 +191,7 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
184
  pdf = FPDF()
185
  pdf.add_page()
186
  pdf.set_font("Arial", 'B', 14)
187
- pdf.cell(0, 10, f"ESTUDIO DE MERCADO: {barrio.upper()}", ln=True)
188
  pdf.ln(5)
189
 
190
  for _, r in df_final.iterrows():
@@ -205,8 +212,8 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
205
  maximo = df_final['Precio'].max()
206
 
207
  resumen = (
208
- f"💰 **ESTIMACIÓN DE RENTA**\n"
209
- f"🔹 **Canon Sugerido:** ${promedio:,.0f}\n"
210
  f"📉 Mínimo Zona: ${minimo:,.0f}\n"
211
  f"📈 Máximo Zona: ${maximo:,.0f}"
212
  )
@@ -217,20 +224,28 @@ def motor_tramitia_final(barrio, ciudad, area, tipo, hab, ban, park, antiguedad)
217
 
218
  # --- INTERFAZ GRÁFICA ---
219
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
220
- gr.Markdown("## 🤖 TramitIA Pro: Analizador Inmobiliario (Rutas Exactas)")
221
 
222
  with gr.Row():
223
  with gr.Column(scale=1):
 
 
224
  c = gr.Textbox(label="Ciudad", value="Barranquilla")
225
  b = gr.Textbox(label="Barrio (Ej: La Concepcion)", value="La Concepcion")
226
- a = gr.Number(label="Área M2", value=70)
227
-
228
- t = gr.Dropdown(
229
- ["Apartamento", "Casa", "Bodega", "Lote", "Oficina"],
230
- label="Tipo",
231
- value="Apartamento"
232
- )
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  with gr.Row():
235
  h = gr.Number(label="Habitaciones", value=3)
236
  ban = gr.Number(label="Baños", value=2)
@@ -240,7 +255,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
240
  ["Menos de 1 año", "1 a 8 años", "9 a 15 años", "16 a 30 años", "Más de 30 años"],
241
  label="Antigüedad", value="1 a 8 años"
242
  )
243
- btn = gr.Button("EJECUTAR ANÁLISIS", variant="primary")
244
 
245
  with gr.Column(scale=2):
246
  res_fin = gr.Markdown("### 💰 El resultado aparecerá aquí...")
@@ -249,6 +264,8 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
249
  with gr.TabItem("Tabla de Resultados"): out_df = gr.Dataframe()
250
  with gr.TabItem("Descargar PDF"): out_pdf = gr.File()
251
 
252
- btn.click(motor_tramitia_final, [b, c, a, t, h, ban, p, e], [msg, out_df, out_pdf, res_fin])
 
 
253
 
254
  demo.launch()
 
20
  subprocess.run(["pip", "install", "fake-useragent"], check=True)
21
  from fake_useragent import UserAgent
22
 
23
+ # --- 1. GENERADOR DE URLS (VENTA/ARRIENDO Y EXTRAS) ---
24
+ def construir_urls_final(operacion, barrio, ciudad, tipo, hab, ban, park, antiguedad, m2_min, m2_max, ascensor, piscina):
 
25
  mapa_ant = {
26
  "Menos de 1 año": "de-0-a-1-anos",
27
  "1 a 8 años": "de-1-a-8-anos",
 
32
  slug_ant = mapa_ant.get(antiguedad, "de-1-a-8-anos")
33
  slug_park = f"{int(park)}-parqueadero" if int(park) == 1 else f"{int(park)}-parqueaderos"
34
 
 
35
  b_slug = barrio.lower().strip().replace(" ", "-")
36
  c_slug = ciudad.lower().strip().replace(" ", "-")
37
+ op_slug = operacion.lower().strip() # 'venta' o 'arriendo'
38
 
 
39
  tipo_slug = tipo.lower().strip()
40
  if tipo_slug in ["apartamento", "casa"]:
41
  tipo_fr = "casas-y-apartamentos-y-apartaestudios"
42
  else:
43
+ tipo_fr = tipo_slug + "s"
44
 
45
+ # --- URL FINCA RAÍZ MULTIFILTRO ---
46
+ url_fr_base = f"https://www.fincaraiz.com.co/{op_slug}/{tipo_fr}/{b_slug}/{c_slug}/{int(hab)}-o-mas-habitaciones/{int(ban)}-o-mas-banos/{slug_park}/{slug_ant}/m2-desde-{int(m2_min)}/m2-hasta-{int(m2_max)}"
47
+
48
+ # Extras condicionales
49
+ if ascensor: url_fr_base += "/con-ascensor"
50
+ if piscina: url_fr_base += "/con-piscina"
51
 
52
+ url_fr = url_fr_base
53
+
54
  # --- URL METROCUADRADO ---
55
+ # MC usa un formato más simple, le pasamos la operación dinámica
56
+ url_mc = f"https://www.metrocuadrado.com/{tipo_slug}-casa-oficina/{op_slug}/{c_slug}/{b_slug}/{int(ban)}-banos-{int(hab)}-habitaciones/?search=form"
57
 
58
  return url_fr, url_mc
59
 
60
+ # --- 2. EXTRACTOR DE PRECIO DINÁMICO ---
61
+ def extraer_precio_regex(texto, operacion):
62
  patron = r'\$\s?(\d{1,3}(?:[.,]\d{3})*)'
63
  coincidencias = re.findall(patron, texto)
64
  if coincidencias:
65
  precios = [int(p.replace('.', '').replace(',', '')) for p in coincidencias]
66
+
67
+ # Filtro de cordura según si es Venta o Arriendo
68
+ if operacion == "Arriendo":
69
+ precios_validos = [p for p in precios if 600000 <= p <= 40000000] # Ignora ventas y admón
70
+ else: # Venta
71
+ precios_validos = [p for p in precios if p >= 40000000] # Ignora arriendos cruzados
72
+
73
  if precios_validos:
74
  return precios_validos[0]
75
  return 0
76
 
77
+ # --- 3. MOTOR DE EXTRACCIÓN ---
78
+ def motor_tramitia_final(operacion, barrio, ciudad, area, m2_min, m2_max, tipo, hab, ban, park, antiguedad, ascensor, piscina):
79
  resultados = []
80
+ url_fr, url_mc = construir_urls_final(operacion, barrio, ciudad, tipo, hab, ban, park, antiguedad, m2_min, m2_max, ascensor, piscina)
81
 
82
  log_visible = f"✅ URLs GENERADAS (AUDITORÍA):\nFR: {url_fr}\nMC: {url_mc}\n\n"
83
  ua = UserAgent()
 
98
  # --- FINCA RAÍZ ---
99
  try:
100
  page = context.new_page()
101
+ log_visible += f"🔄 Escaneando Finca Raíz ({operacion})...\n"
102
  page.goto(url_fr, wait_until="domcontentloaded", timeout=60000)
103
 
104
  page.mouse.move(random.randint(100, 500), random.randint(100, 500))
 
114
  txt = el.inner_text()
115
 
116
  if "$" in txt:
117
+ precio = extraer_precio_regex(txt, operacion) # Pasamos la operación
118
  if precio > 0:
119
  href = el.get_attribute("href")
120
  full_url = f"https://www.fincaraiz.com.co{href}" if href and href.startswith("/") else href
 
135
  # --- METROCUADRADO ---
136
  try:
137
  page = context.new_page()
138
+ log_visible += f"🔄 Escaneando Metrocuadrado ({operacion})...\n"
139
  page.goto(url_mc, wait_until="domcontentloaded", timeout=60000)
140
 
141
  for _ in range(5):
 
150
  txt = card.inner_text()
151
 
152
  if "$" in txt:
153
+ precio = extraer_precio_regex(txt, operacion)
154
  if precio > 0:
155
  enlace = card.query_selector("a")
156
  if enlace:
 
191
  pdf = FPDF()
192
  pdf.add_page()
193
  pdf.set_font("Arial", 'B', 14)
194
+ pdf.cell(0, 10, f"ESTUDIO DE MERCADO: {barrio.upper()} ({operacion.upper()})", ln=True)
195
  pdf.ln(5)
196
 
197
  for _, r in df_final.iterrows():
 
212
  maximo = df_final['Precio'].max()
213
 
214
  resumen = (
215
+ f"💰 **ESTIMACIÓN DE {operacion.upper()}**\n"
216
+ f"🔹 **Valor Sugerido:** ${promedio:,.0f}\n"
217
  f"📉 Mínimo Zona: ${minimo:,.0f}\n"
218
  f"📈 Máximo Zona: ${maximo:,.0f}"
219
  )
 
224
 
225
  # --- INTERFAZ GRÁFICA ---
226
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
227
+ gr.Markdown("## 🤖 TramitIA Pro: Tasador Inmobiliario Integral")
228
 
229
  with gr.Row():
230
  with gr.Column(scale=1):
231
+ op = gr.Radio(["Arriendo", "Venta"], label="¿Qué deseas hacer?", value="Arriendo")
232
+
233
  c = gr.Textbox(label="Ciudad", value="Barranquilla")
234
  b = gr.Textbox(label="Barrio (Ej: La Concepcion)", value="La Concepcion")
 
 
 
 
 
 
 
235
 
236
+ with gr.Row():
237
+ t = gr.Dropdown(["Apartamento", "Casa", "Bodega", "Lote", "Oficina"], label="Tipo", value="Apartamento")
238
+ a = gr.Number(label="Área M2 (Tu Cliente)", value=70)
239
+
240
+ gr.Markdown("### Filtros de Búsqueda")
241
+ with gr.Row():
242
+ m2_min = gr.Number(label="M2 Mínimo", value=10)
243
+ m2_max = gr.Number(label="M2 Máximo", value=200)
244
+
245
+ with gr.Row():
246
+ ascensor = gr.Checkbox(label="Con Ascensor")
247
+ piscina = gr.Checkbox(label="Con Piscina")
248
+
249
  with gr.Row():
250
  h = gr.Number(label="Habitaciones", value=3)
251
  ban = gr.Number(label="Baños", value=2)
 
255
  ["Menos de 1 año", "1 a 8 años", "9 a 15 años", "16 a 30 años", "Más de 30 años"],
256
  label="Antigüedad", value="1 a 8 años"
257
  )
258
+ btn = gr.Button("EJECUTAR ANÁLISIS COMERCIAL", variant="primary")
259
 
260
  with gr.Column(scale=2):
261
  res_fin = gr.Markdown("### 💰 El resultado aparecerá aquí...")
 
264
  with gr.TabItem("Tabla de Resultados"): out_df = gr.Dataframe()
265
  with gr.TabItem("Descargar PDF"): out_pdf = gr.File()
266
 
267
+ btn.click(motor_tramitia_final,
268
+ [op, b, c, a, m2_min, m2_max, t, h, ban, p, e, ascensor, piscina],
269
+ [msg, out_df, out_pdf, res_fin])
270
 
271
  demo.launch()