Come estrarre dati dal sito immobiliare.it con Python

Lo scraping è una tecnica informatica che prevede l’estrazione automatica di informazioni da una pagina web. Esistono molti strumenti informatici in grado di implementare questa tecnica, come ad esempio il modulo di Python BeautifulSoup.

Il codice che segue è in grado di estrarre informazioni riguardanti le case in vendita presenti sul sito web Immobiliare.it. In particolare, il codice esegue una serie di operazioni per ciascun comune presente in un file di testo. Per ogni comune, il codice accede alla pagina web di Immobiliare.it relativa alle case in vendita del comune stesso. In seguito, il codice estrae i link relativi alle singole case in vendita, per poi accedere alla pagina di ogni singola casa e recuperare le informazioni riguardanti la stessa. Infine, il codice scrive le informazioni estratte su un file CSV.

Per fare ciò, il codice utilizza le librerie requests, per effettuare le richieste HTTP, e BeautifulSoup, per analizzare il contenuto HTML delle pagine web. In particolare, il codice utilizza la funzione Session() del modulo requests, che consente di mantenere alcune informazioni di sessione tra una richiesta e l’altra, come ad esempio i cookie. In questo modo, il codice può accedere alle pagine web come un browser web, in modo da evitare eventuali restrizioni imposte dal sito web.

Il codice utilizza inoltre il modulo csv per scrivere le informazioni estratte su un file CSV, e la funzione time.sleep() per inserire un ritardo tra una richiesta e l’altra, in modo da evitare eventuali blocchi imposti dal sito web per un accesso troppo frequente.

Il codice presenta alcune caratteristiche interessanti, come ad esempio la gestione dei casi in cui le informazioni non siano presenti o non siano state correttamente recuperate dal sito web. Ad esempio, se la descrizione di una casa non è disponibile, il codice la sostituisce con una stringa vuota. Inoltre, il codice è in grado di recuperare anche alcune informazioni aggiuntive, come ad esempio il numero di posti auto o le caratteristiche della casa.

import requests
from bs4 import BeautifulSoup
import csv
import time


class ImmobiliareScraper:
    def __init__(self, output_file):
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299"
        })
        outfile = open(output_file, "w", newline="")
        self.writer = csv.writer(outfile)
        self.writer.writerow(["Comune", "Titolo", "Descrizione", "Prezzo",
                              "Superficie", "Locali", "Bagni", "Piano", "Contratto", "Tipologia", "Posti_Auto", "Disponibilita", "Altre_Caratteristiche"])

        self.sleep_time = 0.5

    def write_immobile(self, immobile):
        if 'title' not in immobile.keys():
            immobile['title'] = ''
        if 'description' not in immobile.keys():
            immobile['description'] = ''
        if 'prezzo' not in immobile.keys():
            immobile['prezzo'] = ''
        if 'superficie' not in immobile.keys():
            immobile['superficie'] = ''
        if 'locali' not in immobile.keys():
            immobile['locali'] = ''
        if 'bagni' not in immobile.keys():
            immobile['bagni'] = ''
        if 'piano' not in immobile.keys():
            immobile['piano'] = ''
        if 'contratto' not in immobile.keys():
            immobile['contratto'] = ''
        if 'tipologia' not in immobile.keys():
            immobile['tipologia'] = ''
        if 'posti_auto' not in immobile.keys():
            immobile['posti_auto'] = ''
        if 'disponibilità' not in immobile.keys():
            immobile['disponibilità'] = ''
        if 'altre_caratteristiche' not in immobile.keys():
            immobile['altre_caratteristiche'] = ''

        self.writer.writerow([immobile['comune'], immobile['title'], immobile['description'], immobile['prezzo'], immobile['superficie'], immobile['locali'], immobile['bagni'], immobile['piano'],
                              immobile['contratto'], immobile['tipologia'], immobile['posti_auto'], immobile['disponibilità'], immobile['altre_caratteristiche']])

    def scrape(self, comuni_file):
        with open(comuni_file, "r") as f:
            comuni = f.read().splitlines()
            for comune in comuni:
                comune = comune.lower().replace(' ', '-')
                self.extract_links(comune)

    def extract_links(self, comune):
        url = f"https://www.immobiliare.it/vendita-case/{comune}/"
        print(url)
        page = self.session.get(url)
        soup = BeautifulSoup(page.content, "html.parser")
        self.extract_data(page, comune)
        try:
            max_page_num = soup.find_all('div', {
                'class': 'in-pagination__item hideOnMobile in-pagination__item--disabled'})[-1].text
        except:
            max_page_num = 1
        if max_page_num >= 2:
            for i in range(2, int(max_page_num)):
                url = f"https://www.immobiliare.it/vendita-case/{comune}/?pag={i}"
                page = self.session.get(url)
                self.extract_data(page, comune)

    def extract_data(self, page, comune):
        soup = BeautifulSoup(page.content, "html.parser")
        for item in soup.select(".in-card__title"):
            immobile = {}
            item_url = item["href"]
            print(item_url)
            item_page = self.session.get(item_url)
            item_soup = BeautifulSoup(item_page.content, "html.parser")
            # file = open(item_url.replace('/', '-')+'.html', 'wb')
            # file.write(item_page.content)
            # file.close()
            # json_string = item_soup.find_all('script', {'id': '__NEXT_DATA__'})[0].text.strip()
            # data = json.loads(json_string)
            # print(data)
            immobile['comune'] = comune
            for dt, dd in zip(item_soup.find_all('dt'), item_soup.find_all('dd')):
                try:
                    immobile[dt.text.strip().lower().replace(' ', '_')
                             ] = dd.text.strip()
                except:
                    piano = ''

            title = item_soup.h1.text
            immobile['title'] = title
            try:
                description = item_soup.select_one(
                    '.in-readAll--lessContent').text.strip()
            except:
                print("description is not defined")
                description = ''
            immobile['description'] = description
            try:
                locali = item_soup.find(
                    'li', {'aria-label': 'locali'}).text.strip()
            except:
                print("locali is not defined")
                locali = ''
            immobile['locali'] = locali
            try:
                superficie = item_soup.find(
                    'li', {'aria-label': 'superficie'}).text.strip()
            except:
                print("superficie is not defined")
                superficie = ''
            immobile['superficie'] = superficie
            try:
                bagni = item_soup.find(
                    'li', {'aria-label': 'bagni'}).text.strip()
            except:
                print("bagni is not defined")
            try:
                bagni = item_soup.find(
                    'li', {'aria-label': 'bagno'}).text.strip()
            except:
                print("bagno is not defined")
                bagni = ''
            immobile['bagni'] = bagni
            try:
                piano = item_soup.find(
                    'li', {'aria-label': 'piano'}).text.strip()
            except:
                print("bagno is not defined")
                piano = ''
            immobile['piano'] = piano
            print(immobile)
            self.write_immobile(immobile)
            time.sleep(self.sleep_time)


if __name__ == "__main__":
    scraper = ImmobiliareScraper("immobili.csv")
    scraper.scrape("comuni-italia.txt")

Il metodo write_immobile viene utilizzato per scrivere un singolo record su file CSV. In caso di mancanza di informazioni, i valori mancanti vengono impostati a una stringa vuota. Il metodo scrape è il metodo principale della classe e riceve come argomento il nome di un file contenente i nomi dei comuni su cui effettuare la ricerca. Il software effettua quindi la navigazione sul sito Immobiliare.it e estrae i dati di interesse per ogni annuncio immobiliare trovato.

Il metodo extract_links estrae tutti i link delle pagine degli annunci trovati per un determinato comune. Inoltre, il metodo estrae il numero di pagine presenti per quel comune. Se il numero di pagine è maggiore di 1, il software estrae anche i link delle pagine successive.

Il metodo extract_data estrae i dati di interesse da una singola pagina di un annuncio immobiliare. Il software estrae il titolo, la descrizione, il prezzo, la superficie, il numero di locali, il numero di bagni, il piano, il tipo di contratto, la tipologia dell’immobile, il numero di posti auto, la disponibilità e altre caratteristiche dell’immobile.

Il codice può essere utilizzato per creare un dataset che possa essere utilizzato per addestrare algoritmi di deep learning. In particolare, il dataset sarà costituito da una serie di colonne che contengono informazioni sui beni immobili in vendita su Immobiliare.it.

Il dataset sarà composto da 13 colonne con i seguenti nomi:

  • Comune
  • Titolo
  • Descrizione
  • Prezzo
  • Superficie
  • Locali
  • Bagni
  • Piano
  • Contratto
  • Tipologia
  • Posti_Auto
  • Disponibilita
  • Altre_Caratteristiche

In sintesi, questo codice Python permette di creare un dataset contenente informazioni su beni immobiliari in vendita su Immobiliare.it, che possono essere utilizzati per addestrare algoritmi di deep learning.