Scraping simple de datos con Python

 Como mencioné brevemente en la publicación anterior, uno de mis primeros proyectos personales con python consistió en scrapear avisos de venta de propiedades, específicamente departamentos, para analizar el departamento más conveniente en términos de relación precio/m2. Como además soy curioso, programé la captura de algunos datos adicionales con el objetivo de explorar posteriormente los datos.

Con el objetivo de compartir el script, limpié (bastante) el código e incluso actualicé algunas de las lógicas utilizadas para capturar ciertos datos en específico. El proyecto actualizado, así como los datos de este ejemplo, pueden encontrarse en mi repositorio de GitHub: https://github.com/dfarfanr/scraper_yapo21

Lo primero, es cargar las librerías necesarias para la ejecución del script. 

En general, nadie define al inicio de un proyecto las librerías exactas que usará. Éstas se van descubriendo a medida que se va programando y avanzando en el problema. 

Las librerías a utilizar son estándar a la hora de trabajar con strings scrapeados de sitios web.

from bs4 import BeautifulSoup
import requests
import csv
import re
import datetime

 Lo siguiente, en términos simples, es la definición de una función que nos permita obtener la data de los avisos. En este punto es muy importante señalar que cuando se trata del scraping de datos de sitios web, la solución que se implemente será muy específica, dependiendo del sitio web que queramos scrapear. En este caso, sólo explicaré los conceptos más importantes a fin de mantener una coherencia en términos de la explicación de lo que realiza la función.

La primera parte de la función, está definida de la siguiente manera:

def obtener_avisos():
    url = "https://www.yapo.cl/chile/comprar?ca=15_s&l=0&cmn="
    request = requests.get(url)
    soup = BeautifulSoup(request.text, "html.parser")
    ultima_pagina_link = soup.find('span',{'class':'nohistory FloatRight'}).find('a').get('href')
    ultima_pagina = int(ultima_pagina_link.split("o=",1)[1])
    
    print("Resultados encontrados! Cantidad de paginas a scrapear: " + str(ultima_pagina))
  
    total_data = []

Se define en la variable "url", un enlace específico que nos permitirá obtener la información de los avisos que queremos capturar. 

Además, dentro del mismo código html, encontamos un enlace a la última página con avisos, en este caso la 1003 (ver ejemplo de ejecución más abajo). 

Es muy importante notar, que es necesario emplear nuestra habilidad de detectar patrones para obtener información relevante para nuestro algoritmo. Dentro del sitio web, el atributo "href" del botón a la última página, nos muestra un enlace a un sitio web terminado en "...&o=1003", donde el n° 1003 hace referencia al número de página, además de inferir que a través la configuración del parámetro "o" en la URL, podemos solicitar ver el número de página que deseemos. Y esto es justamente algo que aprovecharemos en el script.

Además, definimos una variable de tipo array vacía en la que posteriormente iremos almacenando los datos que se vayan capturando.

Cabe señalar que una de las características importantes de este sitio web, es que gracias a su diseño, en el sitio que contiene el catálogo de avisos, se muestran datos relevantes y suficientes para el objetivo de este trabajo. Esto nos ahorra el tener que ir por cada enlace de venta a la vez, y nos permite obtener los datos para 50 avisos de venta con sólo scrapear una URL. (esto se entenderá a profundidad cuando se revise el diseño del sitio web a scrapear: https://www.yapo.cl/chile/comprar?ca=15_s&l=0&cmn="

La parte siguiente, es un bucle for anidado dentro de otro. Uno, recorre cada una de las páginas de avisos de venta (desde la 1 hasta la 1.003 para efectos de este ejemplo), y el otro, recorre cada uno de los elementos dentro de esas páginas, scrapeando los datos de cada uno de los avisos. 

    for i in range(1,ultima_pagina+1):
        url_page = url + "&st=a&o=" + str(i)
        print(url_page)
        data = requests.get(url_page)
        soup_inner = BeautifulSoup(data.text, "html.parser")
        
        for row in soup_inner.find_all('tr', {'class':'ad listing_thumbs'}):
            a = {}
            #col = row.find_all('td')
            try: a['id_anuncio'] = row.get('id').encode("utf-8") 
            except: pass
            
            try: a['enlace'] = row.find_all('td')[0].find('a').get('href').encode("utf-8") 
            except: pass
            
            try: a['titulo'] = row.find_all('td')[2].find('a').text.encode("utf-8")
            except: pass
            
            try: a['precio'] = re.sub(r'(^[ \t]+|[ \t]+(?=:))', '', row.find_all('td')[2].find('span', {'class':'price'}).text, flags=re.M).replace("\n", "")
            except: pass
            
            try: a['precio2'] = re.sub(r'(^[ \t]+|[ \t]+(?=:))', '', row.find_all('td')[2].find('span', {'class':'convertedPrice'}).text, flags=re.M).replace("\n", "")
            except: pass
            
            datos_iconos = row.find_all('td')[2]
            
            try: a['banos'] = int(str(datos_iconos).split('fa-bath',1)[1].split('</span>')[0].split('-text">')[1])
            except: pass
            
            try: a['dormitorios'] = int(str(datos_iconos).split('fa-bed',1)[1].split('</span>')[0].split('-text">')[1])
            except: pass
            
            try: a['superficie'] = BeautifulSoup(str(datos_iconos).split('fa-expand',1)[1], "html.parser").text.replace(' icons__element-icon">','').replace("\n", "")
            except: pass
            
            datos_ubicacion = row.find_all('td')[3]
            
            try: a['region'] = str(datos_ubicacion).split('class="region">')[1].split('</span>')[0]
            except: pass
            
            try: a['comuna'] = str(datos_ubicacion).split('class="commune">')[1].split('</span>')[0]
            except: pass
            
            try: a['company_ad'] = str(datos_ubicacion).split('title="Aviso profesional">')[1].split('</span>')[0]
            except: pass
            
            try: a['ubicacion'] = datos_ubicacion.find('i').get('class')[1]
            except:pass
            
            total_data.append(a)

Al término del segundo bucle, añadimos la información obtenida en cada uno de los sitios scrapeados, a nuestro array "total_data", el que acumula toda la data obtenida de los avisos de venta, pero que posteriormente será exportado. 

Por otro lado, el nombre de las variables del diccionario se autoexplica. Su definición, sin embargo, son transformaciones simples utilizando expresiones regulares, operaciones para strings y navegación en html con BeautifulSoup.

La parte final de nuestra función, es la definición de un script encargado de volcar nuestro array en un archivo .csv. Para ello, se implementó un script que escribe fila por fila los n elementos contenidos en el array, y posteriormente se exporta. 

    keys = total_data[0].keys()
    now = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S")
    csvname = "data "
    with open(str(csvname)+str(now)+'.csv','w', newline='') as output_file:
        dict_writer = csv.DictWriter(output_file, keys)
        dict_writer.writeheader()
        dict_writer.writerows(total_data)
    print("Terminado!")
    print("Los resultados pueden ser encontrados en el archivo "+str(csvname)+str(now)+".csv")

Para la ejecución del script, se deberá simplemente llamar a la función "obtener_avisos", de la siguiente manera:

obtener_avisos()

Los prints definidos durante la ejecución del script, tienen el siguiente aspecto: 

Finalmente, la base obtenida tiene más de 50.000 registros, y aún trabajo por recibir, debido a que no tiene los datos en un formato adecuado para trabajarlos directamente.

import pandas as pd
df = pd.read_csv('data 2021-04-29 22-28-17.csv',encoding='latin-1')
df

Vista simple de los datos:

id_anuncio enlace titulo precio precio2 banos dormitorios superficie region comuna company_ad ubicacion
0 b'77981818' b'https://www.yapo.cl/region_metropolitana/com... b'Espacioso departamento 2d + 2b en Plaza \xc3... UF 5.975,00 ($ 176.133.679) 2.0 2.0 66 m2/70 m2 Región Metropolitana Ñuñoa Aviso profesional icon-location
1 b'77981811' b'https://www.yapo.cl/maule/comprar/terreo_en_... b'Terreo en pelluhue' $ 8.500.000 (UF 288,34) NaN NaN 110 m2 VII Maule Pelluhue NaN NaN
2 b'77981804' b'https://www.yapo.cl/valparaiso/comprar/casa_... b'casa' $ 120.000.000 (UF 4.070,77) 1.0 2.0 108 m2/220 m2 V Valparaíso Papudo NaN NaN
3 b'77981793' b'https://www.yapo.cl/region_metropolitana/com... b'casa 72 m2 ' $ 110.000.000 (UF 3.731,54) 1.0 NaN 72 m2/280 m2 Región Metropolitana Buin NaN icon-location
4 b'77981792' b'https://www.yapo.cl/region_metropolitana/com... b'terreno en parque del recuerdo' $ 4.000.000.000 (UF 135.692,39) NaN NaN NaN Región Metropolitana Peñaflor NaN NaN
5 b'77981785' b'https://www.yapo.cl/atacama/comprar/busco_ca... b'busco Casa con subsidio' $ 52.000.000 (UF 1.764,00) 1.0 3.0 NaN III Atacama Copiapó NaN NaN
6 b'77981761' b'https://www.yapo.cl/los_lagos/comprar/amplia... b'Amplia casa, parque fundadores' $ 85.000.000 (UF 2.883,46) 2.0 3.0 NaN X Los Lagos Puerto Montt Aviso profesional NaN
7 b'77981727' b'https://www.yapo.cl/region_metropolitana/com... b'Departamento en \xc3\x91u\xc3\xb1oa 82mts2 .... UF 6.500,00 ($ 191.609.860) 2.0 2.0 82 m2 Región Metropolitana Ñuñoa Aviso profesional icon-location
8 b'77981718' b'https://www.yapo.cl/araucania/comprar/parcel... b'Parcela Pucon Km 7 camino al Volcan' $ 50.000.000 (UF 1.696,15) NaN NaN 5000 m2 IX Araucanía Pucón NaN icon-location
9 b'77981715' b'https://www.yapo.cl/nuble/comprar/sitio_400_... b'sitio 400 m2 Chillan Viejo' $ 3.000.000 (UF 101,76) NaN NaN 400 m2 XVI Ñuble Chillán Viejo NaN NaN
10 b'77484888' b'https://www.yapo.cl/los_lagos/comprar/parcel... b'Parcela en Puerto Varas, Club Campo Residenc... UF 1.700,00 ($ 50.113.348) NaN NaN 5050 m2 X Los Lagos Puerto Varas NaN NaN
11 b'77894564' b'https://www.yapo.cl/nuble/comprar/casa_excel... b'Casa excelente estado sector Libertad Oriente' $ 90.000.000 (UF 3.053,07) 1.0 3.0 79 m2/240 m2 XVI Ñuble Chillán NaN NaN
12 b'77874284' b'https://www.yapo.cl/los_lagos/comprar/habita... b'Habitacional y Comercial' $ 212.047.128 (UF 7.193,29) 3.0 3.0 136 m2/164 m2 X Los Lagos Puerto Montt NaN NaN
13 b'77981623' b'https://www.yapo.cl/valparaiso/comprar/depto... b'Depto condominio Mirador de Re\xc3\xb1aca Vi... $ 44.000.000 (UF 1.492,61) 1.0 3.0 56 m2 V Valparaíso Viña del Mar NaN NaN
14 b'77981620' b'https://www.yapo.cl/maule/comprar/terreno_en... b'Terreno en r\xc3\xado claro yumbel' $ 15.000.000 (UF 508,84) NaN NaN NaN VII Maule Río Claro NaN NaN
15 b'77981548' b'https://www.yapo.cl/region_metropolitana/com... b'Casa Ciudad Sat\xc3\xa9lite Maipu' UF 9.990,00 ($ 294.489.615) 3.0 NaN NaN Región Metropolitana Peñaflor NaN NaN
16 b'77981542' b'https://www.yapo.cl/region_metropolitana/com... b'1d/ 1b/ estacionamiento/ metro toesca dpto' UF 2.550,00 ($ 75.170.022) 1.0 1.0 36 m2 Región Metropolitana Santiago Aviso profesional NaN
17 b'76691059' b'https://www.yapo.cl/region_metropolitana/com... b'Casa' $ 70.000.000 (UF 2.374,61) 2.0 3.0 122 m2/119 m2 Región Metropolitana Melipilla NaN NaN
18 b'77800105' b'https://www.yapo.cl/araucania/comprar/parcel... b'Parcela a 20 minutos de Villarrica' $ 18.000.000 (UF 610,61) NaN NaN 5000 m2 IX Araucanía Loncoche NaN NaN
19 b'77908963' b'https://www.yapo.cl/region_metropolitana/com... b'Preciosa casa en excelente barrio' UF 13.550,00 ($ 399.432.862) 3.0 NaN 160 m2/200 m2 Región Metropolitana Las Condes Aviso profesional icon-location
20 b'77880951' b'https://www.yapo.cl/valparaiso/comprar/compr... b'compro casa' $ 20.000.000 (UF 678,46) 1.0 2.0 NaN V Valparaíso Viña del Mar NaN NaN
21 b'77875816' b'https://www.yapo.cl/region_metropolitana/com... b'casa ' $ 94.000.000 (UF 3.188,77) 2.0 NaN 200 m2 Región Metropolitana Recoleta NaN NaN
22 b'77981483' b'https://www.yapo.cl/coquimbo/comprar/linda_c... b'Linda Casa El Milagro La Serena' $ 117.000.000 (UF 3.969,00) 2.0 NaN 101 m2 IV Coquimbo La Serena Aviso profesional NaN
23 b'77981477' b'https://www.yapo.cl/araucania/comprar/busco_... b'Busco departamento' $ 120.000.000 (UF 4.070,77) 2.0 3.0 NaN IX Araucanía Temuco NaN NaN
24 b'77981450' b'https://www.yapo.cl/antofagasta/comprar/corr... b'Corredora terreno 300 m2, pob. alemania' $ 90.000.000 (UF 3.053,07) NaN NaN 300 m2 II Antofagasta Calama Aviso profesional NaN
25 b'77981445' b'https://www.yapo.cl/antofagasta/comprar/corr... b'Corredora terrenos en Ayllu de Checar, S. P' $ 180.000.000 (UF 6.106,15) NaN NaN 1 ha II Antofagasta San Pedro de Atacama Aviso profesional NaN
26 b'77981423' b'https://www.yapo.cl/antofagasta/comprar/cons... b'consulta m\xc3\xa9dica edificio master 27 m2' UF 2.500,00 ($ 73.696.100) NaN NaN 27 m2 II Antofagasta Calama Aviso profesional NaN
27 b'77981419' b'https://www.yapo.cl/antofagasta/comprar/lind... b'linda casa 3d 3b 4e cond. altos del loa' $ 230.000.000 (UF 7.802,31) 3.0 3.0 NaN II Antofagasta Calama Aviso profesional NaN
28 b'77981415' b'https://www.yapo.cl/antofagasta/comprar/lind... b'linda casa 3d 3b 4e cond. altos del loa' $ 230.000.000 (UF 7.802,31) 3.0 3.0 NaN II Antofagasta Calama Aviso profesional NaN
29 b'77981408' b'https://www.yapo.cl/coquimbo/comprar/busco_c... b'Busco casa' $ 15.000.000 (UF 508,84) NaN NaN NaN IV Coquimbo Los Vilos NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ...
50107 b'42416043' b'https://www.yapo.cl/valparaiso/comprar/sitio... b'SITIO san Juan/Borde de carretera' UF 30.000,00 ($ 861.495.600) NaN NaN 4 ha V Valparaíso San Antonio Aviso profesional NaN
50108 b'47607365' b'https://www.yapo.cl/valparaiso/comprar/sitio... b'SITIO Carretera/' $ 150.000.000 (UF 5.088,46) NaN NaN 1 ha V Valparaíso San Antonio Aviso profesional icon-location
50109 b'47607199' b'https://www.yapo.cl/valparaiso/comprar/sitio... b'SITIO Antonio Nu\xc3\xb1ez Fonseca/Punta Panul' $ 400.000.000 (UF 13.569,23) NaN NaN 2 ha V Valparaíso San Antonio Aviso profesional icon-location
50110 b'52440115' b'https://www.yapo.cl/region_metropolitana/com... b'DEPARTAMENTO Padre Hurtado Sur/Patagonia' UF 8.700,00 ($ 249.833.724) 3.0 3.0 110 m2 Región Metropolitana Las Condes Aviso profesional icon-location
50111 b'42753517' b'https://www.yapo.cl/valparaiso/comprar/casa_... b'CASA Sector los Huasos/Sto Domingo' UF 9.850,00 ($ 282.857.722) NaN NaN 254 m2/1090 m2 V Valparaíso Santo Domingo Aviso profesional icon-location
50112 b'55594654' b'https://www.yapo.cl/maule/comprar/agricola_t... b'AGRICOLA Teno / Autopista del Maipo/Morza' UF 18.380,00 ($ 527.809.637) NaN NaN 1 ha VII Maule Curicó Aviso profesional icon-location
50113 b'42745146' b'https://www.yapo.cl/region_metropolitana/com... b'CASA Carrascal/Lo Espinoza' $ 400.000.000 (UF 13.569,23) 2.0 NaN 381 m2/1632 m2 Región Metropolitana Quinta Normal Aviso profesional icon-location
50114 b'55594630' b'https://www.yapo.cl/maule/comprar/sitio_indu... b'SITIO INDUSTRIAL Teno / Autopista del Maipo/... UF 18.380,00 ($ 527.809.637) NaN NaN 1 ha VII Maule Curicó Aviso profesional icon-location
50115 b'59168858' b'https://www.yapo.cl/region_metropolitana/com... b'Sitio VALLE ESCONDIDO \xe2\x80\xa6' UF 18.404,00 ($ 507.320.247) NaN NaN 2464 m2 Región Metropolitana Lo Barnechea Aviso profesional icon-location
50116 b'59168626' b'https://www.yapo.cl/region_metropolitana/com... b'Sitio VALLE ESCONDIDO \xe2\x80\xa6' UF 33.080,00 ($ 911.875.340) NaN NaN 5194 m2 Región Metropolitana Lo Barnechea Aviso profesional icon-location
50117 b'59169062' b'https://www.yapo.cl/region_metropolitana/com... b'Casa CONDOMINIO EL ALMENDRO , CIUDAD EMPRES\... UF 19.800,00 ($ 545.802.048) NaN NaN 209 m2/1200 m2 Región Metropolitana Huechuraba Aviso profesional icon-location
50118 b'59168022' b'https://www.yapo.cl/region_metropolitana/com... b'Departamento MUSEO RALLI SANTIAGO \xe2\x80\xa6' UF 12.100,00 ($ 333.545.696) NaN 3.0 130 m2 Región Metropolitana Vitacura Aviso profesional icon-location
50119 b'59168664' b'https://www.yapo.cl/region_metropolitana/com... b'Sitio VALLE ESCONDIDO \xe2\x80\xa6' UF 20.910,00 ($ 576.400.041) NaN NaN 2040 m2 Región Metropolitana Lo Barnechea Aviso profesional icon-location
50120 b'48135581' b'https://www.yapo.cl/region_metropolitana/com... b'Casa PASEO DE ALCALA CON PANORAMICA SUR \xe2... $ 1.500.000.000 (UF 50.884,64) NaN NaN 838 m2/3056 m2 Región Metropolitana Lo Barnechea Aviso profesional icon-location
50121 b'42815929' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Carrascal/Lo Espinoza' $ 400.000.000 (UF 13.569,23) NaN NaN 1632 m2 Región Metropolitana Quinta Normal Aviso profesional icon-location
50122 b'47619669' b'https://www.yapo.cl/region_metropolitana/com... b'DEPARTAMENTO Gran Avenida/Tercera Avenida' UF 2.070,00 ($ 59.443.196) 1.0 1.0 31 m2/31 m2 Región Metropolitana San Miguel Aviso profesional icon-location
50123 b'47620214' b'https://www.yapo.cl/region_metropolitana/com... b'DEPARTAMENTO Quinta Avenida/Tercera Transver... UF 3.000,00 ($ 86.149.560) 2.0 3.0 62 m2/66 m2 Región Metropolitana San Miguel Aviso profesional icon-location
50124 b'53145715' b'https://www.yapo.cl/region_metropolitana/com... b'LOCAL COMERCIAL Carlos Fernandez Concha/Sie\... UF 10.400,00 ($ 298.651.808) NaN NaN 684 m2/378 m2 Región Metropolitana San Joaquín Aviso profesional icon-location
50125 b'47620318' b'https://www.yapo.cl/region_metropolitana/com... b'DEPARTAMENTO Quinta Avenida/Tercera Transver... UF 2.960,00 ($ 85.000.899) 2.0 3.0 62 m2/66 m2 Región Metropolitana San Miguel Aviso profesional icon-location
50126 b'44402639' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Santiaguillo / San Diego Santiago' UF 6.000,00 ($ 174.759.300) NaN NaN 200 m2 Región Metropolitana Santiago Aviso profesional icon-location
50127 b'47607260' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Manquecura/El Golf de Manquhue' UF 22.900,00 ($ 657.608.308) NaN NaN 2544 m2 Región Metropolitana Lo Barnechea Aviso profesional icon-location
50128 b'56234741' b'https://www.yapo.cl/region_metropolitana/com... b'CASA Alvaro Casanova/Condominio Club Campo N... UF 16.512,54 ($ 474.182.685) NaN NaN 220 m2/665 m2 Región Metropolitana Peñalolén Aviso profesional icon-location
50129 b'42416071' b'https://www.yapo.cl/valparaiso/comprar/local... b'LOCAL COMERCIAL Edwards/victoria' UF 67.544,00 ($ 1.939.628.626) NaN NaN 2000 m2 V Valparaíso Valparaíso Aviso profesional icon-location
50130 b'42416054' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Recoleta/Rengifo' $ 130.000.000 (UF 4.410,00) NaN NaN 262 m2 Región Metropolitana Recoleta Aviso profesional icon-location
50131 b'42415973' b'https://www.yapo.cl/region_metropolitana/com... b'CASA Recoleta/Rengifo' $ 157.000.000 (UF 5.325,92) 1.0 3.0 90 m2/262 m2 Región Metropolitana Recoleta Aviso profesional NaN
50132 b'47607703' b'https://www.yapo.cl/region_metropolitana/com... b'LOCAL COMERCIAL Vicu\xc3\xb1a Mackenna/Geron... $ 89.990.000 (UF 3.052,73) NaN NaN 90 m2/90 m2 Región Metropolitana La Florida Aviso profesional icon-location
50133 b'42416066' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Rojas Magallanes/Avenida Per\xc3\xba' UF 14.800,00 ($ 425.004.496) NaN NaN 914 m2 Región Metropolitana La Florida Aviso profesional icon-location
50134 b'47652381' b'https://www.yapo.cl/region_metropolitana/com... b'CASA Tabancura Vitacura' UF 25.000,00 ($ 728.163.750) NaN NaN 238 m2/390 m2 Región Metropolitana Vitacura Aviso profesional icon-location
50135 b'48162433' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Carretera San Mart\xc3\xadn/Portezuelo' UF 4,80 ($ 137.839) NaN NaN 1 ha Región Metropolitana Colina Aviso profesional icon-location
50136 b'42769168' b'https://www.yapo.cl/region_metropolitana/com... b'SITIO Chilo\xc3\xa9/Pedro Lagos' UF 40.000,00 ($ 1.102.630.400) NaN NaN 1600 m2 Región Metropolitana Santiago Aviso profesional icon-location

50137 rows × 12 columns

Comentarios

Entradas populares