livedoorの天気予報情報の取得サンプル

bonlifeです。id:faw:20080206:1202308711 の記事を見て、「ちょ、何でlivedoorの天気予報の情報を取得するのにWeather HacksXML使わないの!」って思ったら細かい地域の天気予報には対応してないんですね…orz
ということで、私もWeather Hacksを使わずに情報を取得するPythonスクリプトを書いてみました。明らかにオリジナルより機能縮小の劣化版です。大事な大事なメール送信機能を省いちゃってますし、なんとなく大阪市北区のURLをスクリプト中に固定で埋め込み。lxmlの練習ということで、勘弁してください。XpathのクエリにUnicode文字列を渡す方法で悩んだりしたのは秘密です。(というより、Xpathの書き方で、何箇所もつまづいたのは秘密です。)

出力イメージはこんな感じ。

     02/07
 0 3 6 912151821
曇晴晴晴晴曇雨曇
 5 4 4 5 7 8 5 3
 0 0 0 0 0 0 0 0
     02/08
 0 3 6 912151821
曇曇曇晴曇晴晴晴
 3 3 2 4 7 7 5 3
 0 0 0 0 0 0 0 0

スクリプトは以下の通り。

# -*- coding: utf8 -*-

import urllib2
import re
from lxml import etree

def get_date(e_strong):
    date_string = [ i for i in e_strong.getparent().itertext() ][1]
    m = re.search(r'(\d{1,2}).*?(\d{1,2})', date_string)
    if m:
        return "%02d/%02d" % (int(m.groups(0)[0]),int(m.groups(0)[1]))
    else:
        return ''

def get_weather_info(e_table):
    weather_list = list()
    ts = e_table.xpath("tr/td/small/div[@style='margin-top:5px;']")
    for i in ts:
        weather_list.append(i.text[0])
    return weather_list

def get_temperature_info(e_table):
    temperature_list = list()
    target = u'気温'
    xpath_string = "tr/th[@class='thline'][small/text()='%s']" % target
    e_th = e_table.xpath(xpath_string)
    e_tr = e_th[0].getparent()
    for e in e_tr.xpath('td/small'):
        temperature_list.append(e.text.rstrip(u'℃'))
    return temperature_list

def get_rain_info(e_table):
    rain_list = list()
    target = u'降水量'
    xpath_string = "tr/th[@class='thline'][small/text()='%s']" % target
    e_th = e_table.xpath(xpath_string)
    e_tr = e_th[0].getparent()
    for e in e_tr.xpath('td/small'):
        rain_list.append(e.text.rstrip(u'mm'))
    return rain_list

def get_all_weather_info(target_url):
    result_dict = dict()
    et = etree.parse(urllib2.urlopen(target_url),etree.HTMLParser())
    ts = et.xpath('//table/tr/td/small/strong')
    for i in ts:
        e_table = i.getparent().getparent().getparent().getparent()
        if i.text.strip() == u'今日の天気':
            result_dict['today'] = {'date':get_date(i)}
            result_dict['today'].update({'weather':get_weather_info(e_table)})
            result_dict['today'].update({'temperature':get_temperature_info(e_table)})
            result_dict['today'].update({'rain':get_rain_info(e_table)})
        elif i.text.strip() == u'明日の天気':
            result_dict['tomorrow'] = {'date':get_date(i)}
            result_dict['tomorrow'].update({'weather':get_weather_info(e_table)})
            result_dict['tomorrow'].update({'temperature':get_temperature_info(e_table)})
            result_dict['tomorrow'].update({'rain':get_rain_info(e_table)})
    return result_dict

def print_weather_info(result_dict):
    width = 16
    hour_header = ''.join([ "%2s" % i for i in range(0,24,3) ])
    def print_one_day_info(d):
        print d.get('date').center(width)
        print hour_header
        print ''.join(d.get('weather'))
        print ''.join([ "%2s" % i for i in d.get('temperature') ])
        print ''.join([ "%2s" % i for i in d.get('rain') ])
    print_one_day_info(result_dict.get('today'))
    print_one_day_info(result_dict.get('tomorrow'))

if __name__ == "__main__":
    result_dict = get_all_weather_info('http://weather.livedoor.com/point/city/1655.html')
    print_weather_info(result_dict)

itertext()が思い出せずに苦労したのは数度目…orz 私もそのうち改訂して、携帯にメール送れるようにしてみようっと。
後、あらためて見ると、関数名に _info とか付いてますが、情報を取ってくるに決まってるので、 _info は不要ですね…。get_date() とか _info なしの関数もあるし。あぁ、適当に命名するとこうなるから怖い!