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.
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Also, ich hab die bisherige Diskussion ja nicht verfolgt, aber ich würde genau diese Fehlermeldung erwarten:

Code: Alles auswählen

def print_data_length(symbol, year, week, data):
    ...
...
                print_data_length(data)                        <- hier taucht der nächste Fehler auf
Fehlermeldung:[codebox=bash file=Unbenannt.bsh]...
TypeError: print_data_length() takes exactly 4 arguments (1 given)[/code]
Zum Vergleich:[codebox=bash file=Unbenannt.bsh]>>> def foo(x, y):
... return x + y
...
>>> foo(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 2 arguments (1 given)[/code]
In specifications, Murphy's Law supersedes Ohm's.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Es scheint so, daß ich eine Lösung gefunden habe. Ich habe auf dem Server unseres Hosters, welcher nur 2GB RAM zur Verfügung stellt getestet. 2 Symbole ohne Fehler heruntergeladen. Das 3 Symbol wurde nur bis 36.csv.gzip heruntergeladen und mit einem Fehler abgebrochen. Die Datenmenge für die 140 Wochen beträgt richtigerweise 1,190 GB. Ich habe nur diese Zeile verändert. data = f.read(1024). Kann ich bitte eine Erklärung bekommen, warum es jetzt anscheinend funktioniert.

Code: Alles auswählen

def print_data_length(data):
    f = gzip.GzipFile(fileobj=StringIO(data))
    data = f.read(1024)
    print(len(data))
Die Dateien für das Symbol welches die Fehlermeldung auswirft, sind die Dateien auf dem Server entweder nicht vorhanden, oder defekt.
Hier das Traceback der neuen Fehlermeldung.

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 80, in <module>
    main()
  File "TickDataCsv.py", line 75, in main
    fetch_whole_year(symbol, 2016)
  File "TickDataCsv.py", line 51, in fetch_whole_year
    data = pull_file(symbol, year, week)
  File "TickDataCsv.py", line 19, in pull_file
    response = urllib2.urlopen(url)
  File "/usr/lib/python2.7/urllib2.py", line 154, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 437, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 550, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 475, in error
    return self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 409, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 558, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 404: Not Found
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: NEIN, Du hast keine Lösung gefunden, weil die Funktion ›print_data_length‹ jetzt nicht mehr das tut, was sie ursprünglich gemacht hat. Wenn Du meine Anmerkungen nicht verstehst, dann schreib das doch, dann kann ich ausführlicher Antworten. Ich schreibe extra knapp, damit niemand denkt "jetzt komm mal auf den Punkt". Nachfragen ist immer erlaubt.

Du schreibst selbst, dass die gepackten Daten 8MB, die ungepackten 100MB groß sind. Für einzelne Dateien eigentlich kein Problem mit dem Speicher. Jetzt kann es mehre Szenarien geben. Eine Datei ist gepackt nicht 8MB sondern 80MB. Und schon wirds beim Entpacken knapp mit dem Arbeitsspeicher von 2GB. Oder die Datei hat einen Fehler und aus 100MB sinnvollen Daten werden 1000MB Schrott. Das erkennt gzip an der Checksumme, aber erst, wenn die ganze Datei entpackt wurde, diese passt aber nicht in den Arbeitsspeicher, daher die Fehlermeldung.

Lösung: Blockweise Lesen:

Code: Alles auswählen

def print_data_length(data):
    f = gzip.GzipFile(fileobj=StringIO(data))
    length = 0
    while True:
        block = f.read(1024*1024)
        if not block:
            break
        length += len(block)
    print(length)
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich bin gerade dabei mich mit den möglichen Fehlern beim Herunterladen der
Dateien zu Beschäftigen. Wie fasse ich das Problem richtig an?

Ich habe das Script noch einmal durchlaufen lassen. Dabei sind folgende Fehler
aufgetreten.

1. Die Datei wird heruntergeladen und es wird keine Fehlermeldung ausgegeben.
Die Datei hat nur die Überschrift der Tabelle zum Inhalt. Die Dateien haben eine Größe von 52 Bytes.

2. Abbruch bei Symbol AUDJPY Kalenderwoche 36 bit folgender Fehlermeldung ab:
Hier scheint die Datei auf dem Server zu liegen, aber defekt zu sein.

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 89, in <module>
    main()
  File "TickDataCsv.py", line 86, in main
    fetch_single_week("AUDJPY", 2016, 36)
  File "TickDataCsv.py", line 46, in fetch_single_week
    print_data_length(data)
  File "TickDataCsv.py", line 29, in print_data_length
    block = f.read(1024 * 1024)
  File "/usr/lib/python2.7/gzip.py", line 268, in read
    self._read(readsize)
  File "/usr/lib/python2.7/gzip.py", line 315, in _read
    self._read_eof()
  File "/usr/lib/python2.7/gzip.py", line 354, in _read_eof
    hex(self.crc)))
IOError: CRC check failed 0x64e41a56 != 0x966a8aabL
3. Abbruch bei Symbol AUDJPY Kalenderwoche 37, 38, 39. Die Dateien scheinen nicht auf dem Server zu liegen. Ab Kalenderwoche 40 sind
die Dateien wieder da. Die Fehlermeldung taucht erneut auf, wenn die
Kalenderwoche in der Zukunft liegt.

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 89, in <module>
    main()
  File "TickDataCsv.py", line 86, in main
    fetch_single_week("AUDJPY", 2016, 37)
  File "TickDataCsv.py", line 45, in fetch_single_week
    data = pull_file(symbol, year, week)
  File "TickDataCsv.py", line 19, in pull_file
    response = urllib2.urlopen(url)
  File "/usr/lib/python2.7/urllib2.py", line 154, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 437, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 550, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 475, in error
    return self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 409, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 558, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 404: Not Found
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich bin gerade dabei das Logging zum Laufen zu bringen. Ich habe 2 Funktionen geschrieben. Leider sind diese Fehlerhaft. Vielleicht ist die Idee falsch?
Hier erst einmal meine Fragen:
1. Ist die Idee richtig, das Logging in eine Funktion zu schreiben?
2. Ich möchte, daß einmal täglich ein Logfile erstellt wird, deshalb die Funktion generate_logfile.

Code: Alles auswählen

def generate_logfile():
    return os.path.join("log", str(datetime.date.today())+'.log')

def initialize_logger():
logger = logging.getLogger('Data warehouse')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(generate_logfile)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
Hier die Fehlermeldung dazu: Ich verstehe, was mir die Fehlermeldung sagt, aber wie und wo baue ich den indented block ein?

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 108, in <module>
    main()
  File "TickDataCsv.py", line 101, in main
    initialize_logger(log)
NameError: global name 'initialize_logger' is not defined
wp10750459@vwp7837:~/www/dwh$ vi TickDataCsv.py
wp10750459@vwp7837:~/www/dwh$ python TickDataCsv.py
  File "TickDataCsv.py", line 16
    logger = logging.getLogger('Data warehouse')
         ^
IndentationError: expected an indented block
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Meine Lösung sieht nun so aus. Es scheint zu funktionieren. Bitte schaut einmal drüber. Wie bringe ich das Logging und das exception handling zusammen? Wie baue ich die Meldung logger.error('error message') in die entsprechenden Funktionen ein? Ist die Idee richtig?

Code: Alles auswählen

def initialize_logger():
    logger = logging.getLogger('Data warehouse')
    logger.setLevel(logging.DEBUG)
    newpath = 'log'                                  <=== ist die Zeile eine gute Idee
    if not os.path.exists(newpath):
        os.makedirs(newpath)
        handler = logging.FileHandler(os.path.join("log", str(datetime.date.today())+'.log'))
        handler.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        
         # 'application' code
        logger.debug('debug message')
        logger.info('info message')
        logger.warn('warn message')
        logger.error('error message')
        logger.critical('critical message')
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

ich möchte

Code: Alles auswählen

handler = logging.FileHandler('log/2017-4-4.log') durch 
handler = logging.Filehandler("log", str(datetime.date.today()), '{}.log') ersetzen
Was mache ich falsch?
BlackJack

@aaron: 1. Schreibst Du `FileHandler` mit einem kleinen 'h'.

2.: Übergibst Du drei Argumente die so von `FileHandler` nicht erwartet werden:

Code: Alles auswählen

In [6]: logging.FileHandler("log", str(datetime.date.today()), '{}.log')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-6-e702480fc016> in <module>()
----> 1 logging.FileHandler("log", str(datetime.date.today()), '{}.log')

/usr/lib/python2.7/logging/__init__.pyc in __init__(self, filename, mode, encoding, delay)                                                                      
    895             self.stream = None
    896         else:
--> 897             StreamHandler.__init__(self, self._open())
    898 
    899     def close(self):

/usr/lib/python2.7/logging/__init__.pyc in _open(self)
    916             stream = open(self.baseFilename, self.mode)
    917         else:
--> 918             stream = codecs.open(self.baseFilename, self.mode, self.encoding)
    919         return stream
    920 

/usr/lib/python2.7/codecs.pyc in open(filename, mode, encoding, errors, buffering)
    879             # Force opening of the file in binary mode
    880             mode = mode + 'b'
--> 881     file = __builtin__.open(filename, mode, buffering)
    882     if encoding is None:
    883         return file

ValueError: mode string must begin with one of 'r', 'w', 'a' or 'U', not '2017-04-04b'
Das sieht extrem nach herum raten aus in dem Du wahllos Teile aus Code zusammenkopierst der etwas ähnliches macht(e) in der Hoffnung das da schon irgendwie etwas sinnvolles heraus kommt. So funktioniert programmieren aber nicht. Hier fehlen absolute Grundlagen über Zeichenketten und Funktionsaufrufe. Was hat Dich denn dazu gebracht diese drei Argumente zu übergeben? Welche Argumente erwartet `FileHandler` denn? Und welche übergibst Du und was denkst Du bedeutet das dann?
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Vielen Dank für die Antwort.
1. Ich möchte erstens überprüfen, ob ein Verzeichnis log schon existiert und wenn nein, dann soll ein Verzeichnis erstellt werden.
2. Ich möchte das jede Datei das Datum des aktuellen Tages trägt.

Aus der Python Dokumentation ist folgendes zu lesen.
class logging.FileHandler(filename, mode='a', encoding=None, delay=False)
Das heißt, daß ich als erstes Argument den Filenamen übergeben muß. Wenn ich das richtig verstehe, dann muß ich vorher den Filenamen als Strin zusammen bauen und dann an FileHandler übergeben.
Benutzeravatar
Kebap
User
Beiträge: 776
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

aaron hat geschrieben:Das heißt, daß ich als erstes Argument den Filenamen übergeben muß. Wenn ich das richtig verstehe, dann muß ich vorher den Filenamen als Strin zusammen bauen und dann an FileHandler übergeben.
Das ist korrekt, es wird genau ein String mit dem Dateinamen erwartet und nicht drei. Vielleicht hast du mal bei print gesehen, dass man einfach mehrere Strings mit Kommas getrennt angeben kann und dann werden sie quasi zu einem zusammengefügt. Das funktioniert aber nicht überall.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Die Funktionen scheinen jetzt zu funktionieren. Bitte schaut einmal drüber. es geht bestimmt besser.

Code: Alles auswählen

def generate_logfile():
    return os.path.join("log", str(datetime.date.today())+'.log')

def create_logdir():
    logdir = generate_logfile()
    if not os.path.exists(os.path.dirname(logdir)):
        os.makedirs(os.path.dirname(logdir))

def initialize_logger():
    logfile = generate_logfile()
    print(logfile)
    logdir = create_logdir()
    logger = logging.getLogger('Data warehouse')
    logger.setLevel(logging.DEBUG)
    handler = logging.FileHandler(logfile)
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(m»
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    # 'application' code
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: den Dateinamen mehrfach zu erzeugen, ist ein Fehler, da da ja nicht unbedingt den selben Namen ergeben muß.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Bitte erkläre mir genau was ich falsch gemacht habe. Ich kann dir jetzt nicht folgen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Denk mal darueber nach was passiert, wenn du generate_logfile() um 23:59:59:9999 aufrufst, und dann zwei Millisekunden spaeter schon wieder.

Und mit einem einfachen, alten Trick kannst du das Problem beheben!
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Danke für die Antwort.
den Dateinamen mehrfach zu erzeugen, ist ein Fehler, da da ja nicht unbedingt den selben Namen ergeben muß.
Denk mal darüber nach was passiert, wenn du generate_logfile() um 23:59:59:9999 aufrufst, und dann zwei Millisekunden später schon wieder.
Es werden zwei Logfiles mit unterschiedlichem Inhalt und verschiedenen Namen bedingt durch das neue Datum sein. Hier kann ich noch keinen Fehler erkennen. Geht es vielleicht ein wenig genauer? Vielleicht ein Code schnipsel oder einen Hinweis in der Dokumentation?
BlackJack

@aaron: Es geht ja nicht um zwei Logdateien sondern um *eine* Logdatei für die Du diese Funktion mehr als einmal aufrufst, und bei *einer* Datei möchte man doch eher immer den gleichen Namen haben, also auch nur einmal die Funktion zum erstellen des Namens aufrufen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

@aaron der Fehler liegt darin, dass du zwei unterschiedliche Programmteile hast, die momentan nur zufaellig funktionieren, weil du implizite Annahmen darueber machst, dass sie *NICHT* von dem veraenderlichen Teil abhaengen. Natuerlich ist das *jetzt* kein Fehler. Aber in einer nicht allzu fernen Zukunft, in welcher du feststellst, dass dein Filesystem mit tausenden Dateien pro Verzeichnis ins stocken geraet, moechtest du vielleicht ein Schema "basisverzeichnis/datums-verzeichnis/datum-und-zeitstempel.log" verwenden. Du passt also deine generate_logfile-Funktion an, und alles scheint zu funktionieren. Bis es das eben *nicht* mehr tut, weil du einmal das Verzeichnis mit Datum "gestern" anlegst, aber dann in eine Datei mit Datum "heute" schreiben moechtest. Und BUMM.

Stattdessen generiere *einmal* den Logfile-Namen, und reiche ihn in create_logdir als Argument rein. Dann kannst du sicher sein, dass die Aenderungen an generate_logfile keine solchen negativen Effekte haben koennen.

Und in einer Dokumentation steht das nicht. Das hat was mit dem nachdenken darueber zu tun, welche Konsequenzen der eigene Code entfaltet, und diese Szenarien dann zu behandeln bzw. zu veraendern, so dass sie gar nicht erst auftreten. Das ist schlicht "Programmieren".
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich habe jetzt die Funktion generate_logfile geändert. Ich verstehe immer noch nicht, wie ich das Ergebnis aus generate_logfile an create_logdir übergeben kann. Ich habe folgendes versucht.

Code: Alles auswählen

def generate_logfile():
    logfile = os.path.join("log", str(datetime.date.today())+'.log')
    print(logfile)

def create_logdir(logfile):
    if not os.path.exists(os.path.dirname(logfile)):
        os.makedirs(os.path.dirname(logfile))
Als Fehlermeldung bekomme ich

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 118, in <module>
    main()
  File "TickDataCsv.py", line 110, in main
    create_logdir(logfile)
NameError: global name 'logfile' is not defined
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich bin ehrlich gesagt ein bisschen sprachlos. Und das passiert nicht so oft.

Code: Alles auswählen

def eine_funktion():
      return "was"
      
def eine_andere_funktion(ein_argument):
      print(ein_argument)

eine_variable = eine_funktion()
eine_andere_funktion(eine_variable)
Du wirst schon programmieren lernen muessen. Die Chancen sich ein funktionsfaehiges Programm zu erraten liegen schlechter als bei 6 aus 49.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Jetzt bitte von noch einmal von vorn.
Mit der ersten Funktion generiere ich das Logfile. In der zweiten Funktion rufe ich mit der Zeile logdir = generate_logfile() die Funktion nochmals auf. Das soll nicht sein, sondern das Ergebnis aus der ersten Funktion soll als Argument übergeben werden.Ich möchte die Ergebnisse beider Funktion in einer 3. Funktion weiter verwenden. Ich würde das gern verstehen. Aktuell ist es so, da? ich die beiden Funktionen in der 3. Funktion nochmals aufrufe. Ich lasse durch die 3. Funktion die funktionen a und 2 ausführen. Jetzt bitte einmal für ein Erstklässler.

Code: Alles auswählen

def generate_logfile():
    return os.path.join("log", str(datetime.date.today())+'.log')

def create_logdir():
    logdir = generate_logfile()
    if not os.path.exists(os.path.dirname(logdir)):
        os.makedirs(os.path.dirname(logdir))
        
def initialize_logger():
    logfile = generate_logfile()
    print(logfile)
    logdir = create_logdir()
    logger = logging.getLogger('Data warehouse')
    logger.setLevel(logging.DEBUG)
    handler = logging.FileHandler(logfile)
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(m»
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    # 'application' code
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
Zuletzt geändert von aaron am Mittwoch 5. April 2017, 16:01, insgesamt 1-mal geändert.
Antworten