From c873d0eabb30c9c0415d15dfb28b59b62a59f753 Mon Sep 17 00:00:00 2001 From: James Vega Date: Sun, 19 Sep 2004 21:14:43 +0000 Subject: [PATCH] Update Weather.wunder to use BeautifulSoup. Remove the #! line and add a __contributors__ line. --- plugins/Weather.py | 165 +++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 90 deletions(-) diff --git a/plugins/Weather.py b/plugins/Weather.py index 716f706a4..3a5cdd306 100644 --- a/plugins/Weather.py +++ b/plugins/Weather.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ### # Copyright (c) 2002-2004, Jeremiah Fincher # All rights reserved. @@ -34,14 +32,23 @@ This plugin does weather-related stuff. It can't change the weather, though, so don't get your hopes up. We just report it. """ -__revision__ = "$Id$" +import supybot -import supybot.plugins as plugins +__revision__ = "$Id$" +__contributors__ = { + supybot.authors.jamessan: ['cnn', 'wunder', + 'temperatureUnit configuration variable', + 'convert configuration variable'], + supybot.authors.jemfinch: ['weather'], + supybot.authors.bwp: ['ham'], + } import re import sets import urllib +import BeautifulSoup + import supybot.conf as conf import supybot.utils as utils import supybot.webutils as webutils @@ -84,9 +91,13 @@ class Weather(callbacks.Privmsg): the name of 'weather' which should override this help.""" weatherCommands = ['ham', 'cnn', 'wunder'] threaded = True + def __init__(self): + self.__parent = super(Weather, self) + self.__parent.__init__() + def callCommand(self, name, irc, msg, *L, **kwargs): try: - super(Weather, self).callCommand(name, irc, msg, *L, **kwargs) + self.__parent.callCommand(name, irc, msg, *L, **kwargs) except webutils.WebError, e: irc.error(str(e)) @@ -338,36 +349,9 @@ class Weather(callbacks.Privmsg): else: irc.error('Could not find weather information.') - _wunderUrl = 'http://www.weatherunderground.com/' \ - 'cgi-bin/findweather/getForecast?query=' - _wunderLoc = re.compile(r'[^:]+: ([^<]+)', re.I | re.S) - _wunderFTemp = re.compile( - r'graphics/conds.*?(-?\d+) (°)(F)', - re.I | re.S) - _wunderCond = re.compile(r'
\s+([^<]+)', - re.I | re.S) - _wunderHumid = re.compile(r'Humidity:]+>(\d+%)', - re.I | re.S) - _wunderDew = re.compile(r'Dew Point:]+>\s+' - r'(-?\d+) (°)(F)', - re.I | re.S) - _wunderHeat = re.compile( - r'HeatIndex:]+>\s+(\d+) ([^F]+)(F)<', - re.I | re.S) - _wunderWind = re.compile( - r'Wind:]+>\s+\s+(\d+) (mph)' - r'\s+/\s+(\d+) (km/h)\s+' - r'\s+from the\s+(\w{3})', re.I | re.S) - _wunderPressure = re.compile( - r'Pressure:\s+(\d+\.\d+) (in)\s+/\s+' - r'(\d+) (hPa)', re.I | re.S) - _wunderVisible = re.compile( - r'Visibility:]+>\s+([\w.]+) (miles)' - r'\s+/\s+([\w.]+) (kilometers)', - re.I | re.S) - _wunderUv = re.compile(r'UV:]+>(\d\d?)( out of \d\d?)', - re.I | re.S) - _wunderTime = re.compile(r'Updated:\s+([\w\s:,]+)', re.I | re.S) + _wunderUrl = 'http://mobile.wunderground.com/cgi-bin/' \ + 'findweather/getForecast?query=' + _wunderLoc = re.compile(r'Page (.+?) Forecast', re.I | re.S) _wunderMultiLoc = re.compile(r' @@ -376,74 +360,75 @@ class Weather(callbacks.Privmsg): """ loc = ' '.join(args) url = '%s%s' % (self._wunderUrl, urllib.quote(loc)) - text = webutils.getUrl(url) # Caught in callCommand. + text = webutils.getUrl(url) if 'Search not found' in text: irc.error(noLocationError, Raise=True) - if 'Search results for' in text: - text = text[text.index('Search results for'):] - newloc = self._wunderMultiLoc.search(text) + if 'Click on a city name' in text: + soup = BeautifulSoup.BeautifulSoup() + soup.feed(text) + newloc = soup.first('a').get('href') if newloc is None: irc.error('Multiple locations found. ' 'Please be more specific.', Raise=True) - url = 'http://www.wunderground.com%s' % newloc.group(1) - try: - text = webutils.getUrl(url) - except webutils.WebError, e: - irc.error(str(e), Raise=True) + url = 'http://mobile.wunderground.com%s' % newloc + text = webutils.getUrl(url) + soup.close() + soup = BeautifulSoup.BeautifulSoup() + soup.feed(text) + # Get the table with all the weather info + table = soup.first('table', {'border':'1'}) + trs = table.fetch('tr') + try: + time = trs.pop(0).first('b').string + except AttributeError: + time = '' + info = {} + def isText(t): + return not isinstance(t,BeautifulSoup.NavigableText) and t.contents + def getText(t): + s = getattr(t, 'string', None) + if s is None: + t = t.contents + num = t[0].string + units = t[1].string + # htmlToText strips leading whitespace, so we have to handle + # strings with   differently. + if units.startswith(' '): + units = utils.htmlToText(units) + s = ' '.join((num, units)) + else: + units = utils.htmlToText(units) + s = ' '.join((num, units[0], units[1:])) + return s + for tr in trs: + k = tr.first('td').string + v = filter(isText, tr.fetch('td')[1].contents) + value = map(getText, v) + info[k] = ' '.join(value) location = self._wunderLoc.search(text) - temp = self._wunderFTemp.search(text) + temp = info['Temperature'] convert = self.registryValue('convert', msg.args[0]) if location and temp: - location = location.group(1) - location = location.replace(' Forecast', '') - (temp, deg, unit) = temp.groups() + (temp, deg, unit) = temp.split() if convert: temp = self._getTemp(int(temp), deg, unit, msg.args[0]) else: temp = deg.join((temp, unit)) - time = self._wunderTime.search(text) - if time is not None: - time = ' (%s)' % time.group(1) + resp = ['The current temperature in %s is %s (%s).' %\ + (location.group(1), temp, time)] + conds = info['Conditions'] + resp.append('Conditions: %s.' % info['Conditions']) + humidity = info['Humidity'] + resp.append('Humidity: %s.' % info['Humidity']) + (dew, deg, unit) = info['Dew Point'].split() + if convert: + dew = self._getTemp(int(dew), deg, unit, msg.args[0]) else: - time = '' - resp = ['The current temperature in %s is %s%s.' %\ - (location, temp, time)] - heat = self._wunderHeat.search(text) - if heat is not None: - (heat, deg, unit) = map(str.strip, heat.groups()) - if convert: - heat = self._getTemp(int(heat), deg, unit, msg.args[0]) - resp.append('Heat Index: %s.' % heat) - conds = self._wunderCond.search(text) - if conds is not None: - resp.append('Conditions: %s.' % conds.group(1)) - humidity = self._wunderHumid.search(text) - if humidity is not None: - resp.append('Humidity: %s.' % humidity.group(1)) - dewpt = self._wunderDew.search(text) - if dewpt is not None: - (dew, deg, unit) = dewpt.groups() - if convert: - dew = self._getTemp(int(dew), deg, unit, msg.args[0]) - else: - dew = deg.join((dew, unit)) - resp.append('Dew Point: %s.' % dew) - wind = self._wunderWind.search(text) - if wind is not None: - resp.append('Wind: %s at %s %s (%s %s).' % (wind.group(5), - wind.group(1), - wind.group(2), - wind.group(3), - wind.group(4))) - press = self._wunderPressure.search(text) - if press is not None: - resp.append('Pressure: %s %s (%s %s).' % press.groups()) - vis = self._wunderVisible.search(text) - if vis is not None: - resp.append('Visibility: %s %s (%s %s).' % vis.groups()) - uv = self._wunderUv.search(text) - if uv is not None: - resp.append('UV: %s%s' % uv.groups()) + dew = deg.join((dew, unit)) + resp.append('Dew Point: %s.' % dew) + resp.append('Wind: %s at %s %s.' % tuple(info['Wind'].split())) + resp.append('Pressure: %s.' % info['Pressure']) + resp.append('Visibility: %s.' % info['Visibility']) resp = map(utils.htmlToText, resp) irc.reply(' '.join(resp)) else: