tags:

views:

351

answers:

4

I am using google site to retrieve weather information , I want to find values between XML tags. Following code give me weather condition of a city , but I am unable to obtain other parameters such as temperature and if possible explain working of split function implied in the code:

import urllib

def getWeather(city):

    #create google weather api url
    url = "http://www.google.com/ig/api?weather=" + urllib.quote(city)

    try:
        # open google weather api url
        f = urllib.urlopen(url)
    except:
        # if there was an error opening the url, return
        return "Error opening url"

    # read contents to a string
    s = f.read()

    # extract weather condition data from xml string
    weather = s.split("<current_conditions><condition data=\"")[-1].split("\"")[0]

    # if there was an error getting the condition, the city is invalid


    if weather == "<?xml version=":
        return "Invalid city"

    #return the weather condition
    return weather

def main():
    while True:
        city = raw_input("Give me a city: ")
        weather = getWeather(city)
        print(weather)

if __name__ == "__main__":
    main()

Thank You

+7  A: 

USE

A

PARSER

You can't parse XML using regex(es), so don't try. Here's a start to finding an XML parser in Python. Here's a good site for learning about parsing XML in Python.

UPDATE: Given the new info about PyS60, here's the documentation for using XML from Nokia's website.

UPDATE 2: @Nas Banov has requested sample code, so here it is:

import urllib

from xml.parsers import expat

def start_element_handler(name, attrs):
    """
    My handler for the event that fires when the parser sees an
    opening tag in the XML.
    """
    # If we care about more than just the temp data, we can extend this
    # logic with ``elif``. If the XML gets really hairy, we can create a
    # ``dict`` of handler functions and index it by tag name, e.g.,
    # { 'humidity': humidity_handler }
    if 'temp_c' == name:
        print "The current temperature is %(data)s degrees Celsius." % attrs

def process_weather_conditions():
    """
    Main logic of the POC; set up the parser and handle resource
    cleanup.
    """
    my_parser = expat.ParserCreate()
    my_parser.StartElementHandler = start_element_handler

    # I don't know if the S60 supports try/finally, but that's not
    # the point of the POC.
    try:
        f = urllib.urlopen("http://www.google.com/ig/api?weather=30096")
        my_parser.ParseFile(f)
    finally:
        f.close()

if __name__ == '__main__':
    process_weather_conditions()
Hank Gay
Thanks for the link , but I really wanted to know how above split function orks and why same cant e used for finding out temp_c tag values, I am tyro in python upon that my module usage is limited
Harshit Sharma
Regexes are provably insufficient for general purpose XML parsing, and here's one reason (out of many possible ones): XML can have arbitrarily nested tags. For a single, specific document (not scheme, an actual XML document), you can sometimes get useful values using regexes. This hack will then fail (usually in production), when used on a similar document (which an XML parser would have handled just fine) because the formatting is different, or the new document has some new data in a new tag, etc.
Hank Gay
really?! can't use regex to parse *any* (as compared to *all*) kind of xml? even considering that the site for PyS60 you link to points to "a set of regular expressions that can be used - very easily and effectively - to parse XML content." ... even if we consider that DTDs are based on regular expressions and that nowadays regex covers much more than "regular languages" class?
Nas Banov
@EnTerr I quite clearly understand that you can hack together a regex to extract data from a specific XML document, since my comment says "Regexes are provably insufficient for general purpose XML parsing…." Just as clearly, you understand that attempting to use regexes to process XML over which you have no control is fragile at best. What I don't understand is why you feel the need to argue with a strawman of your own creation or why you would encourage Harshit to continue with an approach you know is fragile.
Hank Gay
@Hank Gay: In your *main* response you say "You can't parse XML using regex(es), so don't try". That is incorrect and that is what i am saying. You need to clarify that and not just in some comment to yourself. You can't expect me or others to read your footnotes when you could write it straight in the first place. Also, you are being dogmatic by saying one should always use sledgehammer no matter the size of the nail. Don't you see the OP has trouble with split() yet you want to break his back with DOM?
Nas Banov
@EnTerr I think we're going to have to agree to disagree. In my mind, you learn the rule first, and the exceptions only after you understand when they are appropriate. The rule is that you parse XML using an XML parser. If the OP doesn't know how `split` works, then the OP doesn't know enough to maintain a bunch of code that tries to use regexes and `split` to extract data from XML. Learning the DOM now will save loads of pain later.
Hank Gay
@Hank Gay: well you won't make a good teacher then, for you always have to start from simple and then develop toward complex, that's how we learn. Take for example numbers - first we are thought the natural numbers and that you cannot subtract bigger from smaller. Then we say oh hey, actually you can - and then we introduce negative numbers, then fractions and real numbers - but heaven forbid, we can't divide by 0. Wait, then we once again extend the concepts in calculus... But can't you trust your kid to go pick up milk from the store if one has not finished class in microeconomics yet?
Nas Banov
@EnTerr I reject all of your analogies on the grounds that using (not writing) a DOM-based parser is *EASIER* than developing a "robust-enough" set of regexes and `split`-calls. Using regexes instead of a parser is like adding [epicycles](http://en.wikipedia.org/wiki/Deferent_and_epicycle) instead of accepting Copernican mechanics.
Hank Gay
@Hank Gay: well thanks for refreshing me on Ptolemeaic vs Copernic planetary motion models - but do you know that Copernic's heliocentric system actually added more epicycles? ;-) It was not necessarily better - until Kepler added elliptic orbits. It's a great demonstration of how our knowledge develops and wides - and yet we still say that "the Sun rises in the east and sets in the west" - do you fight that every time you hear it said?
Nas Banov
@Hank Gay: Back to our thing - why dont you show that EASY demonstration of how to solve the problem of OP with DOM parser (or SAX, whatever). I mean i wrote him working solution with splits() only... let's see the righteous way? and it can work on Nokia, too.
Nas Banov
@Nas Banov Thanks for pointing out the role of Kepler; I knew I was overlooking something when I made that analogy. I've included a simple POC using `expat` in my answer, now. I don't have the Nokia handy to test, so I'll have to take Nokia at their word when they say `expat` works.
Hank Gay
@Hank Gay: Thanks - that's not bad, though such approach won't work if for example i need what the forecast for <condition> is for <day_of_week> Friday. Isn't there some more intuitive way of accessing element of parsed xml, in the manner of `xml['xml_api_reply']['weather']['current_conditions']['temp_c'].data` - i figure while that might be just a thin veneer over one of the libraries, it will make simple tasks simpler?
Nas Banov
Again, I don't have an S60 to test, but supposedly it supports the `ElementTree` API. Assuming that's true, then you could parse the xml into an `ElementTree`; that would look something like this: `tree = ElementTree.fromstring(my_string_from_request)` and then use `findall` to get an iterable of the things that match a path. That would look something like this: `matches = tree.findall('/xml_api_reploy/weather/current_conditions/temp_c')`. You could then do this: `for match in matches: print(match.get('data'))`. Don't have the time to test that right now, but it should be close.
Hank Gay
+2  A: 

XML is structured data. You can do much better than using string manipulation to fetch data out of it. There are the sax, dom and elementree modules in the standard library as well as the high quality lxml library which can do your work for you in a much more reliable fashion.

Noufal Ibrahim
Atually I am bounded to module utilisation as I am programming in PyS60 Module
Harshit Sharma
sax, dom and elementree are part of the standard distribution. In any case, string based parsing of XML will break and your code won't really be able to survive in the wild.
Noufal Ibrahim
+4  A: 

I would suggest using an XML Parser, just like Hank Gay suggested. My personal suggestion would be lxml, as I'm currently using it on a project and it extends the very usable ElementTree interface already present in the standard lib (xml.etree).

Lxml includes added support for xpath, xslt, and various other features lacking in the standard ElementTree module.

Regardless of which you choose, an XML parser is by far the best option, as you'll be able to deal with the XML document as a Python object. This means your code would be something like:

# existing code up to...
s = f.read()
import lxml.etree as ET
tree = ET.parse(s)
current = tree.find("current_condition/condition")
condition_data = current.get("data")
weather = condition_data
return weather
nearlymonolith
Appreciate your reply, but I am programming in PyS60 and need to perform task with limited module usage
Harshit Sharma
Well, you can easily do this same functionality using the xml.etree module in the standard library. You won't have to install anything. A little bit of googling shows that this module seems to be included in the Py60 subset: http://pys60.garage.maemo.org/doc/lib/module-xml.etree.ElementTree.html
nearlymonolith
Ok I tried importing cElementTree , I guess this will help, will confirm after implementation. Thanks again
Harshit Sharma
Traceback (most recent call last): File "weatxml.py", line 36, in <module> main() File "weatxml.py", line 32, in main weather = getWeather(city) File "weatxml.py", line 18, in getWeather tree=ET.parse(s) File "/usr/lib/python2.6/xml/etree/ElementTree.py", line 862, in parse tree.parse(source, parser) File "/usr/lib/python2.6/xml/etree/ElementTree.py", line 579, in parse source = open(source, "rb")IOError: [Errno 2] No such file or directory: '<?xml vers
Harshit Sharma
I'm sorry - I didn't mean that my code would work exactly as you desired. Your error is because s is a string and parse takes a file or file-like object. So, "tree = ET.parse(f)" may work better. I would suggest reading up on the ElementTree api so you understand what the functions I've used above do in practice. Hope that helps, and let me know if it works.
nearlymonolith
A: 

Well, here goes - a non-full parser solution for your particular case:

import urllib

def getWeather(city):
    ''' given city name or postal code,
        return dictionary with current weather conditions
    '''
    url = 'http://www.google.com/ig/api?weather='
    try:
        f = urllib.urlopen(url + urllib.quote(city))
    except:
        return "Error opening url"
    s = f.read().replace('\r','').replace('\n','')
    if '<problem' in s:
        return "Problem retreaving weather (invalid city?)"

    weather = s.split('</current_conditions>')[0]  \
               .split('<current_conditions>')[-1]  \
               .strip('</>')                       
    wdict = dict(i.split(' data="') for i in weather.split('"/><'))
    return wdict

and example of use:

>>> weather = getWeather('94043')
>>> weather
{'temp_f': '67', 'temp_c': '19', 'humidity': 'Humidity: 61%', 'wind_condition': 'Wind: N at 21 mph', 'condition': 'Sunny', 'icon': '/ig/images/weather/sunny.gif'}
>>> weather['humidity']
'Humidity: 61%'
>>> print '%(condition)s\nTemperature %(temp_c)s C (%(temp_f)s F)\n%(humidity)s\n%(wind_condition)s' % weather
Sunny
Temperature 19 C (67 F)
Humidity: 61%
Wind: N at 21 mph

PS. Note that a fairly trivial change in Google output format will break this - say if they were to add extra spaces or tabs between tags or attributes. Which they avoid to decrease size of http response. But if they did, we'd have to get acquainted with regular expressions and re.split()

PPS. how str.split(sep) works is explained in the documentation, here is a excerpt: Return a list of the words in the string, using sep as the delimiter string. ... The sep argument may consist of multiple characters (for example, '1<>2<>3'.split('<>') returns ['1', '2', '3']). So 'text1<tag>text2</tag>text3'.split('</tag>') gives us ['text1<tag>text2', 'text3'], then [0] picks up the 1st element 'text1<tag>text2', then we split at and pick up 'text2' which contains the data we are interested in. Quite trite really.

Nas Banov
Can you explain or a link how this .split("..........")[0]\... I mean the logic behind this will be helpful..Thank You
Harshit Sharma
@Harshit Sharma: ok, added explanation
Nas Banov