Molti report d'ufficio nascono allo stesso modo: si esportano i dati da un gestionale, li si incolla in un file Excel, si aggiungono totali, si formatta l'intestazione, si sistemano le larghezze e si invia il file. Ogni settimana. Con la libreria openpyxl uno script Python può produrre il file finito, già formattato e pronto da inviare, senza aprire Excel e anche su un computer dove Excel non è installato.
Che cosa fa openpyxl
openpyxl legge e scrive file .xlsx lavorando direttamente sulla loro struttura interna. Non controlla Excel come farebbe una macro: crea il file come lo creerebbe Excel. Questo ha una conseguenza importante: le formule vengono scritte ma non calcolate. Il risultato apparirà quando il file verrà aperto in Excel o in un altro foglio di calcolo, che lo ricalcolerà.
pip install openpyxl
Creare il file e scrivere i dati
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
ws.title = "Vendite"
ws.append(["Data", "Cliente", "Prodotto", "Quantità", "Prezzo", "Totale"])
vendite = [
("2025-03-03", "Rossi S.r.l.", "Licenza", 2, 120.0),
("2025-03-04", "Bianchi S.p.A.", "Assistenza", 5, 60.0),
]
for riga, (data, cliente, prodotto, qta, prezzo) in enumerate(vendite, start=2):
ws.append([data, cliente, prodotto, qta, prezzo, f"=D{riga}*E{riga}"])
wb.save("vendite_settimana.xlsx")
append aggiunge una riga in fondo al foglio. La colonna Totale contiene una formula vera, non un numero calcolato in Python: chi riceve il file vede da dove nasce il valore e può modificarlo. Le formule vanno scritte con i nomi delle funzioni in inglese e la virgola come separatore degli argomenti, per esempio =SUM(F2:F50), anche se il file verrà aperto in un Excel italiano, che le mostrerà come =SOMMA(F2:F50).
Date vere, non testo
Nell'esempio le date sono stringhe: Excel le vedrà come testo e non potrà ordinarle né filtrarle per mese. Basta passare oggetti date di Python e indicare il formato di visualizzazione:
from datetime import date
ws["A2"] = date(2025, 3, 3)
ws["A2"].number_format = "DD/MM/YYYY"
Lo stesso vale per gli importi: il numero resta un numero, e il formato decide come appare.
for cella in ws["E"][1:] + ws["F"][1:]:
cella.number_format = "#,##0.00 €"
Il codice di formato usa la convenzione inglese (virgola per le migliaia, punto per i decimali); sarà Excel a mostrarlo secondo le impostazioni locali di chi apre il file.
Formattare l'intestazione
Un report leggibile ha un'intestazione evidente. openpyxl mette a disposizione oggetti per carattere, riempimento e allineamento:
from openpyxl.styles import Font, PatternFill, Alignment
for cella in ws[1]:
cella.font = Font(bold=True, color="FFFFFF")
cella.fill = PatternFill("solid", fgColor="305496")
cella.alignment = Alignment(horizontal="center")
Larghezze, filtri e riquadri bloccati
Tre ritocchi che fanno sembrare il file preparato con cura:
larghezze = {"A": 12, "B": 24, "C": 18, "D": 10, "E": 12, "F": 14}
for colonna, larghezza in larghezze.items():
ws.column_dimensions[colonna].width = larghezza
ws.auto_filter.ref = ws.dimensions # filtro su tutta la tabella
ws.freeze_panes = "A2" # intestazione sempre visibile
openpyxl non ha un adattamento automatico delle colonne come il doppio clic di Excel, perché non conosce la larghezza reale dei caratteri. Si può approssimare calcolando la lunghezza del testo più lungo di ogni colonna, ma una tabella di larghezze fisse è spesso più semplice e prevedibile.
La riga dei totali
ultima = ws.max_row
ws.cell(row=ultima + 1, column=5, value="Totale").font = Font(bold=True)
ws.cell(row=ultima + 1, column=6, value=f"=SUM(F2:F{ultima})").font = Font(bold=True)
max_row restituisce l'ultima riga usata: il totale si adatta da solo al numero di righe di ogni settimana.
Aggiungere un grafico
openpyxl può inserire grafici nativi di Excel, modificabili da chi riceve il file:
from openpyxl.chart import BarChart, Reference
grafico = BarChart()
grafico.title = "Totale per vendita"
dati = Reference(ws, min_col=6, min_row=1, max_row=ultima)
categorie = Reference(ws, min_col=2, min_row=2, max_row=ultima)
grafico.add_data(dati, titles_from_data=True)
grafico.set_categories(categorie)
ws.add_chart(grafico, "H2")
Organizzare lo script
Nel mio lavoro divido sempre questi script in tre parti distinte: lettura dei dati, calcoli, scrittura del file. Se domani il gestionale cambia formato di esportazione, si modifica solo la prima parte; se il destinatario vuole un colore diverso, solo l'ultima. Conviene anche inserire la data nel nome del file, per esempio vendite_2025-03-10.xlsx, così le versioni non si sovrascrivono.
| Obiettivo | Strumento in openpyxl |
|---|---|
| Aggiungere righe | ws.append() |
| Formato numeri e date | cella.number_format |
| Stile intestazione | Font, PatternFill, Alignment |
| Filtri | ws.auto_filter.ref |
| Bloccare l'intestazione | ws.freeze_panes |
| Grafici | openpyxl.chart |
In sintesi
openpyxlcrea file Excel completi senza bisogno di Excel installato.- Le formule vengono scritte in inglese e calcolate all'apertura del file.
- Usate date e numeri veri con
number_format, non testo. - Intestazione formattata, filtri e riquadri bloccati rendono il report leggibile.
- Separate lettura, calcoli e scrittura per uno script facile da mantenere.