Pliki CSV i Python - odczyt i zapis
Niezwykle ważny temat, wręcz fundament dla każdego administratora sklepu internetowego, czyli pliki z danymi - tym razem na warsztat bieżemy te w formacie CSV. Omówię na szybko jak w prosty sposób zacząć z nich korzystać i ożenić je z naszymi skryptami w Pythonie. Skupie się na ich odczycie i zapisie danych.
Ciut teorii i informacji o plikach CSV
Chociaż podejrzewam Was o znajomość z plikiem tego rodzaju, to jednak zaryzykuję i wytłumaczę któż on zaś.
CSV (ang. comma-separated values, wartości rozdzielone przecinkiem) – format przechowywania danych w plikach tekstowych i odpowiadający mu typ MIME text/csv.
I to w zasadzie tyle z definicji o formacie CSV, bo w tym zdaniu zawarte są najistotniejsze informacje:
- Są to pliki tekstowe (można bez problemu je otworzyć w przysłowiowym notatniku)
- Wartości oddzielone są przecinkiem (lub innym separatorem np. tabulatorem, średnikiem)
- Jest to format przechowywania danych w plikach typu text/csv (to znaczy, że jest to jakiś znany i uznawany format)
- Dodam, że jest on chyba jednym z najprostszym i najbardziej rozpowszechnionym (na chwilę obecną) formatem (XML może być równie popularny, ale na pewno jest bardziej skomplikowany).
Przykładowy plik CSV może wyglądać tak (otwierając go w najprostszym edytorze tekstu):
kolumna 1,kolumna 2,kolumna 3
wartosc1_kol1,wartosc1_kol2,wartosc1_kol3
wartosc2_kol1,wartosc2_kol2,wartosc2_kol3
wartosc3_kol1,wartosc3_kol2,wartosc3_kol3
Ok, fajnie, tylko co nam to daje? A no skoro jest to szeroko rozpowszechniony format, możemy korzystać z jego konstrukcji w różnych aplikacjach, jak np. MS Excel czy LibreOffice Calc, które automatycznie sformatują go do postaci tabeli.
Na pewno wielką zaletą formatu CSV, jest uniwersalny charakter, możemy w nim przechowywać wszystko to, co może zostać zapisane w formie tekstu (znaki, ciągi znaków, liczby) i skorzysta na postaci w formie tabeli (czyli nazwa kolumny i wartość). Wiele aplikacji, takich jak sklepy internetowe, korzystają z tego formatu do wymiany informacji z użytkownikiem (bo jest przyjazny człowiekowi) ale również między innymi serwisami (np. hurtownia wystawia stany magazynowe klientom).
Co na to Python?
Jak zwykle, mamy gotowy moduł w standardowej bibliotece służący do obsługi tego typu formatu. Jego nazwy nikt pewnie się nie domyślił, więc powiem że chodzi o moduł csv. Standardowo zaczynamy od jego importu.
Odczyt
Skoro jest to plik tekstowy, to otwieramy go identycznie jak standardowy plik tekstowy (thanks Captain Obvious!).
Zatrzymajmy się tutaj na chwilę, ponieważ musimy omówić istotną sprawę - kodowanie.
Chodzi o to, aby przy odczycie z pliku polskich znaków (litery diakrytyzowane), nie zmieniały się one w tzw. krzaki. Temat kodowania, zwłaszcza w Pythonie, to temat rzeka, który zasługuje na nowy wpis (a nawet serię). Co ważne, musicie pamiętać, że wszystkie wpisy na blogu traktują o najnowszej wersji Pythona (3.5+). W skrócie, wersja Python 2 w inny sposób traktuje typ string
a co za tym idzie i jego kodowanie.
Kolejnym istotnym pojęciem jest Unicode (UTF-8), znowu w skrócie, jest to uniwersalny zestaw znaków (jakby zbiór) w komputerach, stworzony by zawierał wszystkie znaki ze wszystkich języków. Tyle teorii. W praktyce, obecnie większość plików tekstowych zapisujemy właśnie w kodowaniu UTF-8
, nie bawimy się już w ISO 8859-2
(mam nadzieję). Daje to możliwość umieszczania w takim tekcie wszelkich liter diakrytyzowanych, z różnych języków, i nikt nie musi się głowić “hmm.. krzaki… jakie tu jest kodowanie?”.
Dlatego dla uproszczenia: Zapisujmy pliki w kodowaniu UTF-8, i otwierajmy je też w UTF-8.
Otwierając plik, chcemy wskazać jego kodowanie (nie musi to być w każdym przypadku utf-8
, ktoś mógł użyc innego kodowania do zapisu tego pliku, ale jeżeli są krzaki to najłatwiej jest spróbować otworzyć z utf-8
).
Kontynuując, pamiętajmy jak wygląda format pliku CSV, jest on nie tylko podzielony na kolumny ale również na wiersze, to też będziemy czytać po kolei, następujące po sobie linie.
output:
kolumna 1
kolumna 2
kolumna 3
wartosc1_kol1
wartosc1_kol2
wartosc1_kol3
wartosc2_kol1
wartosc2_kol2
wartosc2_kol3
wartosc3_kol1
wartosc3_kol2
wartosc3_kol3
Dlaczego odwołujemy się do row[n]
? Każdy wiersz zczytywany jest do listy, na przykład:
W pliku:
wartosc1_kol1,wartosc1_kol2,wartosc1_kol3
W Pythonie:
[wartosc1_kol1, wartosc1_kol2, wartosc1_kol3]
Jak widać możemy odwoływać się do indeksów wartości tej listy. Co jeżeli nie znamy numeru kolumny w której występuje interesująca nas wartość, a jedynie jej nazwę?
Kolumny mają swoje nazwy zdefiniowane w pierwszym wierszu pliku CSV. Wystarczy, że zamiast do zwykłej listy (tablica 2D), wczytamy kolejne wiersze jako słownik (dict) - co da nam strukturę w stylu
{nazwa_kolumny: wartość, ..., nazwa_kolumny_n: wartość_n}
W tym celu musimy użyć bardziej wyrafinowanego czytacza.
output:
wartosc1_kol1
wartosc1_kol2
wartosc1_kol3
wartosc2_kol1
wartosc2_kol2
wartosc2_kol3
wartosc3_kol1
wartosc3_kol2
wartosc3_kol3
Zapis
Łatwo się domyślić, że to co tutaj omówię, będzie po prostu odwróceniem procesu odczytu. Żeby nie przeciągać od razu zacznę od kodu i go omówię.
Metoda writerow()
zapisuje linię do pliku. To znaczy, że możemy ją zapętlić i zapisywać nieokreśloną ilość danych. Trywialny przykład (i również bezsensowny), czyli jak zapisać tabliczkę mnożenia (rozmiar 10x10):
A teraz troche bardziej praktyczny przykład, czyli zapis struktur do pliku CSV (typu dict
)
Zauważ, że nie musimy wypełniać wartości dla wszystkich kolumn w danym wierszu, ale musimy zdefiniować wszystkie używane kolumny (header).
CSV to oczywiście nie jedyny format danych, warto przy okazji wspomnieć tutaj o XML, JSON, YAML i innych, ale to materiał na następne wpisy, które na pewno się pojawią na moim blogu. Zachęcam więc do śledzenia wpisów.