#!/usr/bin/env python
# INITIAL FIGURING OUT OF XML PARSING FROM Thomas Upton's (http://www.thomasupton.com/) weather.py SCRIPT
# HAVE LEARNED A LOT FROM HIS PYTHON SCRIPT
import sys, urllib, os, re, ast
from optparse import OptionParser
from xml.dom.minidom import parse
# SET UP WEATHER NAME SERVICE FOR THE XML
weatherNS = 'http://xml.weather.yahoo.com/ns/rss/1.0'
WEATHER_URL = 'http://xml.weather.yahoo.com/forecastrss?p=%s'
METRIC_PARAMETER = '&u=c'
URLTYPE = "RSS"
# FILE FOR WHERE THE WOEID TO ZIPCODE MAP GOES
# CURRENTLY SET TO BE A HIDDEN FOLDER IN THE USERS HOME DIRECTORY
# YOU CAN CUSTOMIZE THIS IF YOU WANT THE FILE SOMEPLACE ELSE
FCASTWOEIDDATA = os.getenv('HOME') + os.path.sep + ".zip_woeid_map.txt"
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# DEBUG FLAG
DEBUG = False
UNITTEST = False
UNITS = "F"
YQLBASEURL = "http://query.yahooapis.com/v1/public/yql"
IPLOCURL = "http://ipinfodb.com/my_ip_location.php"
# CONSTANTS
COMMA = "%2C"
SPACE = "%20"
EQUAL = "%3D"
def myencode(instr):
rstr = ""
for i in xrange(len(instr)):
if instr[i] == " ":
rstr += SPACE
elif instr[i] == "=":
rstr += EQUAL
else:
rstr += instr[i]
return rstr
def getLocationData():
"""
getWOEID
- FUNCTION TO GET THE WOEID ID FROM THE LOCALLY STORED DATABASE
OR
PULLS THE DATA FROM THE INTERNET AND STORES INTO THE DATABASE
- INPUT: NONE
- OUPUTS: locdict - DICTIONARY OF LOCATION DATA
"""
# SET UP OUR DICTIONARY
locdict = { 'zipcode' : "",
'country' : "",
'stprov' : "",
'city' : "",
'lat' : "",
'lon' : ""}
# GET THE ZIP CODE BASED UPON OUR EXTERNAL IP ADDRESS
if not UNITTEST:
url = urllib.urlopen(IPLOCURL)
ippagedata = url.read()
url.close()
if UNITTEST and DEBUG:
# DEBUG
print "USING LOCAL FILE"
loco = open("./myloc.txt")
ippagedata = loco.read()
loco.close()
# REGULAR EXPRESSION TO FIND THE NEEDED DATA IN THE WEBPAGE
# WOULD BE BETTER TO USE SGML TO PARSE THE HTML BUT I DON'T
# WANT TO FIGURE IT OUT
# GET THE COUNTRY
mat = re.search('<li>Country : .*<img',ippagedata)
locdict['country'] = mat.group(0).split(': ')[1].split('<img')[0].rstrip().lower()
# GET THE STATE/PROVINCE
mat = re.search('<li>State/Province : .*</li>',ippagedata)
locdict['stprov'] = mat.group(0).split(': ')[1].split('</li>')[0].lower()
# GET THE CITY
mat = re.search('<li>City : .*</li>',ippagedata)
locdict['city'] = mat.group(0).split(': ')[1].split('</li>')[0].lower()
# GET THE ZIP CODE
mat = re.search('<li>Zip or postal code : .*</li>',ippagedata)
locdict['zipcode'] = mat.group(0).split(': ')[1].split('</li>')[0]
# GET THE LATITUDE
mat = re.search('<li>Latitude : .*</li>',ippagedata)
locdict['lat'] = mat.group(0).split(': ')[1].split('</li>')[0]
# GET THE LONGITUDE
mat = re.search('<li>Longitude : .*</li>',ippagedata)
locdict['lon'] = mat.group(0).split(': ')[1].split('</li>')[0]
# RETURN THE LOCATION DICTIONARY
return locdict
def getWOEID(zipcode):
"""
getWOEID
- FUNCTION TO GET THE WOEID ID FROM THE LOCALLY STORED DATABASE
OR
PULLS THE DATA FROM THE INTERNET AND STORES INTO THE DATABASE
- INPUT: zipcode - ZIPCODE FROM LOCATION DATA OR USER INPUT
- OUPUTS: woeid - THE WOEID WE WANT BASED ON OUR ZIPCODE
"""
# CHECK TO SEE IF THE ZIP IS IN THE FILE
haveWOEID = False
infile = False
if os.path.isfile(FCASTWOEIDDATA):
fin = open(FCASTWOEIDDATA,'r+a')
infile = True
else:
# WE HIT THE CASE WHERE THE FILE DOESN'T EXIST
fin = open(FCASTWOEIDDATA,'w')
infile = False
# IF WE HAVE THE INPUT FILE, LOOK FOR OUR ZIPCODE
if infile:
for line in fin.readlines():
if zipcode in line:
haveWOEID = True
# GET THE WOEID AND RIGHT STRIP TO REMOVE THE RETURN LINE FROM THE FILE
woeid = line.split(",")[1].rstrip()
# NOT RETURNING HERE SO WE CAN GRACEFULLY CLOSE THE INPUT FILE
# THIS IS A STUPID LITTLE ISSUE
else:
haveWOEID = False
# IF WE DON'T HAVE THE WOEID, LET'S GET THE SHIZZLE AND STORE LOCALLY
if not haveWOEID:
# GET THE URL TO FIND WOEID
yqlquery = "select woeid from geo.places where text=" + zipcode + " limit 1"
yqlformat = "json"
WOEIDURL = YQLBASEURL + "?q=" + myencode(yqlquery) + "&format=" + yqlformat
# GET THE DATA FROM THE INTERNETS
reqresp = urllib.urlopen(WOEIDURL)
# NEED TO BREAK THE STRING INTO THE APPROPRIATE DICTIONARY STRUCTURE
resp = ast.literal_eval(reqresp.read())
reqresp.close()
# SEARCH THE RESPONSE FOR THE WOEID
# JSON IS AWESOME! YAY FOR NESTED DICTIONARIES
woeid = resp['query']['results']['place']['woeid']
# WRITE THE DATA TO THE FILE
fmt = "%s,%s\n" % (zipcode, woeid)
fin.write(fmt)
# NOW WE CAN CLOSE THE FILE
fin.close()
# RETURN THE WOEID WE FOUND
return woeid
def makeUrl(utype,locdict,woeid,units):
"""
makeUrl
- FUNCTION TO CREATE THE URL FOR EITHER THE YAHOO WEATHER RSS
OR THE 5-DAY FORECAST
- INPUT: utype - TYPE OF URL: RSS OR 5DAY
locdict - ZIPCODE FROM LOCATION DATA OR USER INPUT
woeid - THE WOEID WE WANT BASED ON OUR ZIPCODE
units - UNITS OF MEASURE
- OUPUTS: url - THE URL FOR THE PAGE WE WANT TO PARSE
"""
if utype == "RSS":
url = "http://weather.yahooapis.com/forecastrss?w="+woeid
if units == "C":
url += "&u=c"
elif utype == "5DAY":
# FORMAT THE COUNTRY PROPERLY
tmp = locdict['country'].split(" ")
country = ""
for i in xrange(len(tmp)):
country += tmp[i]
if i < len(tmp)-1:
country += "-"
# FORMAT THE STATE/PROVINCE CORRECTLY
tmp = locdict['stprov'].split(" ")
stprov = ""
for i in xrange(len(tmp)):
stprov += tmp[i]
if i < len(tmp)-1:
stprov += "-"
# FORMAT THE CITY PROPERLY
tmp = locdict['city'].split(" ")
city = ""
for i in xrange(len(tmp)):
city += tmp[i]
if i < len(tmp)-1:
city += "-"
# GENERATE URL
url = "http://weather.yahoo.com/" + country + "/" + stprov + "/" + city + "-" + woeid + "/"
if DEBUG:
print country
print stprov
print city
if units == "C":
url += "&unit=c"
# RETURN THE URL
return url
if __name__ == "__main__":
# SET UP THE COMMAND LINE OPTION HOLDER
cmdparse = OptionParser()
# ADD COMMAND LINE OPTIONS
cmdparse.add_option('-t', '--temp', action="store_true", default=False)
cmdparse.add_option('-d', '--deg', action="store_true", default=False)
cmdparse.add_option('-c', '--cond', action="store_true", default=False)
cmdparse.add_option('-o', '--code', action="store_true", default=False)
cmdparse.add_option('-b', '--both', action="store_true", default=False)
cmdparse.add_option('-m', '--metric', action="store_true", default=False)
# PARSE THE COMMAND LINE OPITONS
opts, args = cmdparse.parse_args(sys.argv[1:])
# GET OUR LOCATION DICTIONARY
locdict = getLocationData()
if (opts.metric):
UNITS = "C"
# GET OUR WOEID
mywoeid = getWOEID(locdict['zipcode'])
# GET THE APPROPRIATE URL WE REQUIRE
url = makeUrl(URLTYPE,locdict,mywoeid,UNITS)
# PARSE THE XML FILE
dom = parse(urllib.urlopen(url))
# GRAB THE UNITS OF MEASURE AND CURRENT CONDITION
currUNITS = dom.getElementsByTagNameNS(weatherNS, 'units')[0]
currCOND = dom.getElementsByTagNameNS(weatherNS, 'condition')[0]
# PARSE OUT currUNITS AND currCond
unit = currUNITS.getAttribute('temperature')
currWX = currCOND.getAttribute('text')
currTEMP = currCOND.getAttribute('temp')
currCODE = currCOND.getAttribute('code')
# PRINT OUT DATA BASED UPON CLI ARGUMENTS
if (opts.temp):
if (opts.deg):
print currTEMP + unichr(176) + unit
else:
print currTEMP + unit
if (opts.cond):
print currWX
if (opts.code):
print currCODE
if (opts.both):
print currWX + ", " + currTEMP + unit