Volver al blog
Guías
Raluca Penciuc3 de marzo de 202310 min de lectura

Cómo Web Scrape Idealista: Una Guía Completa (Actualización 2023)

Cómo Web Scrape Idealista: Una Guía Completa (Actualización 2023)

Requisitos previos

Antes de empezar, asegurémonos de que disponemos de las herramientas necesarias.

En primer lugar, descarga e instala Node.js desde el sitio web oficial, asegurándote de utilizar la versión Long-Term Support (LTS). Esto también instalará automáticamente Node Package Manager (NPM), que utilizaremos para instalar otras dependencias.

Para este tutorial, usaremos Visual Studio Code como nuestro Entorno de Desarrollo Integrado (IDE) pero puedes usar cualquier otro IDE de tu elección. Crea una nueva carpeta para tu proyecto, abre el terminal y ejecuta el siguiente comando para configurar un nuevo proyecto Node.js:

npm init -y

Esto creará un archivo package.json en el directorio de su proyecto, que almacenará información sobre su proyecto y sus dependencias.

A continuación, tenemos que instalar TypeScript y las definiciones de tipo para Node.js. TypeScript ofrece tipado estático opcional que ayuda a prevenir errores en el código. Para ello, ejecuta en el terminal

npm install typescript @types/node --save-dev

Puede verificar la instalación ejecutando:

npx tsc --version

TypeScript utiliza un archivo de configuración llamado tsconfig.json para almacenar las opciones del compilador y otros ajustes. Para crear este archivo en su proyecto, ejecute el siguiente comando:

npx tsc -init

Asegúrate de que el valor de "outDir" se establece en "dist". De esta forma separaremos los archivos TypeScript de los compilados. Puedes encontrar más información sobre este archivo y sus propiedades en la documentación oficial de TypeScript.

Ahora, crea un directorio "src" en tu proyecto, y un nuevo archivo "index.ts". Aquí es donde guardaremos el código de scraping. Para ejecutar código TypeScript tienes que compilarlo primero, así que para asegurarnos de que no olvidamos este paso extra, podemos usar un comando definido a medida.

Dirígete al archivo "package.json" y edita la sección "scripts" de la siguiente manera:

"scripts": {

    "test": "npx tsc && node dist/index.js"

}

De esta forma, cuando vayas a ejecutar el script, sólo tienes que escribir "npm run test" en tu terminal.

Por último, para raspar los datos del sitio web, utilizaremos Puppeteer, una librería de navegador headless para Node.js que permite controlar un navegador web e interactuar con sitios web de forma programática. Para instalarla, ejecuta este comando en el terminal:

npm install puppeteer

Es muy recomendable cuando se quiere asegurar la integridad de los datos, ya que hoy en día muchos sitios web contienen contenido generado dinámicamente. Si tienes curiosidad, puedes consultar antes de continuar la documentación de Puppeteer para ver completamente de lo que es capaz.

Localización de los datos

Ahora que ya tienes tu entorno configurado, podemos empezar a ver cómo extraer los datos. Para este artículo, he elegido extraer la lista de casas y apartamentos disponibles en una región de Toledo, España: https://www.idealista.com/pt/alquiler-viviendas/toledo/buenavista-valparaiso-la-legua/.

Vamos a extraer los siguientes datos de cada listado de la página:

  • la URL;
  • el título;
  • el precio;
  • los detalles (número de habitaciones, superficie, etc.);
  • la descripción

Puede ver toda esta información resaltada en la siguiente captura de pantalla:

Página de anuncios inmobiliarios de Idealista con las herramientas de desarrollo del navegador resaltando el código HTML de los campos de título, precio y descripción

Abriendo las Herramientas de Desarrollador en cada uno de estos elementos podrás notar los selectores CSS que usaremos para ubicar los elementos HTML. Si eres bastante novato en el funcionamiento de los selectores CSS, no dudes en consultar esta guía para principiantes.

Extracción de datos

Para empezar a escribir nuestro script, vamos a verificar que la instalación de Puppeteer ha ido bien:

import puppeteer from 'puppeteer';

async function scrapeIdealistaData(idealista_url: string): Promise<void> {

    

    // Launch Puppeteer

    const browser = await puppeteer.launch({

        headless: false,

    	  args: ['--start-maximized'],

    	  defaultViewport: null

    })

    // Create a new page

    const page = await browser.newPage()

    // Navigate to the target URL

    await page.goto(idealista_url)

    // Close the browser

    await browser.close()

}

scrapeIdealistaData("https://www.idealista.com/pt/alquiler-viviendas/toledo/buenavista-valparaiso-la-legua/")

Aquí abrimos una ventana del navegador, creamos una nueva página, navegamos a nuestra URL de destino y cerramos el navegador. En aras de la simplicidad y la depuración visual, abro la ventana del navegador maximizada en modo no headless.

Como todos los listados tienen la misma estructura y los mismos datos, podemos extraer toda la información de la lista completa de propiedades en nuestro algoritmo. Después de ejecutar el script, podemos recorrer todos los resultados y compilarlos en una sola lista.

Para obtener la URL de todas las propiedades, localizamos los elementos de anclaje con la clase "item-link". A continuación, convertimos el resultado en una matriz JavaScript y asignamos a cada elemento el valor del atributo "href".

// Extract listings location

const listings_location = await page.evaluate(() => {

    const locations = document.querySelectorAll('a.item-link')

    const locations_array = Array.from(locations)

    return locations ? locations_array.map(a => a.getAttribute('href')) : []

})

console.log(listings_location.length, listings_location)

A continuación, para los títulos, podemos hacer uso del mismo elemento de anclaje, salvo que esta vez extraeremos su atributo "title".

// Extract listings titles

const listings_title = await page.evaluate(() => {

    const titles = document.querySelectorAll('a.item-link')

    const titles_array = Array.from(titles)

    return titles ? titles_array.map(t => t.getAttribute('title')) : []

})

console.log(listings_title.length, listings_title)

Para los precios, localizamos los elementos "span" que tienen 2 nombres de clase: "item-price" y "h2-simulated". Es importante identificar los elementos lo más unívocamente posible, para no alterar el resultado final. También hay que convertirlo en un array y luego mapearlo a su contenido de texto.

// Extract listings prices

const listings_price = await page.evaluate(() => {

    const prices = document.querySelectorAll('span.item-price.h2-simulated')

    const prices_array = Array.from(prices)

    return prices ? prices_array.map(p => p.textContent) : []

})

console.log(listings_price.length, listings_price)

Aplicamos el mismo principio para los detalles de las propiedades, analizando los elementos "div" con el nombre de clase "item-detail-char".

// Extract listings details

const listings_detail = await page.evaluate(() => {

    const details = document.querySelectorAll('div.item-detail-char')

    const details_array = Array.from(details)

    return details ? details_array.map(d => d.textContent) : []

})

console.log(listings_detail.length, listings_detail)

Y por último, la descripción de las propiedades. Aquí aplicamos una expresión regular adicional para eliminar todos los caracteres de nueva línea innecesarios.

// Extract listings descriptions

const listings_description = await page.evaluate(() => {

    const descriptions = document.querySelectorAll('div.item-description.description')

    const descriptions_array = Array.from(descriptions)

    return descriptions ? descriptions_array.map(d => d.textContent.replace(/(\r\n|\n|\r)/gm, "")) : []

})

console.log(listings_description.length, listings_description)

Ahora deberías tener 5 listas, una por cada dato que hemos scrapeado. Como he mencionado antes, deberíamos centralizarlas en una sola. De esta manera, la información que hemos recopilado será mucho más fácil de procesar.

// Group the lists

const listings = []

for (let i = 0; i < listings_location.length; i++) {

    listings.push({

        url: listings_location[i],

        title: listings_title[i],

        price: listings_price[i],

        details: listings_detail[i],

        description: listings_description[i]

    })

}

console.log(listings.length, listings)

El resultado final debería ser el siguiente:

[

  {

    url: '/pt/inmueble/99004556/',

    title: 'Apartamento em ronda de Buenavista, Buenavista-Valparaíso-La Legua, Toledo',

    price: '750€/mês',

    details: '\n3 quart.\n115 m² área bruta\n2º andar exterior com elevador\nOntem \n',

    description: 'Apartamento para alugar na Ronda Buenavista, em Toledo.Três quartos e duas casas de banho, sala, cozinha, terraço, garagem e arrecadação....'

  },

  {

    url: '/pt/inmueble/100106615/',

    title: 'Moradia em banda em Buenavista-Valparaíso-La Legua, Toledo',

    price: '1.000€/mês',

    details: '\n4 quart.\n195 m² área bruta\nOntem \n',

    description: 'Magnífica casa geminada para alugar com 3 andares, 4 quartos aconchegantes, 3 banheiros, sala ampla e luminosa, cozinha totalmente equipa...'

  },

  {

    url: '/pt/inmueble/100099977/',

    title: 'Moradia em banda em calle Francisco Ortiz, Buenavista-Valparaíso-La Legua, Toledo',

    price: '800€/mês',

    details: '\n3 quart.\n118 m² área bruta\n10 jan \n',

    description: 'O REMAX GRUPO FV aluga uma casa mobiliada na Calle Francisco Ortiz, em Toledo.Moradia geminada com 148 metros construídos, distribuídos...'

  },

  {

    url: '/pt/inmueble/100094142/',

    title: 'Apartamento em Buenavista-Valparaíso-La Legua, Toledo',

    price: '850€/mês',

    details: '\n4 quart.\n110 m² área bruta\n1º andar exterior com elevador\n10 jan \n',

    description: 'Apartamento muito espaçoso para alugar sem móveis, cozinha totalmente equipada.Composto por 4 quartos, 1 casa de banho, terraço.Calefaç...'

  }

]

Eludir la detección de bots

Si ejecutas tu script al menos 2 veces durante el transcurso de este tutorial, puede que ya hayas notado esta molesta página:

Página de verificación antibots de Idealista que muestra una imagen de un rompecabezas con margaritas y una barra deslizante

Idealista utiliza DataDome como protección antibot, que incorpora un desafío CAPTCHA de GeeTest. Se supone que debes mover la pieza del puzle hasta completar la imagen, y entonces deberías ser redirigido de vuelta a tu página de destino.

Puedes pausar fácilmente tu script de Puppeteer hasta que resuelvas el reto usando este código:

await page.waitForFunction(() => {

    const pageContent = document.getElementById('main-content')

    return pageContent !== null

}, {timeout: 10000})

Esto le dice a nuestro script que espere 10 segundos a que un selector CSS especificado aparezca en el DOM. Debería ser suficiente para resolver el CAPTCHA y luego dejar que se complete la navegación.

Idealista ha bloqueado la página de acceso indicando que se ha detectado un uso sospechoso y que se ha bloqueado el acceso

...A menos que la página de Idealista te bloquee de todos modos.

Llegados a este punto, el proceso se ha vuelto más complejo y difícil, y ni siquiera has ampliado tu proyecto.

Como he mencionado antes, Idealista está protegido por DataDome. Recopilan múltiples datos del navegador para generar y asociarte con una huella digital única. Si sospechan, recibes el desafío CAPTCHA de arriba, que es bastante difícil de resolver automáticamente.

Entre los datos recogidos del navegador encontramos:

  • propiedades del objeto Navigator (deviceMemory, hardwareConcurrency, languages, platform, userAgent, webdriver, etc.)
  • controles de tiempo y rendimiento
  • WebGL
  • Escaneo de IP de WebRTC
  • registro de los movimientos del ratón
  • incoherencias entre el User-Agent y su sistema operativo
  • y muchos más.

Una forma de superar estos retos y continuar con el scraping a gran escala es utilizar una API de scraping. Este tipo de servicios proporcionan una forma sencilla y fiable de acceder a los datos de sitios web como Idealista.com, sin necesidad de construir y mantener tu propio scraper.

WebScrapingAPI es un ejemplo de este tipo de producto. Su mecanismo de rotación de proxy evita por completo los CAPTCHA, y su base de conocimientos ampliada permite aleatorizar los datos del navegador para que se parezca a un usuario real.

La configuración es rápida y sencilla. Lo único que tienes que hacer es registrar una cuenta para recibir tu clave API. Puedes acceder a ella desde tu panel de control y se utiliza para autenticar las solicitudes que envías.

Guía de inicio rápido del panel de control que muestra tres pasos: clave de acceso a la API, API Playground e integración en tu aplicación

Como ya has configurado tu entorno Node.js, podemos hacer uso del SDK correspondiente. Ejecuta el siguiente comando para añadirlo a las dependencias de tu proyecto:

npm install webscrapingapi

Ahora sólo queda ajustar los selectores CSS anteriores a la API. La potente función de reglas de extracción permite analizar datos sin modificaciones significativas.

import webScrapingApiClient from 'webscrapingapi';

const client = new webScrapingApiClient("YOUR_API_KEY");

async function exampleUsage() {

    const api_params = {

        'render_js': 1,

    	  'proxy_type': 'residential',

    	  'timeout': 60000,

    	  'extract_rules': JSON.stringify({

            locations: {

                selector: 'a.item-link',

                output: '@href',

                all: '1'

        	},

        	titles: {

                selector: 'a.item-link',

                output: '@title',

                all: '1'

        	},

        	prices: {

                selector: 'span.item-price.h2-simulated',

                output: 'text',

                all: '1'

        	},

        	details: {

                selector: 'div.item-detail-char',

                output: 'text',

                all: '1'

        	},

        	descriptions: {

                selector: 'div.item-description.description',

                output: 'text',

                all: '1'

        	}

        })

    }

    const URL = "https://www.idealista.com/pt/alquiler-viviendas/toledo/buenavista-valparaiso-la-legua/"

    const response = await client.get(URL, api_params)

    if (response.success) {

        // Group the lists

    	  const listings = []

    	  for (let i = 0; i < response.response.data.locations.length; i++) {

            listings.push({

                url: response.response.data.locations[i],

                title: response.response.data.titles[i],

                price: response.response.data.prices[i],

                details: response.response.data.details[i],

                description: response.response.data.descriptions[i].replace(/(\r\n|\n|\r)/gm, "")

        	})

    	  }

    	  console.log(listings.length, listings)

    } else {

        console.log(response.error.response.data)

    }

}

exampleUsage();

Conclusión

En este artículo, te hemos mostrado cómo scrapear Idealista, una popular web inmobiliaria española, usando TypeScript y Puppeteer. Hemos repasado el proceso de configuración de los requisitos previos y el scraping de los datos, y hemos comentado algunas formas de mejorar el código.

Web scraping Idealista puede proporcionar información valiosa para empresas y particulares. Utilizando las técnicas descritas en este artículo, puede extraer datos como URL de propiedades, precios y descripciones del sitio web.

Además, si quiere evitar las medidas antibot y la complejidad del proceso de scraping, utilizar un scraper profesional puede ser más eficaz y fiable que crear uno propio.

Siguiendo los pasos y las técnicas descritas en esta guía, podrá liberar el poder del web scraping Idealista y utilizarlo para satisfacer sus necesidades empresariales. Ya sea para la investigación de mercado, la generación de leads o la creación de nuevas oportunidades de negocio, el web scraping Idealista puede ayudarle a mantenerse por delante de la competencia.

Acerca del autor
Raluca Penciuc, desarrolladora full-stack en WebScrapingAPI
Raluca PenciucDesarrollador full-stack

Raluca Penciuc es desarrolladora full stack en WebScrapingAPI, donde se dedica a crear rastreadores, mejorar las técnicas de evasión y buscar formas fiables de reducir la detección en los sitios web de destino.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.