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.
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: 17746
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.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Hallo ich bin immer noch nicht weiter mit dem error handling. Nach stundenlangem Googeln und ausprobieren habe ich kein brauchbares Ergebnis. Ich brauche dringend einen Tip. Welche Funktion(en) muß ich anfassen? Danke für die Hilfe
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Auch wenn ihr den Speicherfehler beim Herunterladen der Dateien nicht bestätigt habt, so würde ich gern das Shutil Modul verwenden.

Ich habe in der folgenden Funktion die letzte Zeile verändert. von output.write(data) nach shutil.copyfileobj(data, output). Leider ohne Erfolg. Habe die Dokumentation und Google schon mehrfach danach befragt. Ich kann die Fehlermeldung nicht richtig deuten. Schaut euch bitte das Traceback einmal an.

Code: Alles auswählen

def save_file(symbol, year, week, data):
    filename = generate_filename(symbol, year, week)
    print(filename)
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    with open(filename, "wb") as output:
        shutil.copyfileobj(data, output)
Hier die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv27.py", line 82, in <module>
    main()
  File "TickDataCsv27.py", line 79, in main
    fetch_single_week("EURUSD", 2017, 11)
  File "TickDataCsv27.py", line 44, in fetch_single_week
    save_file(symbol, year, week, data)
  File "TickDataCsv27.py", line 37, in save_file
    shutil.copyfileobj(data, output)
  File "/usr/lib64/python2.7/shutil.py", line 49, in copyfileobj
    buf = fsrc.read(length)
AttributeError: 'str' object has no attribute 'read'
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: copyfileobj kopiert von einem File-Objekt in ein anderes, also z.B. von dem Objekt das urlopen zurückgibt, zu Deinem Dateiobjekt.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

copy, copy2, copytree, copyfileobj, move ausprobiert. Leider ohne Erfolg. Vielleicht verwende ich shutil auch an der falschen Stelle?
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Hier habe ich einen neuen Ansatz. Leider funktioniert auch dieser nicht richtig. shutil.copy(filename, "data") ist auch falsch. shutil.copy(filename, "filenme") funktioniert auch nicht.

Code: Alles auswählen

def save_file(symbol, year, week, data):
    filename = generate_filename(symbol, year, week)
    print(filename)
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    with open(filename, "wb") as output:
        shutil.copy(filename, "data")
        output.write(data)
BlackJack

@aaron: Solange Du die Datei vor dem Speichern auf Festplatte zweimal lesen musst/willst wird Dir das eh nicht helfen, denn solange musst Du die Datei auf jeden Fall einmal komplett im Speicher halten damit die Daten ein zweites mal gelesen werden können, weil ein Dateiobjekt das die Daten aus dem Netz überträgt nicht ”seekable” ist. Das könntest Du nur umgehen wenn Du kopieren von Netz auf Platte und ermitteln der unkomprimierten Grösse in einem Schritt machst, was aber eigenen Code erfordert der ein kleines bisschen komplizierter wird als das was Du bis jetzt da machst.

Ich sehe bei den Dateigrössen auch keinen Grund warum das zu einem `MemoryError` führen sollte. Selbst bei einem 32-Bit-Python dürfte das nicht passieren, bei einem 64-Bit-Python und dem RAM schon gar nicht.

Wo Du bei der Verarbeitung Speicher sparen kannst ist beim ermitteln der unkomprimierten Grösse. Das musst Du aber selber ausprogrammieren, denn da wird ja nicht einfach nur kopiert, da musst Du die Längen der Blöcke aufsummieren.

Edit: Am einfachsten vom Code her wäre es wohl `shutil` zu verwenden um direkt von dem `Response`-Dateiobjekt in ein Dateiobjekt auf Platte kopieren zu lassen und zur Bestimmung der unkomprimierten Grösse die Datei erneut, blockweise, von Platte zu lesen. Bei dem RAM den Du hast dürfte das lesen ”von Platte” auf ein lesen aus dem Cache hinauslaufen, also auch keinen grossen Geschwindigkeitsverlust bedeuten.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Danke für die schnelle Antwort. Wie fasse ich das Problem am besten an? Könntest du mir ein wenig unter die Arme greifen? In welcher Funktion soll ich shutil verwenden? Das ist mit Sicherheit absolut logisch und richtig was du vorschlägst, nur mit der Umsetzung hapert es hier.
Edit: Am einfachsten vom Code her wäre es wohl `shutil` zu verwenden um direkt von dem `Response`-Dateiobjekt in ein Dateiobjekt auf Platte kopieren zu lassen und zur Bestimmung der unkomprimierten Grösse die Datei erneut, blockweise, von Platte zu lesen. Bei dem RAM den Du hast dürfte das lesen ”von Platte” auf ein lesen aus dem Cache hinauslaufen, also auch keinen grossen Geschwindigkeitsverlust bedeuten.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Bitte schaut Euch noch einmal meinen Versuch shutil zum Laufen zu bringen an. Es funktioniert nicht. Ich bin für jede Hilfe dankbar. Baue ich das Modul in der richtigen Funktion ein? Ich habe hier ein Verständnisproblem, trotz lesen der Dokumentation.

Code: Alles auswählen

def save_file(symbol, year, week, data):
    filename = generate_filename(symbol, year, week)
    print(filename)
    url = URL_TEMPLATE.format(symbol, year, week)
    response = requests.get(url, stream = True)
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    with open(filename, "wb") as output:
        response.raw.decode_content = True
        shutil.copyfileobj(response.raw, output)
        output.write(data)
Zuletzt geändert von Anonymous am Donnerstag 30. März 2017, 12:48, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Ich finde die Funktion überladen, warum rufst du dort Daten ab? Die Funktion save_file sollte meines Erachtens nur (!) das Speichern als Datei erledigen und den Rest als Parameter bekommen haben. Kannst Du mal die Fehlermeldung posten, das wäre m. E. sehr hilfreich.

Vollständiges Tool unter
https://github.com/joergklein/Datawareh ... DataCsv.py

Ich finde das Tool dort leichter zu lesen. Hast Du mal versucht ganz auf shutil zu verzichten? Ich nutze es nur zum Verschieben oder Kopieren von Dateien, nicht aber im Download von csv-Files...
BlackJack

@pixewakb: Ja hat er versucht, und es sind *grosse* Dateien, deswegen kann ja der Tipp von hier die nicht komplett in den Speicher herunter zu laden sondern beim herunterladen schon in eine Datei zu speichern. Das kann man nun selbst schreiben, oder man nimmt `shutil` dafür weil das Problem da ja schon mal gelöst wurde.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Für mich liest sich folgender Abschnitt im Code:

Code: Alles auswählen

with open(filename, "wb") as output:
        response.raw.decode_content = True
        shutil.copyfileobj(response.raw, output)
        output.write(data)
so, als würde er die Aufgabe einmal mit with open... und einmal mit shutil zu lösen versuchen. Ich habe beide Sachen selbst noch nie kombiniert? csv-files downloaden und abspeichern mache ich "häufig", aber die Dateien sind kleiner und es gibt eine fertige Funktion, die das für mich löst. Ich meine, dass da pandas verbaut wäre (habe ich nicht programmiert, habe ich mir noch nicht genauer angesehen).

Als ich das noch selbst gemacht habe, dann habe ich das mittels requests geladen und dann direkt als string-Daten auf die Festplatte gepackt. Das ist jetzt aber sicher mehr als ein Jahr her.
BlackJack

@pixewakb: Du musst die Ausgabedatei ja mit `open()` öffnen — wie willst Du sonst die Zieldatei erstellen? Also braucht man `open()` *und* `shutil`.

Da ist natürlich offensichtlich noch ein Quelltextfragment enthalten das keinen Sinn (mehr) macht, aber auch zu einer sehr eindeutigen Fehlermeldung führen wird, die eigentlich durch nachdenken sehr schnell dazu führen sollte, das man erkennt, das das keinen Sinn (mehr) macht. Vermutlich wird aber davor noch ein anderer Fehler auftreten. „Es funktioniert nicht.“ ist mir aber zu vage als Fehlerbeschreibung das ich da jetzt anfangen wollte zu raten.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Vielen Dank für die Rückmeldung. Wie gesagt, die ersten Dateien sind wieder ohne Probleme heruntergeladen worden. Ich habe jetzt noch einmal die Funktion umgeschrieben. Diese sieht jetzt so aus:

Code: Alles auswählen

def save_file(symbol, year, week, data):
    filename = generate_filename(symbol, year, week)
    print(filename)
    url = URL_TEMPLATE.format(symbol, year, week)
    response = requests.get(url, stream = True)
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
        response.raw.decode_content = True
        shutil.copyfileobj(response.raw, data)
        output.write(data)
Das ist die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 85, in <module>
    main()
  File "TickDataCsv.py", line 80, in main
    fetch_whole_year(symbol, 2016)
  File "TickDataCsv.py", line 57, in fetch_whole_year
    print_data_length(data)
  File "TickDataCsv.py", line 29, in print_data_length
    data = f.read()
  File "/usr/lib/python2.7/gzip.py", line 261, in read
    self._read(readsize)
  File "/usr/lib/python2.7/gzip.py", line 320, in _read
    self._add_read_data( uncompress )
  File "/usr/lib/python2.7/gzip.py", line 338, in _add_read_data
    self.extrabuf = self.extrabuf[offset:] + data
MemoryError
Zuletzt geändert von Anonymous am Samstag 1. April 2017, 16:44, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@aaron: Der Traceback passt nicht nur nicht zur gezeigten `save_file()`-Funktion, denn die kommt dort überhaupt nicht vor — die `save_file()`-Funktion führt auch zu fehlern, aber anderen. Das heisst die wird gar nicht verwendet wenn diese Ausnahmen nicht kommen.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Hier noch einmal die ursprüngliche Funktion, ohne die Änderung. Gebt mir doch bitte einmal einen Tip. Vielleicht suche ich auch in der falschen Funktion.

Code: Alles auswählen

def save_file(symbol, year, week, data):
    filename = generate_filename(symbol, year, week)
    print(filename)
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    with open(filename, "wb") as output:
        output.write(data)
Hier das Traceback

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 82, in <module>
    main()
  File "TickDataCsv.py", line 77, in main
    fetch_whole_year(symbol, 2016)
  File "TickDataCsv.py", line 54, in fetch_whole_year
    print_data_length(data)
  File "TickDataCsv.py", line 29, in print_data_length
    data = f.read()
  File "/usr/lib/python2.7/gzip.py", line 261, in read
    self._read(readsize)
  File "/usr/lib/python2.7/gzip.py", line 320, in _read
    self._add_read_data( uncompress )
  File "/usr/lib/python2.7/gzip.py", line 338, in _add_read_data
    self.extrabuf = self.extrabuf[offset:] + data
MemoryError
Antworten