Seite 1 von 1

Get Weather!

Verfasst: Freitag 11. September 2009, 20:17
von Dav1d
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

Verfasst: Freitag 11. September 2009, 20:43
von cofi
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.

Verfasst: Samstag 12. September 2009, 18:18
von Dav1d
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

Verfasst: Samstag 12. September 2009, 19:01
von DasIch
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.

Verfasst: Samstag 12. September 2009, 22:32
von Hyperion
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.

Verfasst: Sonntag 13. September 2009, 09:50
von Dav1d
@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)

Verfasst: Sonntag 13. September 2009, 12:59
von Hyperion
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 ;-)

Verfasst: Sonntag 13. September 2009, 13:20
von Dav1d
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

Verfasst: Sonntag 13. September 2009, 16:50
von 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.

Verfasst: Freitag 18. September 2009, 02:06
von snafu
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. ^^

Verfasst: Freitag 18. September 2009, 06:13
von Dav1d
find ich gut, allerdings auch ziemlich umständlich man hätte die Liste die rauskommt zerlegen können

Verfasst: Freitag 18. September 2009, 07:40
von /me
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?

Verfasst: Freitag 18. September 2009, 09:18
von snafu
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. ;)

Verfasst: Freitag 18. September 2009, 12:57
von jbs
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?

Verfasst: Freitag 18. September 2009, 16:44
von snafu
@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.

Verfasst: Freitag 18. September 2009, 18:47
von Dav1d
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'

Verfasst: Mittwoch 23. Dezember 2009, 19:20
von Dav1d
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']