From 79e90260a88d8e7133e4c9a39f7d6fdde3539121 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 29 Mar 2017 12:47:52 -0500 Subject: [PATCH] Update Weather.com weather backend to reflect API changes (#558) * Update Weather.com weather backend to reflect API changes Their API still provides a feed which requires a rotating API key, but that API key is no longer something we can parse out of the webpage. Fortunately, the forecast data is compiled server-side anyway and is present as a blob of javascript that we can parse out of the page and load as JSON. * Clarify directions for getting location code --- i3pystatus/weather/weathercom.py | 190 +++++++++++++------------------ 1 file changed, 77 insertions(+), 113 deletions(-) diff --git a/i3pystatus/weather/weathercom.py b/i3pystatus/weather/weathercom.py index 93fa606..838a5f6 100644 --- a/i3pystatus/weather/weathercom.py +++ b/i3pystatus/weather/weathercom.py @@ -7,11 +7,7 @@ from html.parser import HTMLParser import json import re -API_PARAMS = ('api_key', 'lang', 'latitude', 'longitude') - -API_URL = 'https://api.weather.com/v2/turbo/vt1precipitation;vt1currentdatetime;vt1pollenforecast;vt1dailyForecast;vt1observation?units=%s&language=%s&geocode=%s,%s&format=json&apiKey=%s' - -FORECAST_URL = 'https://weather.com/weather/today/l/%s' +WEATHER_URL = 'https://weather.com/weather/today/l/%s' class WeathercomHTMLParser(HTMLParser): @@ -23,28 +19,13 @@ class WeathercomHTMLParser(HTMLParser): def __init__(self, logger, location_code): self.logger = logger self.location_code = location_code - for attr in API_PARAMS: - setattr(self, attr, None) - # Not required for API call, but still parsed from the forecast page - self.city_name = '' super(WeathercomHTMLParser, self).__init__() - def safe_eval(self, data): - ''' - Execute an eval with no builtins and no locals - ''' - try: - return eval(data, {'__builtins__': None}, {}) - except Exception as exc: - self.logger.log( - 5, - 'Failed to eval() data: %s\n\nOriginal data follows:\n%s', - exc, data - ) - return {} - - def read_forecast_page(self): - with urlopen(FORECAST_URL % self.location_code) as content: + def get_weather_data(self): + url = WEATHER_URL % self.location_code + self.logger.debug('Making request to %s to retrieve weather data', url) + self.weather_data = None + with urlopen(url) as content: try: content_type = dict(content.getheaders())['Content-Type'] charset = re.search(r'charset=(.*)', content_type).group(1) @@ -53,10 +34,10 @@ class WeathercomHTMLParser(HTMLParser): html = content.read().decode(charset) try: self.feed(html) - except Exception as exc: - self.logger.debug( + except Exception: + self.logger.exception( 'Exception raised while parsing forecast page', - exc + exc_info=True ) def handle_data(self, content): @@ -64,62 +45,29 @@ class WeathercomHTMLParser(HTMLParser): tag_text = self.get_starttag_text().lower() except AttributeError: tag_text = '' - if tag_text == '