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.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du erzeugst einen Log-File-Namen, und gibst den zurueck. Das hast du schon (generate_logfile).

Du speicherst dir den Namen in einer Variablen, und den gibst du weiter an all die anderen Dinge, die ihn brauchen:

Code: Alles auswählen

def generate_logfile():
    ...
    
def ensure_logdir_exists(logfilename):
     ....
     
def initialize_logger():
      logfile_name = generate_logfile()
      ensure_logdir_exists(logfile_name)
      ...
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: jetzt hast Du wieder den funktionierenden Zustand gezeigt. Und vom funktionierenden zum kaputten Zustand hast Du mehr geändert, als eigentlich nötig wäre. Die Frage ist, warum hast Du das gemacht? Irgendetwas hast Du Dir ja dabei gedacht. Um Dir helfen zu können, wäre es gut, was Du Dir dabei gedacht hast, weil wie es generell richtig ist, hat __deets__ ja schon geschrieben.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Der Code sieht jetzt so aus. Es funktioniert die Funktion def ensure_logdir_exits(logfilename): nocht nicht. Mir ist nicht klar, wie die Argumentübergabe an die Funktion def ensure_logdir_exits(logfilename): erfolgt.

Code: Alles auswählen

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

def ensure_logdir_exits(logfilename):
    if not os.path.exists(os.path.dirname(logfilename)):
        os.makedirs(os.path.dirname(logfilename))

def initialize_logger():
    logfile_name = generate_logfile()
    print(logfile_name)
    ensure_logdir_exists(logfile_name)
    logger = logging.getLogger('Data download')
    logger.setLevel(logging.DEBUG)
    handler = logging.FileHandler(logfile_name)
    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')
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: wenn Du eine Fehlermeldung erhältst, sagt sie Dir, was Du falsch machst.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Sorry die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 115, in <module>
    main()
  File "TickDataCsv.py", line 108, in main
    initialize_logger()
  File "TickDataCsv.py", line 22, in initialize_logger
    ensure_logdir_exists(logfile_name)
NameError: global name 'ensure_logdir_exists' is not defined
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Vergleich mal:

Code: Alles auswählen

def ensure_logdir_exits(logfilename):
    ...             ^^^

def initialize_logger():
    ...
    ensure_logdir_exists(logfile_name)
    ...              ^^^

Code: Alles auswählen

Traceback (most recent call last):
...
NameError: global name 'ensure_logdir_exists' is not defined
                                         ^^^
In specifications, Murphy's Law supersedes Ohm's.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

DANKE
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: die Fehlermeldung sollte Dir eigentlich gleich sagen, dass Du Dich irgendwo vertippt hast, ohne dass Du hier extra fragen mußt. Programmieren heißt nicht, zu hoffen, dass irgendwer für jemanden die Arbeit macht.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich versuche das exception handling zu implementieren. Es kann zu 3 Fehlertypen
kommen.
1. Die Datei ist auf dem Server nicht vorhanden, obwohl diese eigentlich da
sein sollte oder das Datum liegt in der Zukunft.
HTTP Error 404: Not Found

2. Die Datei ist Beschädigt.
CRC check failed 0x64e41a56 != 0x966a8aabL

3. Die Datei ist auf dem Server vorhanden und wird heruntergeladen. Beim Öffnen
der Datei wird festgestellt, das diese bis auf die Tabellenüberschrift leer
ist. Die Größe der Datei beträgt nur 52 Bytes.

Meine Fragen sind:
Angenommen, eine Datei ist auf dem Server nicht vorhanden. Wie geht es weiter,
damit die nächste Datei heruntergeladen und das Programm nicht beendet wird?

Als letzten Schritt möchte ich diese Informationen in das logfile schreiben.

Hier ein Versuch ein Teil des exception handling einzubauen.

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 as e:
        print(e)
        exit(1)

def print_data_length(data):
    try:
        f = gzip.GzipFile(fileobj=StringIO(data))
        length = 0
        while True:
            block = f.read(1024*1024)
            if not block:
                break
            length += len(block)
            print(length)
    except IOError as e:
        print(e)
        exit(1)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: Sinn der Fehlerbehandlung ist es ja das Programm wieder in einen Zustand zu versetzen, dass es weiterlaufen kann. Ein ›exit‹ macht dagegen das Gegenteil. So kann man die Fehlerbehandlung gleich weglassen. An welcher Stelle im Programm macht es denn einen Unterschied, ob etwas erfolgreich war, oder nicht?
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Zu Deiner Frage.
An welcher Stelle im Programm macht es denn einen Unterschied, ob etwas erfolgreich war, oder nicht?
Ich denke es beginnt schon mit dem Erstellen des Dateinamens.

Code: Alles auswählen

def generate_filename(symbol, year, week):                                          
    return os.path.join("data", symbol, str(year), '{}.csv.gz'.format(week))
Ist die Datei auf dem Server nicht vorhanden oder defekt, dann kann der Dateiname für die nächste Datei erstellt werden.
Das Traceback zeigt aber einen Fehler in der Funktion pulled file.

Code: Alles auswählen

Traceback (most recent call last):
  File "TickDataCsv.py", line 115, in <module>
    main()
  File "TickDataCsv.py", line 110, in main
    fetch_whole_year(symbol, 2017)
  File "TickDataCsv.py", line 83, in fetch_whole_year
    data = pull_file(symbol, year, week)
  File "TickDataCsv.py", line 46, in pull_file
    response = urllib2.urlopen(url)
  File "/usr/lib64/python2.7/urllib2.py", line 154, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib64/python2.7/urllib2.py", line 437, in open
    response = meth(req, response)
  File "/usr/lib64/python2.7/urllib2.py", line 550, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib64/python2.7/urllib2.py", line 475, in error
    return self._call_chain(*args)
  File "/usr/lib64/python2.7/urllib2.py", line 409, in _call_chain
    result = func(*args)
  File "/usr/lib64/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

Code: Alles auswählen

def pull_file(symbol, year, week):    
    url = URL_TEMPLATE.format(symbol, year, week)    
    response = urllib2.urlopen(url)    
    return response.read()    
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: der entscheidende Satz, den Du geschrieben hast, ist folgender:
aaron hat geschrieben:Ist die Datei auf dem Server nicht vorhanden oder defekt, dann kann der Dateiname für die nächste Datei erstellt werden.
Das Traceback zeigt aber einen Fehler in der Funktion pulled file.
Kannst Du in generate_filename mit der nächsten Datei weitermachen? Nein, weil da ja gar kein Fehler auftreten kann. Kannst Du in pull_file mit der nächsten Datei weitermachen? Nein! Weil die Funktion ja nur für eine Datei gedacht ist.

Was ist dann die Lösung? Dort, wo Du in der Schleife alle Dateien durchgehst, hast Du die Möglichkeit, bei einem Fehler einfach mit der nächsten Datei weiterzumachen. Dort wäre also der richtige Platz für eine Fehlerbehandlung.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Hier die veränderte Funktion fetch_whole_year. Richtigerweise wird CRC check failed 0x64e41a56 != 0x966a8aabL bei der beschädigten Datei ausgegeben. Dannach stopt das Programm. Wie laße ich die funktion weiter laufen?

Code: Alles auswählen

def fetch_whole_year(symbol, year):                                      
      try:                                                                 
          for symbol in symbol:                                            
              last_week = datetime.date(year, 12, 31).isocalendar()[1]     
              for week in range(1, last_week + 1):             
                  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,»
      except IOError as e:                            
          print(e)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: warum brichst Du auch die ganze Schleife ab, wenn bei einem Datum ein Fehler auftritt?
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

So sieht die Funktion jetzt aus. Das scheint jetzt soweit zu funktionieren. Für die Wochen ab Kalenderwoche 13 2017 gibt die Funktion jetzt 39 mal HTTP Error 404: Not Found aus, was richtig ist. Die Schleife hört erst nach der Kalenderwoche 52 auf zu laufen. Wiie gehe ich jetzt vor um zu prüfen ob die Kalenderwoche in der Zukunft liegt? Noch eine Schleife die die aktuelle Kalenderwoche abfragt?

Code: Alles auswählen

def fetch_whole_year(symbol, year):     
      for symbol in symbol:     
          last_week = datetime.date(year, 12, 31).isocalendar()[1]     
          for week in range(1, last_week + 1):     
              try:     
                  if not exists_file(symbol, year, week):     
                      data = pull_file(symbol, year, week)     
                      print_data_length(data)                  
                      save_file(symbol, year, week, data)     
              except IOError as e:                            
                  print(e)             
              else:            
                  print("File for {}/{}/{} already fetched.".format(     
                      symbol, year, week)                                
                  )            
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich wollte das Print aus der Funktion in ein Logfile umleiten. Leider scheint diese Idee falsch zu sein. Ich wollte innerhalb der Funktion das Logging mit logger = initialize_logger() aufrufen. Bitte erklärt mir doch was ich falsch gemacht habe.

Code: Alles auswählen

def fetch_whole_year(symbol, year):     
      logger = initialize_logger()     
      for symbol in symbol:     
          last_week = datetime.date(year, 12, 31).isocalendar()[1]     
          for week in range(1, last_week + 1):     
              try:     
                  if not exists_file(symbol, year, week):     
                      data = pull_file(symbol, year, week)     
                      print_data_length(data)     
                      save_file(symbol, year, week, data)     
              except IOError as e:     
                  print(e)     
                  logger.error(e)     
              else:     
                  print("File for {}/{}/{} already fetched.".format(     
                      symbol, year, week)     
                  )     
                  logger.info("File for {}/{}/{} already fetched.".format(     
                      symbol, year, week)     
                  )     
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich habe drei Funktionen, die noch nicht wie gewünscht funktionieren. Bei der Funktion fetch_whole_year ist das Logging nicht richtig implementiert.
In meinen Post davor habe ich meine Idee gezeigt. Hier noch einmal die Funktion ohne das Logging.
Frage: Wie wird das Logging richtig einhebaut? In dieser Funktion soll dann auch die nächste Funktion initialize_logger aufgerufen werden. Wie rufe ich die zweite Funktion initialize_logger in der Funktion fetch_whole_year auf? Sollte die Funktion initilise_logger auch durch diese Funktion aufgerufen werden? Das Absenden der Email sollte dann abschliessend durch die Funktion initialise_logger erfolgen. Nachdem das Logfile geschrieben wurde soll dieses per Email verschickt werden. Ich habe eine Funktion send_email geschrieben. Hier sind noch zwei Fehler zu beheben.
1. Hier rufe ich die Funktion generate_logfile auf. Wie ich aber gelernt habe sollte die Fuktion generate_logfile nicht mehrfach aufgerufen werden. Welche Idee ist richtig?
2. Fehler beim Versenden der Email. Ich bekomme folgende Fehlermeldung:

Code: Alles auswählen


def fetch_whole_year(symbol, year):
    for symbol in symbol:
        last_week = datetime.date(year, 12, 31).isocalendar()[1]
        for week in range(1, last_week + 1):
            try:
                if not exists_file(symbol, year, week):
                    data = pull_file(symbol, year, week)
                    print_data_length(data)
                    save_file(symbol, year, week, data)
            except IOError as e:
                print(e)
            else:
                print("File for {}/{}/{} already fetched.".format(
                    symbol, year, week)
                )


def initialize_logger():
    logfile_name = generate_logfile()
    print(logfile_name)
    ensure_logdir_exists(logfile_name)
    logger = logging.getLogger('Data download')
    logger.setLevel(logging.DEBUG)
    handler = logging.FileHandler(logfile_name)
    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')

Code: Alles auswählen

def send_email():
    email_user = 'user'
    email_pwd = 'passwd'

    SENDER = 'absender@example.com'
    RECIPIENT = 'empfenger@example.com'

    logfile = generate_logfile()
    fp = open(logfile, 'rb')
    msg = MIMEText(fp.read())
    fp.close()

    msg['From'] = SENDER
    msg['To'] = RECIPIENT
    msg['Subject'] = 'The  logfile from %s' % logfile

    try:
        server = smtplib.SMTP('wpxxxxxx.mailout.server-he.de', '25')
        server.set_debuglevel(True)
        server.ehlo()
        server.starttls()
        server.login(email_user, email_pwd)
        server.sendmail(SENDER, [RECIPIENT], msg.as_string())
        server.close()
        print 'Email send successfully.'
    except:
        print "Failed to send the email."

Code: Alles auswählen

reply: '354 Enter message, ending with "." on a line by itself\r\n'
reply: retcode (354); Msg: Enter message, ending with "." on a line by itself
data: (354, 'Enter message, ending with "." on a line by itself')
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: Du verwendest Logger falsch. Wenn Du logging verwendest, dann brauchst Du nicht auch noch alles per print auszugeben, weil das ja durch eine passende Logger-Konfiguration passieren kann. Innerhalb einer Funktion sollte gar kein spezieller Logger initialisiert werden. Das passiert am Anfang des Hauptprogramms, wo man die Konfiguration auch per Konfigurationsdatei erledigen kann, so dass man bei geänderten Anforderungen ans Logging das Programm gar nicht anfassen muß. Sodann hat normalerweise jedes Modul einen modulweiten logger. Die Log-Meldungen sind falsch. Für Exceptions gibt es logger.exception, das auch den Stacktrace mitloggt. Für erfolgreiche Downloads wird immer "already fetched" ausgegeben, was ja nicht stimmt, wenn die Datei eben noch nicht existiert hat.

Zur Email: Du merkst gar nicht, was falsch läuft, weil Du jede Exception mit einer nichtssagenden Ausgabe unterdrückst. So funktioniert Exception-Handling nicht, wie wir schon mehrfach betont haben. Die Ausgabe kommt daher, dass Du debuglevel auf True gesetzt hast, sind also kein Zeichen für einen Fehler.
aaron
User
Beiträge: 92
Registriert: Donnerstag 1. Dezember 2016, 23:10

Ich bekomme bei der Ansicht der Emailquelldaten folgenden Fehler gemeldet.
X-ACL-Warn: Message does not conform to RFC2822 standard und die Email landet immer im Spammordner
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@aaron: dann solltest Du schauen ob msg.as_string() RFC2822-konform ist (wovon ich mal ausgehe) oder ob Dein Mail-Gateway inkompatibel ist.
Antworten