mitmario.dev

Excel-Dateien

Python Sandbox 4 Min Lesezeit 3 BeispieleLektion 4 von 5

Eine CSV öffnet Excel ohne Murren. Mehr aber auch nicht. Wie die Tabelle danach aussieht, entscheidet Excel selbst. Die Spalten sind so schmal, dass die Hälfte der Namen abgeschnitten ist, die Spaltennamen sind nicht fett, und ob aus einem Betrag eine Zahl wird, hängt davon ab, wie der Rechner eingestellt ist. Eine CSV kennt eben nur Text.

Eine Excel-Datei (.xlsx) weiß dagegen von jeder Zelle, ob sie eine Zahl ist und wie sie aussehen soll, und von jeder Spalte, wie breit sie ist. Mit der Standardbibliothek lassen sich solche Dateien nicht schreiben. Dafür gibt es das Paket openpyxl, und das kann sie auch lesen.

Eine Mappe schreiben und lesen

Eine Mappe schreiben und lesen
from decimal import Decimal

from openpyxl import Workbook, load_workbook

mappe = Workbook()
blatt = mappe.active
blatt.title = "2026"
blatt.append(["Posten", "Betrag"])
blatt.append(["Heizung", Decimal("1302.65")])
blatt.append(["Hausmeister", Decimal("240.00")])
mappe.save("nebenkosten.xlsx")

gelesen = load_workbook("nebenkosten.xlsx")
print(gelesen.sheetnames)
for zeile in gelesen.active.iter_rows(values_only=True):
    print(zeile)

Eine Excel-Datei heißt Arbeitsmappe (workbook) und hat ein oder mehr Blätter (worksheet). Workbook() legt eine neue Mappe an, erst mal nur im Speicher. Darin ist ein leeres Blatt, und das bekommst du über mappe.active. Es heißt Sheet, bis man ihm mit blatt.title einen Namen gibt.

append hängt unten eine Zeile an, als Liste mit einem Wert je Spalte. Die Datei selbst entsteht erst mit save.

Ansehen kannst du die Mappe hier nicht, auf dieser Maschine gibt es kein Excel. Deshalb wird sie im Beispiel gleich wieder eingelesen. load_workbook öffnet eine Mappe, sheetnames nennt ihre Blätter, und iter_rows(values_only=True) gibt jede Zeile als Tupel ihrer Werte zurück.

Beim Hausmeister steht 240 statt 240.00. Eine Zahl in Excel trägt keine Nachkommastellen mit sich herum. Wie viele angezeigt werden, ist eine Frage des Formats.

Zahl oder Text

Zahl oder Text
from decimal import Decimal

from openpyxl import Workbook, load_workbook

mappe = Workbook()
blatt = mappe.active
blatt.append(["Wasser", Decimal("298.40")])
blatt.append(["Hausmeister", "240,00"])
mappe.save("nebenkosten.xlsx")

gelesen = load_workbook("nebenkosten.xlsx")
for name, betrag in gelesen.active.iter_rows(values_only=True):
    print(name, repr(betrag), type(betrag).__name__)

Für die CSV hat jeder Betrag ein Komma bekommen, als Text. In einer Excel-Datei ist genau das der Fehler. '240,00' kommt als str zurück, also als Text.

Excel zeigt so eine Zelle linksbündig an, mit einem kleinen grünen Dreieck in der Ecke und dem Hinweis, hier sei eine Zahl als Text gespeichert. Und SUMME über die Spalte lässt sie einfach weg. Wer die Summe nachrechnet, findet den Fehler. Wer ihr glaubt, nicht.

Ein Betrag geht deshalb als Zahl in die Mappe, als Decimal, float oder int. Das Komma setzt nachher Excel.

Format, Breite und Fett

Format, Breite und Fett
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Font

mappe = Workbook()
blatt = mappe.active
blatt.append(["Zähler", "Verbrauch"])
blatt.append(["Wohnung", 2384.5])
blatt.append(["Waschküche", 96])
blatt["A1"].font = Font(bold=True)
blatt["B1"].font = Font(bold=True)
for zelle in blatt["B"][1:]:
    zelle.number_format = '#,##0.0 "kWh"'
blatt.column_dimensions["A"].width = 12
mappe.save("verbrauch.xlsx")

gelesen = load_workbook("verbrauch.xlsx").active
for zeile in gelesen.iter_rows():
    for zelle in zeile:
        print(zelle.coordinate, repr(zelle.value), zelle.number_format, zelle.font.bold)
print("Spalte A:", gelesen.column_dimensions["A"].width)

Wie eine Zahl aussieht, steht in ihrem Zahlenformat, number_format. #,##0.0 "kWh" heißt mit Trennzeichen für die Tausender, eine Stelle hinter dem Komma und dahinter die Einheit. Für Euro mit zwei Stellen wäre es #,##0.00 "€".

Das Format schreibt man immer mit den amerikanischen Zeichen, also Komma für die Tausender und Punkt vor den Nachkommastellen. Excel zeigt es dann so an, wie der Rechner eingestellt ist, auf einem deutschen also 2.384,5 kWh und 96,0 kWh. In der Datei bleibt die Zahl, was sie ist, nämlich 2384.5 und 96. blatt["B"] sind alle Zellen der Spalte B, und [1:] lässt die Zeile mit den Spaltennamen weg.

Die Breite der Spalte A setzt blatt.column_dimensions["A"].width, gemessen ungefähr in Zeichen. Ohne eigene Breite ist eine Spalte in Excel gut acht Zeichen breit. Ein längerer Name ragt dann in die Spalte daneben oder wird abgeschnitten. Hier reicht die 12 für Waschküche mit seinen zehn Zeichen.

Fett wird eine Zelle mit Font(bold=True) aus openpyxl.styles, im Beispiel A1 und B1, jede für sich. So wie blatt["B"] die Zellen einer Spalte sind, sind blatt[1] alle Zellen der ersten Zeile. Beim Einlesen ist alles wieder da. number_format und font.bold hängen an jeder Zelle, die Breite an der Spalte.

Zu Hause

Installiert wird openpyxl in der Umgebung des Projekts, mit python -m pip install openpyxl, und in die requirements.txt kommt es auch. Unter Windows klappt save nur, wenn die Datei nicht gerade in Excel offen ist. Sonst endet das Programm mit einem PermissionError. Die Abhilfe ist so einfach, wie sie klingt. Excel zumachen und das Programm noch einmal starten.

Was die Aufgabe will

Der Kontoauszug aus Lektion 12.6, diesmal als Excel-Datei. Die Ausgaben je Kategorie stehen darin mit fetten Spaltennamen, im Euro-Format und in einer Spalte, die breit genug ist. Danach liest pruefen.py die Mappe Zelle für Zelle ein, so wie im letzten Beispiel, nur gründlicher. Die Datei liegt im Dateibaum. Wer wissen will, wie man eine Mappe einliest, die jemand anderes geschrieben hat, findet es dort.

Zum Mitnehmen

Mit openpyxl schreibt und liest Python echte Excel-Dateien. Workbook() legt eine Mappe an, mappe.active ist das erste Blatt, append() hängt Zeilen an, save() speichert und load_workbook() liest die Datei wieder ein. Beträge kommen als Zahl in die Mappe, nie als Text mit Komma, und wie sie aussehen, bestimmt number_format.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.