Create TOC

2010년 3월 1일

기온 변화 그래프 2

기상청 홈페이지가 개편되면서 날씨 정보에 대한 xml를 제공하기 시작했습니다. 그러나 기온 변화 그래프에서 필요한 현재 시간 날씨 정보에 대해서는 xml 데이타를 제공하지 않습니다.

기온 변화 그래프를 계속 그리기 위해서 기상청 웹페이지를 해석해서 그래프로 그리는 간단한 python 스크립트를 작성해봤습니다.

이 스크립트는 기상청 페이지를 가져와서 xml로 변환하는 buildwexml.py와 그래프를 그리는 gengraph.py 두 부분으로 나뉘어 있습니다.

이 스크립트는 Debian Linux, python 2.5 환경에서 제작, 테스트 되었습니다.

코드는 아래와 같습니다

gengraph.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""gengraph.py

buildwexml.py를 이용해 생성한 xml 파일을 가지고 그래프를 그리는 스크립트
"""
__author__ = 'Yun-yong Choi'
__version__ = '0.1'

import time
import xml.etree.ElementTree

import Gnuplot, Gnuplot.funcutils

import buildwexml

# 데이타를 저장할 경로
write_root = '/tmp/'
# 그래프 파일 경로
graph_filename = '/home/markboy/temperature_graph.png'
writeinfo = {
      u'서울':[u'seoul.dat', u'Seoul'],
      u'부산':[u'busan.dat', u'Busan'],
      u'제주':[u'jeju.dat', u'Jeju'],
}
findcities = [ u'서울', u'부산', u'제주' ]

# web에서 xml 정보를 가져온다.
buildwexml.build(write_root + 'current.xml')

#xml을 해석한다.
xmlobj = xml.etree.ElementTree.parse(write_root + 'current.xml')
timestr = time.strftime('%Y/%m/%d %H', 
                time.strptime(xmlobj.getiterator('datetime')[0].attrib['data'], '%Y.%m.%d.%H:%S') )
# city node를 순회한다.
for city in  xmlobj.getiterator('city'):
    # 찾는 도시가 있으면
    if city.attrib['name'] in findcities :
        # 기온값을 저장한다.
        f = file(write_root + writeinfo[city.attrib['name']][0], 'at')
        f.write("%s %s\n" % (timestr, city.find('temperature').find('now').attrib['data']))
        f.close()
# 일주일분만 표시하기 위해서 7일전 날짜를 구한다.
ago = time.time() - 60 * 60 * 24 * 7
ago_str = time.strftime("%Y/%m/%d", time.localtime(ago) )

# plot 파일 저장
f = file(write_root + 'temperature_graph.plot', 'wt')
f.write("""
set term png small
set size 1.0, 0.6
set output '%s'
set grid
set key left bottom
set ylabel \"Temperature(C)\"

set xdata time
set timefmt \"%%Y/%%m/%%d\"
set xrange [\"%s\":]
set timefmt \"%%Y/%%m/%%d %%H\"
set format x \"%%m/%%d\"

plot """ % (graph_filename, ago_str) )

for key in writeinfo.keys() :
    f.write("'%s' using 1:3 title \"%s\" with line" % (write_root + writeinfo[key][0], writeinfo[key][1]))
    if key <> writeinfo.keys()[-1] :
        f.write(', ')
f.close()
# 그래프 그리기
g = Gnuplot.Gnuplot()
g.load(write_root + 'temperature_graph.plot')

buildwexml.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" buildwexml.py

기상청의 현재 날씨 정보 페이지를 가져와서 xml 파일을 만드는 스크립트
"""
__author__ = 'Yun-yong Choi'
__version__ = '0.2'

import os
import sys
import codecs
import urllib2

def getWebpage(url, referer='') :
    """url 파일을 읽어온다"""
    debug = 0
    if debug :
        return file(url.split('/')[-1], 'rt').read()
    else :
        opener = urllib2.build_opener()
        opener.addheaders = [
            ('User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)'),
            ('Referer', referer),
        ]
        return opener.open(url).read()

def getMainPage():
    return getWebpage('http://www.kma.go.kr/weather/observation/currentweather.jsp')

def getDataPage():
    return getWebpage('http://www.kma.go.kr/weather/observation/currentweather_data.jsp', 'http://www.kma.go.kr/weather/observation/currentweather.jsp' )

def normalize(s):
    """<td> tag와  를 제거한 문자열을 돌려준다.
    
        s - 입력 문자열
    """
    return s.replace('<td>', '').replace('</td>', '').replace(' ', '')

def printUsing():
    """사용 방법을 출력한다"""
    print sys.argv[0], '<output file name>'
    
def getDateTime(buffers):
    """ html 내용을 해석해서 데아타가 생성된 날짜를 얻는다. 날짜 형식은 yyyy.mm.dd.HH:SS 이다.
    
        buffers - html 파일 내용
    """
    return buffers.split('<p class="table_topinfo">')[1].split('</p>')[0].split('/>')[-1]

    
def getDatablocks(buffers):
    """html 내용을 해석해서 도시별로 묶은 list를 돌려준다
    
        buffers - html 파일 내용
    """
    # <table class="table_develop 앞부분을 잘라낸다.
    a = buffers.split('<table class="table_develop"')[1]
    # 맨 처음 만나는 </table>을 기준으로 뒤를 잘라낸다.
    b = a.split('</table>')[0]
    # </thread>를 기준으로 앞을 잘라낸다.
    c = b.split('</thead>')[1].replace('<tr>', '')
    # 빈 줄 제거
    r = ''
    for line in c.split('\n') :
        line = line.decode('cp949').encode('utf-8')
        line = line.strip()
        if len(line) > 0 :
            r = r + line + '\n'
    # </tr> 기준으로 잘라내면 데이타 block이 완성된다.
    return r.split('</tr>\n')[:-1] # 마지막 block은 버린다.

def writeXMLheader(out):
    """XML header를 기록한다."""
    out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    out.write('<xml>\n')
    out.write('  <current_weather>\n')
    
def writeXMLfooter(out):
    """XML footer를 기록한다."""
    out.write('  </current_weather>')
    out.write('</xml>')
    
def writeXMLnode(out, datablock):
    data = datablock.split('\n') # 0번째 <tr>부분은 버린다.
    # 0 - 이름
    city = data[0].split('</a>')[0].split('>')[2]
    out.write("""    <city name="%s">
      <!-- 날씨 -->
      <weather>
        <!-- 현재일기 -->
        <now data="%s" />
        <!-- 시정(km) -->
        <visibility data="%s" />
        <!-- 운량 (1/10) -->
        <clouds data="%s" />
        <!-- 중하운량 -->
        <ml_clouds data="%s" />
      </weather>
      <!-- 기온 -->
      <temperature>
        <!-- 현재 기온 -->
        <now data="%s" />
        <!-- 이슬점온도 -->
        <dew_point_c data="%s" />
        <!-- 체감 온도 -->
        <wind_chill_c data="%s" />
      </temperature>
      <!-- 강수 -->
      <precipitation>
        <!-- 일강수(mm) -->
        <now data="%s" />
        <!-- 적설 (cm) -->
        <snow_cover data="%s" />
        <!-- 습도 (%%) -->
        <humidity data="%s" />
      </precipitation>
      <!-- 바람 -->
      <wind>
        <!-- 풍향 -->
        <direction data="%s" />
        <!-- 풍속(m/sec) -->
        <speed data="%s" />
      </wind>
      <!-- 기압 (hPa) -->
      <atmospheric_pressure>
        <!-- 해면 기압 -->
        <see_level data="%s" />
      </atmospheric_pressure>
    </city>
""" % (city,
    normalize(data[1]),
    normalize(data[2]),
    normalize(data[3]),
    normalize(data[4]),
    normalize(data[5]),
    normalize(data[6]),
    normalize(data[7]),
    normalize(data[8]),
    normalize(data[9]),
    normalize(data[10]),
    normalize(data[11]),
    normalize(data[12]),
    normalize(data[13]) ) )

def build(outputname) :
    """ 기상청 사이트에서 현재 날씨 정보를 읽어와서 xml로 저장한다.

        outputname - 저장할 파일 이름
    """
    out = file(outputname, 'wt')
    writeXMLheader(out)
    out.write('    <datetime data="%s" />\n' % getDateTime(getMainPage()))

    for datablock in getDatablocks(getDataPage()) :
        writeXMLnode(out, datablock)
    writeXMLfooter(out)

if __name__ == '__main__' :
    if len(sys.argv) <> 2 :
        printUsing()
        sys.exit(1)
    build(sys.argv[1])