Formati e fonti di dati 9 min di lettura

Web scraping con Excel e VBA

Web scraping con Excel e VBA: richieste HTTP alle pagine, parsing di HTML e JSON e aggiornamento automatico delle tabelle senza programmi esterni.

TW
Team Web-Scraping.it
Raccolta dati per le esigenze del business
Pubblicato il 4 marzo 2025

L’estrazione di dati da internet si associa di solito a Python o a servizi specializzati. Ma se i dati devono finire subito in una tabella, essere elaborati con formule e mostrati ai colleghi, Excel insieme a VBA resta uno dei modi più rapidi di arrivare al risultato. Non serve installare un interprete, configurare un ambiente né spiegare in contabilità che cos’è pip install. Apri la cartella di lavoro, premi un pulsante — e i dati sono sul foglio.

In questo articolo vediamo come funziona lo scraping con VBA: quali oggetti usare per le richieste HTTP, come fare il parsing di HTML e JSON, come riversare il risultato nelle celle e come non incappare in un blocco. Gli esempi sono funzionanti: puoi copiarli nell’editor VBA ed eseguirli.

Quando Excel e VBA sono una buona scelta

Ha senso puntare su VBA quando:

  • il risultato vive comunque in Excel (un report, una dashboard, un registro di quotazioni);
  • il volume di dati è piccolo o medio — decine o migliaia di righe, non milioni;
  • serve un’automazione «a pulsante» per persone senza competenze di programmazione;
  • la fonte fornisce i dati tramite una semplice richiesta HTTP o una API aperta.

Se invece servono una scala seria, l’aggiramento di protezioni JavaScript complesse o richieste in parallelo, meglio guardare verso Python (requests, BeautifulSoup, Playwright). In quel tipo di compiti VBA tocca presto il suo tetto.

Gli strumenti dentro VBA

Per fare scraping in VBA esistono alcuni «motori» principali:

Oggetto Funzione Quando usarlo
MSXML2.XMLHTTP / ServerXMLHTTP Richieste HTTP Il modo principale di ottenere la risposta del server
WinHttp.WinHttpRequest.5.1 Richieste HTTP Alternativa con timeout configurabili
HTMLDocument (MSHTML) Parsing dell’HTML Quando servono elementi per tag/classi
RegExp (VBScript) Espressioni regolari Estrazione mirata dal testo
Split / InStr / Mid Funzioni di stringa Parsing semplice di JSON e testo senza librerie
QueryTables / Power Query Tabelle già pronte Quando la pagina fornisce una tabella HTML pulita

La maggior parte di questi oggetti si istanzia «al volo» con CreateObject, cioè senza aggiungere riferimenti al progetto a mano. È una comodità: la cartella di lavoro funziona su qualsiasi macchina con Excel.

La richiesta HTTP di base

Lo scraper più semplice si limita a ottenere il testo di una pagina. Ecco una funzione che esegue una richiesta GET e restituisce l’HTML o il JSON come stringa:

vba
Function GetResponse(ByVal url As String) As String
    Dim http As Object
    Set http = CreateObject("MSXML2.XMLHTTP")

    http.Open "GET", url, False
    ' Ci facciamo passare per un normale browser: molti siti tagliano le richieste senza User-Agent
    http.setRequestHeader "User-Agent", _
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
    http.send

    If http.Status = 200 Then
        GetResponse = http.responseText
    Else
        GetResponse = "ERROR: " & http.Status & " " & http.statusText
    End If

    Set http = Nothing
End Function

Il terzo argomento di OpenFalse — indica una richiesta sincrona: il codice attende la risposta. Per la maggior parte dei compiti è sufficiente. L’header User-Agent è critico: senza, una parte dei server restituisce un 403 o un captcha.

Parsing di JSON senza librerie

VBA non sa fare il parsing del JSON «di serie», ma per le risposte semplici bastano le funzioni di stringa. Supponiamo che l’API abbia restituito:

json
{"price": 152.34, "currency": "USD", "symbol": "AAPL"}

Il valore di un campo si può estrarre con una piccola funzione:

vba
Function ExtractJsonValue(ByVal json As String, ByVal key As String) As String
    Dim pattern As String
    Dim startPos As Long, endPos As Long

    pattern = """" & key & """:"
    startPos = InStr(json, pattern)
    If startPos = 0 Then Exit Function

    startPos = startPos + Len(pattern)
    ' Saltiamo la virgoletta se il valore è una stringa
    If Mid(json, startPos, 1) = """" Then startPos = startPos + 1

    ' La fine del valore è una virgola, una graffa di chiusura o una virgoletta
    endPos = startPos
    Do While endPos <= Len(json)
        Dim ch As String
        ch = Mid(json, endPos, 1)
        If ch = "," Or ch = "}" Or ch = """" Then Exit Do
        endPos = endPos + 1
    Loop

    ExtractJsonValue = Trim(Mid(json, startPos, endPos - startPos))
End Function

Questo approccio funziona con gli oggetti piatti. Se la struttura è annidata e complessa, meglio integrare un parser JSON già pronto per VBA (per esempio il modulo open source VBA-JSON di Tim Hall) — trasforma la risposta in Dictionary e Collection, con cui lavorare è molto più comodo.

La catena «richiesta HTTP → parsing del JSON → scrittura nella cella» è il modello di base su cui si costruisce lo scraping dei tassi di cambio. La maggior parte dei servizi delle banche centrali — compreso il feed dei tassi di riferimento della BCE — e delle API valutarie restituisce proprio JSON o XML, e la funzione di estrazione dei valori descritta sopra copre l’80% dei casi. L’analisi dettagliata di una soluzione completa con aggiornamento automatico a timer è nell’articolo «Scraping dei tassi di cambio».

Parsing dell’HTML con MSHTML

Quando i dati non stanno in una API ma direttamente nel markup della pagina, torna comodo l’oggetto HTMLDocument. Permette di cercare gli elementi come nel browser — per id, tag e classi.

vba
Function ParseHtmlElement(ByVal url As String, ByVal elementId As String) As String
    Dim http As Object, htmlDoc As Object
    Set http = CreateObject("MSXML2.XMLHTTP")

    http.Open "GET", url, False
    http.setRequestHeader "User-Agent", "Mozilla/5.0"
    http.send

    Set htmlDoc = CreateObject("htmlfile")
    htmlDoc.body.innerHTML = http.responseText

    Dim el As Object
    Set el = htmlDoc.getElementById(elementId)
    If Not el Is Nothing Then
        ParseHtmlElement = Trim(el.innerText)
    End If

    Set http = Nothing
    Set htmlDoc = Nothing
End Function

Se devi estrarre più elementi per classe o tag, serve un ciclo sulla collezione:

vba
Sub ParseAllRows(ByVal url As String)
    Dim http As Object, htmlDoc As Object
    Set http = CreateObject("MSXML2.XMLHTTP")
    http.Open "GET", url, False
    http.setRequestHeader "User-Agent", "Mozilla/5.0"
    http.send

    Set htmlDoc = CreateObject("htmlfile")
    htmlDoc.body.innerHTML = http.responseText

    Dim rows As Object, i As Long
    Set rows = htmlDoc.getElementsByTagName("tr")

    For i = 0 To rows.Length - 1
        ' Scriviamo il testo di ogni riga della tabella sul foglio, a partire dalla riga 2
        Cells(i + 2, 1).Value = Trim(rows.Item(i).innerText)
    Next i

    Set http = Nothing
    Set htmlDoc = Nothing
End Sub

Espressioni regolari

A volte il valore cercato è annegato nel testo senza un involucro comodo. In quel caso ti salva RegExp:

vba
Function ExtractByRegex(ByVal text As String, ByVal pattern As String) As String
    Dim re As Object
    Set re = CreateObject("VBScript.RegExp")
    re.Global = False
    re.IgnoreCase = True
    re.pattern = pattern

    Dim matches As Object
    Set matches = re.Execute(text)
    If matches.Count > 0 Then
        ' Restituiamo il primo gruppo di cattura
        ExtractByRegex = matches(0).SubMatches(0)
    End If

    Set re = Nothing
End Function

' Esempio: estrarre il numero da una stringa tipo "Prezzo: 152.34 EUR"
' value = ExtractByRegex(s, "Prezzo:\s*([\d\.]+)")

Scrittura del risultato sul foglio

Scrivere i dati nelle celle una alla volta è lento. Se le righe sono tante, accumulale in un array e riversale con un’unica assegnazione:

vba
Sub WriteArrayFast(data() As Variant)
    Dim n As Long
    n = UBound(data) - LBound(data) + 1
    ' Riversiamo l'intera colonna in una sola operazione
    Range("A1").Resize(n, 1).Value = Application.Transpose(data)
End Sub

Questo riversamento è decine di volte più veloce di un ciclo che scrive in ogni cella, soprattutto con l’aggiornamento dello schermo disattivato:

vba
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ... scraping e scrittura ...
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True

Esempio pratico: una tabella di quotazioni

Costruiamo un piccolo scraper che scorre una lista di ticker, richiede il prezzo a una API fittizia e riversa il risultato sul foglio.

vba
Sub ParseQuotes()
    Dim tickers As Variant
    tickers = Array("AAPL", "MSFT", "GOOGL", "TSLA")

    Dim i As Long, url As String, response As String, price As String

    ' Intestazioni della tabella
    Cells(1, 1).Value = "Ticker"
    Cells(1, 2).Value = "Prezzo"
    Cells(1, 3).Value = "Ora"

    Application.ScreenUpdating = False

    For i = LBound(tickers) To UBound(tickers)
        url = "https://example-api.com/quote?symbol=" & tickers(i)
        response = GetResponse(url)              ' la funzione della sezione precedente
        price = ExtractJsonValue(response, "price")

        Cells(i + 2, 1).Value = tickers(i)
        Cells(i + 2, 2).Value = Val(price)
        Cells(i + 2, 3).Value = Now

        ' Pausa tra le richieste per non sovraccaricare il server e non beccarsi un blocco
        Application.Wait Now + TimeValue("0:00:01")
    Next i

    Application.ScreenUpdating = True
    MsgBox "Fatto: caricate " & (UBound(tickers) + 1) & " quotazioni", vbInformation
End Sub

È uno scheletro semplificato. In pratica, per l’estrazione di quotazioni di borsa si aggiungono il parsing del volume degli scambi, della variazione percentuale e degli storici, oltre alla gestione dei weekend e degli orari di chiusura della borsa. L’implementazione completa, con aggiornamento automatico e formattazione condizionale, è nell’articolo «Estrazione di quotazioni di borsa».

Gestione degli errori e robustezza

Le richieste di rete falliscono: il server non risponde, scatta il timeout, arriva un formato inatteso. Lo scraper deve sopravvivere a tutto questo, non morire al primo errore.

vba
Function SafeGet(ByVal url As String, Optional retries As Long = 3) As String
    Dim attempt As Long
    For attempt = 1 To retries
        On Error Resume Next
        Dim http As Object
        Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
        http.SetTimeouts 5000, 5000, 10000, 10000   ' resolve, connect, send, receive
        http.Open "GET", url, False
        http.setRequestHeader "User-Agent", "Mozilla/5.0"
        http.send

        If Err.Number = 0 And http.Status = 200 Then
            SafeGet = http.responseText
            On Error GoTo 0
            Exit Function
        End If
        On Error GoTo 0

        ' La pausa prima del tentativo successivo cresce ogni volta
        Application.Wait Now + TimeValue("0:00:0" & attempt)
    Next attempt

    SafeGet = ""   ' tutti i tentativi sono esauriti
End Function

L’oggetto WinHttpRequest è qui più comodo di XMLHTTP proprio per il metodo SetTimeouts: permette di fissare in modo esplicito i limiti di attesa e di non restare appesi per sempre.

Etica e limiti

Qualche regola che fa risparmiare nervi e reputazione:

  • Leggi il robots.txt e le condizioni d’uso. Non tutti i siti permettono la raccolta automatica di dati.
  • Fai pause tra le richieste. Decine di richieste al secondo sembrano un attacco e portano al ban dell’IP.
  • Preferisci le API ufficiali. Se la fonte ha una API, usala: è più stabile e legale.
  • Non raccogliere dati personali senza base giuridica e senza consenso.
  • Metti in cache il risultato. Se il tasso di cambio si aggiorna una volta al giorno, non serve martellare il server ogni minuto.

L’alternativa senza codice: Power Query

Va detto che per molti compiti VBA non serve affatto. Il Power Query integrato in Excel (Dati → Recupera dati → Da Web) sa caricare tabelle HTML e risposte JSON dall’interfaccia, con aggiornamento automatico pianificato. Se la fonte fornisce una tabella pulita o una API REST senza autenticazioni astruse, Power Query risolve il compito più in fretta e senza una sola riga di codice. VBA resta per i casi in cui servono logica, diramazioni, cicli su una lista e un parsing fuori standard.

Conclusione

Lo scraping in VBA si costruisce con pochi mattoni: la richiesta HTTP (XMLHTTP o WinHttp), il parsing della risposta (funzioni di stringa, RegExp o HTMLDocument), la scrittura nelle celle e la gestione degli errori. Una volta padroneggiato questo set, puoi automatizzare la raccolta di quasi qualsiasi dato tabellare senza uscire dalla tua solita cartella di lavoro di Excel.

Due scenari classici su cui è comodo fare pratica:

  • Scraping dei tassi di cambio — una fonte JSON/XML semplice, ideale per il primo scraper.
  • Estrazione di quotazioni di borsa — un po’ più complessa: lista di ticker, aggiornamenti frequenti, formattazione.

Entrambi sono sviluppati in articoli dedicati — parti da quello più vicino al tuo compito; le funzioni descritte qui (GetResponse, ExtractJsonValue, SafeGet) saranno la base comune di entrambi.