Dateien herunterladen und in einem Verzeichnis ablegen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Das hat nichts mit irgendetwas auf dem Server zu tun. Die unkomprimierten Daten passen einfach nicht in den Arbeitsspeicher. Welche Größe haben denn die Daten unkomprimiert? Du darfst eben nicht alles auf einmal lesen, sondern mußt in Blöcken arbeiten.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Eine Datei unkomprimiert ist zwischen 100 und 130 Megabyte groß. Das sind doch gepackte Dateien im gzip Format, welche zwischen 8,5 und 10 Megabyte groß sind. Also mehr RAM und nur einige wenige Daten herunterladen. Pause machen und dann weder starten. Meinst Du das mit in Blöcken arbeiten?
BlackJack

@aaron: Pause machen hilft nicht wenn man den Speicher voll müllt und nach der Pause damit weiter macht. Es sieht so aus als wenn Du mehr als eine dieser Dateien gleichzeitig entpackt im Speicher hältst, das ist/wird dann halt problematisch. 5 Dateien entpackt sind dann 500 bis 650 MiB, plus vielleicht die ungepackten Daten — wir wissen ja nicht was Du da genau machst.

Du darfst halt immer nur eine Datei zu einer Zeit verarbeiten, also herunterladen, entpacken, speichern. Und dann vielleicht auch nicht die entpackten Daten im Speicher halten. Das macht ja sowieso nicht so viel Sinn. Im `shutil`-Modul gibt es Funktionen die dabei behilflich sein können die Daten direkt vom `GzipFile`-Objekt in ein Dateiobjekt zu kopieren, wobei sie ja auch gleich entpackt werden. Und diese Funktionen lesen auch nicht die komplette Quelldatei in den Speicher, sondern kopieren blockweise.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Vielen Dank für den Hinweis auf das `shutil`-Modul.
@aaron: Pause machen hilft nicht wenn man den Speicher voll müllt und nach der Pause damit weiter macht. Es sieht so aus als wenn Du mehr als eine dieser Dateien gleichzeitig entpackt im Speicher hältst, das ist/wird dann halt problematisch. 5 Dateien entpackt sind dann 500 bis 650 MiB, plus vielleicht die ungepackten Daten — wir wissen ja nicht was Du da genau machst.
Ich habe ein Gitlab Repo angelegt. Hilfe, Tipps, Tricks und konstruktive Kritik sind sehr willkommen.
https://github.com/joergklein/Datawarehouse.git

Ich lerne gerade Handling Exceptions. Entschuldigt bitte meine Fragen. Ich bin Anfänger.
BlackJack

@aaron: Also bei mir läuft das gerade ohne Probleme. Der Traceback verrät ja das es um das Jahr 2015 ging und das Programm ist gerade bei ``data/EURUSD/2015/39.csv.gz``.

Wieviel RAM hast Du denn in dem Rechner?
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Vielen Dank das Du dir die Zeit nimmst Dir meine Idee anzusehen. Dank der Hilfe von Sirius3. Der Rechner auf dem ich den Test ausgeführt habe hat 64GB RAM und als Betriebssystem verwende ich CentOS 7. Ich werde alle Daten vom 01.01. 1999 (Erster Handel mit dem Euro) bis heute von 40 Währungspaaren archivieren. Das ist eine sehr große Sammlung an Daten, die in einer Datenbank archiviert werden sollen. Das macht circa 5 Terabyte an Datenvolumen.
BlackJack

Also das lief bei mir problemlos durch ohne Speicherprobleme. Kann ich auch nicht nachvollziehen wie das passieren konnte. :K
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Vielen Dank für den Test. Ich denke, daß es dennoch sinnvoll ist das `shutil`-Modul zu verwenden. Was denkst Du darüber?
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@aaron: Ich habe das jetzt mal für 2015 durchlaufen lassen, gleichfalls ohne Probleme. Der Speicherverbrauch des Python-Prozesses lag dabei zwischen 20 und 170 MB.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Die entpackte Datenmenge lässt sich auf etwa die Hälfte reduzieren. Zumindest bei mir entpacken sich die .gz Dateien zu csv-Dateien im UTF-16 little endian Format.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Danke für das Testen und die gute Nachricht, daß sich die Größe der entpackten Dateien um die Hälfte reduzieren läßt.
Ich bin gerade dabei mich mit dem exception handlung zu beschäftigen. Ich habe folgende Fragen. Sollte ich zu jeder Funktion ein exeption handling schreiben? Ich habe zu Testzwecken ein Symbol gewählt, von dem ich weiß, das dieses nicht auf dem Server vorhanden ist. Der IO Error wird mir angezeigt. Der ValueError nicht, weil die Information von der Funktion main() and die Funktion fetch_single_week übergeben wird. Ist das exception handling für die eine Funktion halbwegs richtig?

Ich bekomme folgende Fehlermeldung:

Code: Alles auswählen

python TickDataCsv27.py 
Traceback (most recent call last):
  File "TickDataCsv27.py", line 90, in <module>
    main()
  File "TickDataCsv27.py", line 87, in main
    fetch_single_week("EURCAD", 2017, 1)
  File "TickDataCsv27.py", line 48, in fetch_single_week
    except IOError as (errno, strerror):
ValueError: need more than 0 values to unpack

Code: Alles auswählen

def fetch_single_week(symbol, year, week):
    try:
        if not exists_file(symbol, year, week):
            data = pull_file(symbol, year, week)
            print_data_length(data)
            save_file(symbol, year, week, data)
        else:
            print("File for {}/{}/{} already fetched.".format(symbol, year, week))
    except IOError as (errno, strerror):
        print "I/O error({0}): {1}".format(errno, strerror)
    except ValueError:
        print "The requested file does not exist on the server."
    except:
        print "Unexpected error:", sys.exec_info()[0]
        raise
        
def main():
    # "symbol", year, week
    fetch_single_week("EURCAD", 2017, 1)

if __name__ == '__main__':
    main()

Zuletzt geändert von Anonymous am Donnerstag 23. März 2017, 10:46, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@aaron: Man sollte nur Ausnahmen behandeln die man tatsächlich sinnvoll behandeln kann.

Das mit dem `ValueError` habe ich nicht verstanden? An welcher Stelle genau erwartest Du den und unter welchen Umständen? Ausnahmebehandlung sollte nicht zu viel Code behandeln, das gilt um so mehr je generischer die Ausnahme ist. `ValueError` kann ja alles mögliche bedeuten, das also die Ausgegebene Meldung stimmt das die Datei nicht auf dem Server existiert, halte ich für ziemlich gewagt. Das kann auch andere Gründe haben.

Tuple unpacking auf Ausnahmen anwenden ist vielleicht keine so gute Idee. So etwas habe ich schon ziemlich lange nicht mehr gesehen — wo hast Du diese Idee denn her? Da muss man etwas über die Anzahl und Reihenfolge der Argumente der `__init__()` von der jeweiligen Ausnahme wissen, und wenn das nicht stimmt, weil sich etwas an der Ausnahme selbst geändert hat oder man einen abgeleiteten Typ mit einer anderen/erweiterten `__init__()`-Signatur bekommt, dann funktioniert das nicht mehr. Und `IOError` verhält sich an der Stelle ja anscheinend noch nicht einmal wie erwartet, also nicht einmal die ”message” ist dort entpackbar.

Der letzte ``except``-Zweig macht sehr wahrscheinlich keinen Sinn.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich habe die Idee von hier: http://www.python-kurs.eu/ausnahmebehandlung.php

In der main() Funktion steht folgende Zeile fetch_single_week("EURCAD", 2017, 1). Das Symbol ist zur Zeit wirklich nicht auf dem Server vorhanden. In welcher Funktion baue ich nun den ersten try Block ein? Ich möchte danach ein Logfile erstellen und per Email versenden.
BlackJack

@aaron: Das ist mit Vorsicht zu geniessen. An der Stelle steht mitten in dem deutschsprachigen Tutorial englischer Text‽ Nun ja, und es muss halt nicht Stimmen das sich Ausnahmen wie Tupel verhalten, und selbst wenn sie das tun, gibt es die von mir angesprochenen Probleme mit Anzahl und Reihenfolge der Elemente.

Über sinnvolle Fehlerbehandlung steht da auch nicht viel, nur wie das halt syntaktisch aussieht. Und das nackte ``except:`` zur Sicherheit am Ende was eine Ausgabe mit ``print`` macht und dann die Ausnahme wieder auslöst, ist auch eher für die Demonstration der Syntax und nicht etwas was man in jedem Fall an jede Ausnahmebehandlung anhängen sollte.

Bei dem kurzen Abschnitt über ``assert`` irritiert mich das damit etwas ”gefangen” wird, was ja das Gegenteil von ”werfen” ist. Aber genau das macht ``assert``: Ausnahmen ”werfen”. Und mir fehlt auch der deutliche Hinweis, dass der Ausdruck bei ``assert`` keine Effekte haben darf die Einfluss auf das Programm haben, weil nicht garantiert ist, dass ``assert``-Anweisungen überhaupt ausgeführt werden. Es wäre auch die Erklärung nett gewesen das ``assert`` nur Sachen überprüfen sollte von denen der Programmierer an der Stelle ausgeht das die immer wahr sind, weil sonst innerhalb des Programms ein Fehler sein muss, und nicht beispielsweise zum Prüfen von Fehlern in der Benutzereingabe. Der Abschnitt erklärt die Syntax, aber danach denke ich nicht das ein Anfänger weiss was er mit der Anweisung machen sollte und was nicht.

Welche Fehlerbehandlung wo Sinn macht, kommt ganz darauf an wo und wie Du eine Ausnahme behandeln möchtest. Als erstes müsstest Du schauen welche Ausnahme Du behandeln musst. Wenn der Ort wo Du die letztendlich behandeln möchtest weiter entfernt ist von der Stelle wo sie auftritt und die Ausnahme sehr generisch ist, musst Du wahrscheinlich sogar an zwei Stellen ansetzen und selbst eine spezifischere Ausnahme einführen. In diesem Fall vermute ich allerdings einen `urllib2.HTTPError` wenn die Daten nicht auf dem Server existieren. Der ja gleichzeitig ein `IOError` ist, allerdings mit einem leeren `args`-Tupel, was dann genau zu dem Problem führt das Du das nicht auf zwei Namen entpacken kannst. Eine `errno` hat der auch gar nicht, beziehungsweise ist die in diesem Fall `None`. Das Attribut existiert da nur weil es von `IOError` geerbt ist. Den HTTP-Fehlercode bekommt man von der Ausnahme mit der `getcode()`-Methode wenn man bei der Behandlung des Fehlers testen möchte ob es ein 404 war oder etwas anderes.

Für Logfiles würde sich das `logging`-Modul aus der Standardbibliothek anbieten. Logger haben dort auch eine Methode um Ausnahmen zu protokollieren. Zudem kann man das auch so konfigurieren das die Ausgaben an mehreren Stellen landen, also beispielsweise in einer Datei und auf der Standardfehlerausgabe des Prozesses, so dass man sich redundante ``print``-Ausgaben komplett sparen kann.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Vielen Dank für die ausführliche Antwort. Ich brauche ein deutschsprachiges Handbuch. Ein Link reicht völlig. Das welches ich hier erwähnt habe scheint nicht wirklich gut zu sein. Aktuell arbeite ich noch mit Python 2.7.x. Das Script wird es später auch in der Python Version 3.5.x geben.
Für Logfiles würde sich das `logging`-Modul aus der Standardbibliothek anbieten. Logger haben dort auch eine Methode um Ausnahmen zu protokollieren. Zudem kann man das auch so konfigurieren das die Ausgaben an mehreren Stellen landen, also beispielsweise in einer Datei und auf der Standardfehlerausgabe des Prozesses, so dass man sich redundante ``print``-Ausgaben komplett sparen kann.
Wenn ich dich richtig verstanden habe, dann sollte ich mein Augenmerk auf das `logging`-Modul aus der Standardbibliothek legen? Wer von euch hat so ein ähnliches Problem schon einmal gelöst?

Hier eine Idee für urllib2.HTTPError

Code: Alles auswählen

import urllib2
try:
   urllib2.urlopen("some url")
except urllib2.HTTPError as err:
   if err.code == 404:
       print „The requested URL [URL] was not found on this server.“
   else:
       raise
Zuletzt geändert von Anonymous am Donnerstag 23. März 2017, 13:47, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@aaron: Wenn man Python 2 und 3 unterstützen möchte sind zwei unabhängige Programme IMHO die letzte Wahl. Ich würde da eher eine Quelltextbasis schreiben die mit beiden Versionen läuft. Da gibt es auch Bibliotheken die dabei helfen, wie `future` oder `six`. Und bei HTTP-Kram das externe `requests` statt der `urllib*`-Module aus der Standardbibliothek.

Zur Ausnahmebehandlung: Ja so würde das ungefähr syntaktisch aussehen und nein das macht natürlich so keinen Sinn, ausser die Aufgabe ist tatsächlich nur: Mache eine Ausgabe wenn keine Ressource für 'some url' gefunden wird.

Statt deutschem Handbuch wäre Englisch lernen wahrscheinlich die bessere Alternative. Denn es wird keine gute und aktuelle deutsche Übersetzung der gesamten Python-Dokumentation geben. Schon gar nicht wenn man Bibliotheken ausserhalb der Standardbibliothek mit hinzu zieht. Auch Domänenwissen, beispielsweise über HTTP steht direkt in der Regel erst einmal in Englisch zur Verfügung. Ob es dafür dann Übersetzungen gibt und wie gut die sind…
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich habe jetzt folgendes versucht:

Code: Alles auswählen

def pull_file(symbol, year, week):
    try:
        url = URL_TEMPLATE.format(symbol, year, week)
        response = urllib2.urlopen(url)
        return response.read()
    except urllib2.HTTPError, e:
        print e.code
    except urllib2.URLError, e:
        print e.args
Der 404 Fehler und Not a gzipped file wird angezeigt. Jetzt sollte sich das Programm normal beenden, ohne den Traceback ... Ist die Idee jetzt richtig? Die Ausgabe sieht jetzt so aus:

Code: Alles auswählen

404
Traceback (most recent call last):
  File "TickDataCsv27.py", line 85, in <module>
    main()
  File "TickDataCsv27.py", line 82, in main
    fetch_single_week("EURCAD", 2017, 1)
  File "TickDataCsv27.py", line 46, in fetch_single_week
    print_data_length(data)
  File "TickDataCsv27.py", line 32, in print_data_length
    data = f.read()
  File "/usr/lib64/python2.7/gzip.py", line 254, in read
    self._read(readsize)
  File "/usr/lib64/python2.7/gzip.py", line 296, in _read
    self._read_gzip_header()
  File "/usr/lib64/python2.7/gzip.py", line 190, in _read_gzip_header
    raise IOError, 'Not a gzipped file'
IOError: Not a gzipped file
Zuletzt geändert von Anonymous am Donnerstag 23. März 2017, 15:15, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: das ist nicht richtig, weil Du ja im Fehlerfall nichts sinnvolles machst. An der Stelle muß also eine Fehlerbehandlung eine andere Exception werfen, die dann weiter oben verarbeitet werden kann, oder gleich ganz entfallen.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

BlackJack hat geschrieben:@aaron: Wenn man Python 2 und 3 unterstützen möchte sind zwei unabhängige Programme IMHO die letzte Wahl. Ich würde da eher eine Quelltextbasis schreiben die mit beiden Versionen läuft. Da gibt es auch Bibliotheken die dabei helfen, wie `future` oder `six`. Und bei HTTP-Kram das externe `requests` statt der `urllib*`-Module aus der Standardbibliothek.
Gut ich werde jetzt das exepition handling in den Griff bekommen und dann die die Quelltextbasis für Python 2 und 3 erstellen.
Zur Ausnahmebehandlung: Ja so würde das ungefähr syntaktisch aussehen und nein das macht natürlich so keinen Sinn, ausser die Aufgabe ist tatsächlich nur: Mache eine Ausgabe wenn keine Ressource für 'some url' gefunden wird.

Statt deutschem Handbuch wäre Englisch lernen wahrscheinlich die bessere Alternative. Denn es wird keine gute und aktuelle deutsche Übersetzung der gesamten Python-Dokumentation geben. Schon gar nicht wenn man Bibliotheken ausserhalb der Standardbibliothek mit hinzu zieht. Auch Domänenwissen, beispielsweise über HTTP steht direkt in der Regel erst einmal in Englisch zur Verfügung. Ob es dafür dann Übersetzungen gibt und wie gut die sind…
Meine Englischkenntnisse sind relativ gut. Es ist diese Bequemlichkeit, die sich gern bei mir einschleicht. Ein deutschsprachiges Buch läßt sich für mich leichter lesen als ein englischsprachiges Buch.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

aaron hat geschrieben:Meine Englischkenntnisse sind relativ gut. Es ist diese Bequemlichkeit, die sich gern bei mir einschleicht. Ein deutschsprachiges Buch läßt sich für mich leichter lesen als ein englischsprachiges Buch.
Bei guten Englischkenntnissen ist dies reine Übungssache. Stürz Dich daher am besten direkt in die englischsprachigen Dokumentationen. Der Markt für deutschsprachige Dokumentationen ist zu klein, um für Autoren wirklich attraktiv zu sein. Und daran wird sich vermutlich auch nichts ändern.
Antworten