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:
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 FunctionIl terzo argomento di Open — False — 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:
{"price": 152.34, "currency": "USD", "symbol": "AAPL"}Il valore di un campo si può estrarre con una piccola funzione:
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 FunctionQuesto 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.
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 FunctionSe devi estrarre più elementi per classe o tag, serve un ciclo sulla collezione:
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 SubEspressioni regolari
A volte il valore cercato è annegato nel testo senza un involucro comodo. In quel caso ti salva RegExp:
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:
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 SubQuesto riversamento è decine di volte più veloce di un ciclo che scrive in ogni cella, soprattutto con l’aggiornamento dello schermo disattivato:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ... scraping e scrittura ...
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = TrueEsempio 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.
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.
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 FunctionL’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.txte 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.