Get Weather!

Code-Stücke können hier veröffentlicht werden.
Antworten
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ich hab hier ein kleines Snippet, was für den ein oder anderen vllt nützlich ist, geschrieben hab ichs eigentlich nur als Zwischenschritt (wird noch ne GUI drumrum gebaut!

Über das google-wetter-api, werden aktuelle daten geladen und als Unicode zurückgegeben

Vorraussetztungen: BeautifulSoup

Code: Alles auswählen

#!/usr/bin/python

try:
    import urllib2 as ulib
except ImportError:
    import urllib.request as ulib
try:
    from BeautifulSoup import BeautifulStoneSoup
except ImportError:
    print 'Pls install BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/)'


di = ['city', 'postal_code', 'current_date_time']
dc = ['condition', 'temp_f', 'temp_c', 'humidity', 'wind_condition']
df = ['day_of_week', 'low', 'high', 'condition']

dis = []
dcs = []
dfs = []

class getweather:
    def __init__(self, ort):
        
        f = ulib.urlopen('http://www.google.de/ig/api?weather=' + ort)
        encoding = f.info()['content-type'].split('charset=')[1]
        f = f.read().decode(encoding)

        stonesoup = BeautifulStoneSoup(f)

        self.forecast_information = stonesoup.find('forecast_information')
        self.current_conditions = stonesoup.find('current_conditions')
        self.forecast_conditions = stonesoup.findAll('forecast_conditions')

    def getdi(self):
        for data in di:
            dis.append(self.forecast_information.find(data)['data'])
        return dis

    def getdc(self):
        for data in dc:
            dcs.append(self.current_conditions.find(data)['data'])
        return dcs

    def getdf(self):
        for forecast in self.forecast_conditions:
            for data in df:
                dfs.append(forecast.find(data)['data'])
        return dfs
        
if __name__ == '__main__':
    munich = getweather('80331')
    print munich.getdi()
    berlin = getweather('Berlin')
    print berlin.getdc()

Code: Alles auswählen

Erklärung für die "Abkürzungen", ergibt sich aus der Api-XML

di = datainformation
dc = datacurrent
df = dataforecast

dis = datainformationsave
dic = datacurrentsave
dfs = dataforecastsave
mit dem

Code: Alles auswählen

try:
    import urllib2 as ulib
except ImportError:
    import urllib.request as ulib
urllib2 = Python 2
aber urllib.request = Python 3 (ich weiß es nicht), falls es nicht so ist pls berichtigt mich, zum testen hab ich gerade keiner Python 3 zur Hand

mfg

PS: Würde mich über Feedback freuen
the more they change the more they stay the same
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Macht dich die "Erklaerung" nicht stutzig? Warum nimmst du nicht die Namen die keiner Erklaerung beduerfen? Um der API zu entsprechen ist in dem Fall IMHO ein sehr schwaches Argument.

Die Python2/3-Angelegenheit wuerde ich nicht ueber den ``ImportError``, sondern ueber eine Versionsabfrage (``sys.version_info``) loesen.

Was BeautifulSoup angeht, spricht IMHO nichts dagegen, wenn das Script nicht die Datei selbst in das aktuelle Verzeichnis herunterlaedt und erneut importiert, evtl nach Frage an den Benutzer.

Edit:
Und zum Code:
``f`` will geschlossen werden, auch ist die Neuzuweisung unguenstig. ``content`` waere ein besserer Name.
Warum stehen deine Listen ausserhalb der Klasse? Du brauchst keine globalen Variablen, v.a. da du sie als Instanzvariablen missbrauchst.
Deine ganzen ``get*`` lassen sich evtl besser als ``property`` schreiben auch waere ein Buffern oder ein komplettes Auslesen in ``__init__`` besser.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ich hab das mal ein bisschen umgestellt:

Code: Alles auswählen

#!/usr/bin/python

import sys
import os

if sys.version_info[0] < 3:
    import urllib2 as ulib
else:
    import urllib.request as ulib

try:
    if sys.argv[1] == '--beautifulsoup':
        if not os.path.exists(os.path.join(os.path.split(sys.argv[0])[0], 'BeautifulSoup.py')):
            dl = ulib.urlopen('http://www.crummy.com/software/BeautifulSoup/download/3.x/BeautifulSoup-3.0.6.py')
            bsoup = dl.read()
            bsoupfile = open('BeautifulSoup.py', 'w')
            bsoupfile.write(bsoup)
            dl.close()
            bsoupfile.close()
except IndexError:
    pass

try:
    from BeautifulSoup import BeautifulStoneSoup
except ImportError:
    print('Pls install BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/)')
    print('or use the parameter: --beautifulsoup, to download it and use it after downloading')
    sys.exit(1)



class getweather:
    def __init__(self, place):
        
        # List, which are needed to download the data, the d in the name of the lists present data
        self.dinfo = ['city', 'postal_code', 'current_date_time']
        self.dcurrent = ['condition', 'temp_f', 'temp_c', 'humidity', 'wind_condition']
        self.dforecast = ['day_of_week', 'low', 'high', 'condition']

        # Lists to save the datas
        self.dinfos = []
        self.dcurrents = []
        self.dforecasts = []
        
        self.place = place   
        
        self.getweatherdata()
     
    def getweatherdata(self):
        '''Downloading the whole data and extracting into the lists'''
        f = ulib.urlopen('http://www.google.de/ig/api?weather=' + self.place)
        encoding = f.info()['content-type'].split('charset=')[1]
        self.content = f.read().decode(encoding)
        f.close()
       
        stonesoup = BeautifulStoneSoup(self.content)
        forecast_information = stonesoup.find('forecast_information')
        current_conditions = stonesoup.find('current_conditions')
        forecast_conditions = stonesoup.findAll('forecast_conditions')

        for data in self.dinfo:
            self.dinfos.append(forecast_information.find(data)['data'])
        for data in self.dcurrent:
            self.dcurrents.append(current_conditions.find(data)['data'])
        for forecast in forecast_conditions:
            for data in self.dforecast:
                self.dforecasts.append(forecast.find(data)['data'])

    def returndata(self):
        '''Retruning of the lists with data'''
        return self.dinfos, self.dcurrents, self.dforecasts
            
if __name__ == '__main__':
    munich = getweather('80331')
    a, b, c = munich.returndata()
    print(a, b, c)
Das hier:

Code: Alles auswählen

try:
    if sys.argv[1] == '--beautifulsoup':
        if not os.path.exists(os.path.join(os.path.split(sys.argv[0])[0], 'BeautifulSoup.py')):
            dl = ulib.urlopen('http://www.crummy.com/software/BeautifulSoup/download/3.x/BeautifulSoup-3.0.6.py')
            bsoup = dl.read()
            bsoupfile = open('BeautifulSoup.py', 'w')
            bsoupfile.write(bsoup)
            dl.close()
            bsoupfile.close()
except IndexError:
    pass
gefällt mir gar nicht (try, except-block), allerdings weis ich auch nicht wie ichs anders machen soll, wenn wer ne Idee hat her damit :wink:

//Edit: Code jetzt auf englisch mit Kommentaren und Docstrings
the more they change the more they stay the same
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Wieso machst du daraus nicht eine Funktion die ein dict mit den Daten zurück gibt? Vom Aufbau der XML Datei ist dass problemlos machbar.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

DasIch hat geschrieben:Wieso machst du daraus nicht eine Funktion die ein dict mit den Daten zurück gibt? Vom Aufbau der XML Datei ist dass problemlos machbar.
Zumal der OP Klasse schon wie eine Funktion benannt hat - wenn man so etwas macht, ist das oftmals schon ein Indiz für den Missbrauch des Klassenkonstruktes.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

@DasIch, sowas in der Art wollte ich noch machen

@Hyperion, jo das stimmt die Namen sind nicht perfekt geweählt!


//Edit: So das ganze sieht jetzt etwas anders aus!

Code: Alles auswählen

#!/usr/bin/python

import sys
import os

if sys.version_info[0] < 3:
    import urllib2 as ulib
else:
    import urllib.request as ulib

try:
    if sys.argv[1] == '--beautifulsoup':
        if not os.path.exists(os.path.join(os.path.split(sys.argv[0])[0], 'BeautifulSoup.py')):
            dl = ulib.urlopen('http://www.crummy.com/software/BeautifulSoup/download/3.x/BeautifulSoup-3.0.6.py')
            bsoup = dl.read()
            bsoupfile = open('BeautifulSoup.py', 'w')
            bsoupfile.write(bsoup)
            dl.close()
            bsoupfile.close()
except IndexError:
    pass

try:
    from BeautifulSoup import BeautifulStoneSoup
except ImportError:
    print('Pls install BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/)')
    print('or use the parameter: --beautifulsoup, to download it and use it after downloading')
    sys.exit(1)



def gwapi(place, lang='en'):
      
    # Dictionaries to save the informations
    winfo = {'city' : '', 
             'postal_code' : '',
             'latitude_e6' : '',
             'longitude_e6' : '',
             'forecast_date' : '',
             'current_date_time' : ''}
    wcurrent = {'condition' : '',
                'temp_f' : '',
                'temp_c' : '',
                'humidity' : '',
                'wind_condition' : ''}
    wforecast = {'day_of_week' : '',
                 'low' : '',
                 'high' : '',
                 'condition': ''}
     
    # Available Languages 
    languages = { 'en' : 'com',
                  'de' : 'de'}   
        
    if lang in languages.keys():
        lang = languages[lang]
    else:
        lang = languages['en']
       
    f = ulib.urlopen('http://www.google.' + lang + '/ig/api?weather=' + place)
    encoding = f.info()['content-type'].split('charset=')[1]
    content = f.read().decode(encoding)
    f.close()
       
    stonesoup = BeautifulStoneSoup(content)
    if stonesoup.find('problem_cause'):
        raise ValueError('city not available')
    forecast_information = stonesoup.find('forecast_information')
    current_conditions = stonesoup.find('current_conditions')
    forecast_conditions = stonesoup.findAll('forecast_conditions')
        

    for data in winfo.keys():
        winfo[data] = forecast_information.find(data)['data']
    for data in wcurrent.keys():
        wcurrent[data] = current_conditions.find(data)['data']
    for forecast in forecast_conditions:
        for data in wforecast.keys():
            wforecast[data] = forecast.find(data)['data']
    return winfo, wcurrent, wforecast
           
if __name__ == '__main__':
    a, b, c = gwapi('Munich', lang='de')
    print(a, b, c)
the more they change the more they stay the same
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Statt gwapi() würde ich eher etwas wie get_weather() als Namen wählen. Das passt besser zu einer Funktion.

Die Zeilen 56-59 würde ich eher so schreiben:

Code: Alles auswählen

lang = languages.get(lang, "en")
Wobei das Überschriben von Namen imho in den seltensten Fällen sinnvoll ist. Denn die Value-Werte entsprechen doch eher Domains.

Schlussendlich sind a, b und c keine guten Namen.

Ob die Keys für die dictionaries (oder das Sprach-Mapping komplett) innerhalb der Funktion definiert werden sollten, wäre die nächste Überlegung. Ich würde wohl die Keys global definieren und dann in der Funktion nur noch die Dicts anlegen. Das Sprachen-Dict würde ich komplett auf Modulebene definieren.

Desweiteren: Doku, Doku, Doku ;-)
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Mit
Ich würde wohl die Keys global definieren und dann in der Funktion nur noch die Dicts anlegen.
versteh ich nicht ganz was du meinst
the more they change the more they stay the same
BlackJack

Ich würd's so machen:

Code: Alles auswählen

#!/usr/bin/env python
import urllib
from functools import partial
from pprint import pprint
from xml.etree import ElementTree as etree


URL_TEMPLATE = 'http://www.google.%s.//ig/api?weather=%s'
LANGUAGE_TO_DOMAIN = {'en': 'com', 'de': 'de'}

FORECAST_INFORMATION_KEYS = ['city', 'postal_code', 'latitude_e6',
                             'longitude_e6', 'forecast_date',
                             'current_date_time']
CURRENT_FORECAST_KEYS = ['condition', 'temp_f', 'temp_c', 'humidity',
                         'wind_condition']
FORECAST_KEYS = ['day_of_week', 'low', 'high', 'condition']


def node2dict(keys, node):
    return dict((k, node.find(k).get('data')) for k in keys)


def get_weather(city, language='en'):
    url = urllib.urlopen(URL_TEMPLATE % (LANGUAGE_TO_DOMAIN.get(language, 'en'),
                                         city))
    try:
        doc = etree.fromstring(url.read())
    finally:
        url.close()
    
    forecast_information = node2dict(FORECAST_INFORMATION_KEYS,
                                     doc.find('./*/forecast_information'))
    current_conditions = node2dict(CURRENT_FORECAST_KEYS,
                                   doc.find('./*/current_conditions'))
    forecast_conditions = map(partial(node2dict, FORECAST_KEYS),
                              doc.findall('./*/forecast_conditions'))
    return (forecast_information, current_conditions, forecast_conditions)


def main():
    result = get_weather('Munich', 'en')
    pprint(result)


if __name__ == '__main__':
    main()
Funktioniert allerdings nicht mit 'de' weil Google kaputtes XML liefert, und zwar welches, bei dem die Kodierungsangabe nicht stimmt. Es gibt keine, also muss man das eigentlich als UTF-8 dekodieren, aber es ist kein UTF-8. Eine Kodierungsangabe in den HTTP-Headern ist kein Ersatz für eine Angabe im XML selbst.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hab's jetzt mal ein bißchen objektorientiert gemacht:

Code: Alles auswählen

>>> from weather import get_weather
>>> ge = get_weather('de-45881', 'de')
>>> ge.info.city
'Gelsenkirchen, NRW'
>>> ge.info.zipcode
('DE', 45881)
>>> ge.info.zipcode_string
'DE-45881'
>>> ge.info.datetime
datetime.datetime(2009, 9, 18, 0, 0, 4)
>>> ge.info.datetime_string
'Fri, 18 Sep 2009 00:00:04 +0000'
>>> ge.current.condition
'Klar'
>>> ge.current.temp_c
13
>>> ge.current.temp_f
55
>>> ge.forecast
{'Mo.': (22, 12, u'Vereinzelt st\xfcrmisch'), 'So.': (23, 13, 'Vereinzelt Regen'), 'Fr.': (21, 12, 'Klar'), 'Sa.': (24, 13, 'Klar')}
>>> ge = get_weather(45881)
>>> ge.info.city
'Rawson, OH'
>>> ny = get_weather('new york')
>>> ny.info.city
'New York, NY'
>>> paris = get_weather('paris', 'fr')
>>> paris.forecast
{'ven.': (24, 13, "Nuageux dans l'ensemble"), 'dim.': (23, 12, 'Brouillard'), 'sam.': (24, 12, 'Couverture nuageuse partielle'), 'lun.': (23, 11, 'Averses')}
>>> munich = get_weather('münchen', 'fr')
>>> munich.info.city
'Munich, BY'
>>> munich.forecast
{'ven.': (21, 11, 'Pluie'), 'dim.': (22, 11, 'Risques de pluie'), 'sam.': (22, 11, 'Risques de pluie'), 'lun.': (23, 11, u'Risques de temp\xeate')}
Der Code dazu

Hm, ich geh da morgen nochmal drüber. Ich glaub, ich mach mir die Sache mit dem Zipcode viel zu kompliziert. ^^
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

find ich gut, allerdings auch ziemlich umständlich man hätte die Liste die rauskommt zerlegen können
the more they change the more they stay the same
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Dav1d hat geschrieben:find ich gut, allerdings auch ziemlich umständlich man hätte die Liste die rauskommt zerlegen können
Ich verstehe jetzt nicht, was an einer Liste umständlich sein soll.

Wie würdest du das zerlegen wollen?
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Welche Liste? Meinst du das Wörterbuch mit den Tupeln? Warum soll es umständlicher sein, eine Liste zu benutzen als etwas zu zerlegen? Aber kann ja gut sein, dass man manche Schritte noch vereinfachen kann. Habe das nach bestem Wissen und Gewissen strukturiert. ;)
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Was spricht denn gegen ``...&hl={1}`` anstatt von ``...google.{1}...``?
Ist doch schöner wenn man die Sprache explizit angibt und nicht auf die TLD setzt, oder?
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@jbs: Daran hab ich gar nicht gedacht... Du hast recht: Es ist schöner. Ich schreibe gerade an einem Kommandozeilen-Interface und werd deinen Tipp einbauen, wenn ich's veröffentliche.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

snafu hat geschrieben:Welche Liste? Meinst du das Wörterbuch mit den Tupeln? Warum soll es umständlicher sein, eine Liste zu benutzen als etwas zu zerlegen? Aber kann ja gut sein, dass man manche Schritte noch vereinfachen kann. Habe das nach bestem Wissen und Gewissen strukturiert. ;)
Ah, stimmt, die Liste war in den alten 'Versionen'
the more they change the more they stay the same
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ich hab das komplette Skript nochmal überarbeitet, diesmal wird lxml oder BeautifulSouo verwendet, welches gerade installiert ist!

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
if sys.version_info[0] < 3:
    import urllib2 as ulib
else:
    import urllib.request as ulib

try:
    from lxml import etree
    parser = 'etree'
except ImportError:
    try:
        from BeautifulSoup import BeautifulStoneSoup
        parser = 'beautifulstonesoup'
    except ImportError:
        raise ImportError('lxml or BeautifulSoup must be installed')

langs = {'german' : 'de',
         'french' : 'fr',
         'english' : 'en',
         'de' : 'de',
         'fr' : 'fr',
         'en' : 'en'}
          

def get_weather(location, lang):
    '''parses a XML-Document from the Google-Weather API'''
    lang = langs.get(lang, 'en')
    url = 'http://www.google.com/ig/api?weather={location}&hl={lang}' \
                                .format(location=location, lang=lang)
    f = ulib.urlopen(url)
    encoding = f.info()['content-type'].split('charset=')[1]
    content = f.read().decode(encoding)
    #print type(content) # "unicode"?
    f.close()

    if parser == 'etree':
        parsed = _parse_weather_lxml(content)
    else:
        parsed = _parse_weather_bss(content)
        
    return parsed

def _parse_weather_lxml(data):
    stored_data = {'forecast_information' : None,
                   'current_conditions' : None,
                   'forecast_conditions' : []}
    
    etree_xml = etree.fromstring(data)
    root = etree_xml.find('weather')
    
    for r_child in root.getchildren():
        tag = r_child.tag
        buf = {}
        if not tag == 'problem_cause': # error?
            for child in r_child:
                buf[child.tag] = child.get('data', '')
            if not tag == 'forecast_conditions':
                stored_data[tag] = buf
            else:
                stored_data[tag].append(buf)
        else: # jep, error!
            stored_data[tag] = r_child.get('data', '')
    
    return stored_data

def _parse_weather_bss(data):
    stored_data = {'forecast_information' : None,
                   'current_conditions' : None,
                   'forecast_conditions' : []}
    
    stonesoup = BeautifulStoneSoup(data)
    
    for r_child in stonesoup.find('weather'):
        tag = r_child.name
        buf = {}
        if not tag == 'problem_cause': # error?
            for child in r_child:
                buf[child.name] = child.get('data', '')
            if not tag == 'forecast_conditions':
                stored_data[tag] = buf
            else: 
                stored_data[tag].append(buf)
        else: # jep, error
            stored_data[tag] = r_child.get('data', '')

    return stored_data

if __name__ == '__main__':
    from pprint import pprint
    
    location = raw_input('Ort/Location: ')
    lang = raw_input('Sprache/Language: ')
    
    parsed_xml = get_weather(location, lang)
    
    if not 'problem_cause' in parsed_xml:
        pprint(parsed_xml)
    else:
        print 'Error:', parsed_xml['problem_cause']
the more they change the more they stay the same
Antworten