ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [파이썬] 웹 크롤러 + Tkinter : 기상청 일기예보
    Python/파이썬 웹 크롤러 2022. 12. 18. 22:11
    반응형

    https://www.weather.go.kr/w/pop/rss-guide.do

     

    RSS 서비스 안내

    RSS(Really Simple Syndication, Rich Site Summary)란 블로그처럼 컨텐츠 업데이트가 자주 일어나는 웹사이트에서, 업데이트된 정보를 쉽게 구독자들에게 제공하기 위해 XML을 기초로 만들어진 데이터 형식입

    www.weather.go.kr

    아래 코드는 1.5시간에 1번씩 기상청 RSS를 크롤링해온다. 
    필요 이상의 과도한 크롤링은 서버 부담만 늘릴 뿐이다. 

    서울 강남 개포1동 RSS 주소 : https://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=1168066000 

    import tkinter as tk
    import xml.etree.ElementTree as ET
    from datetime import datetime, timedelta
    from urllib import request
    
    
    def crawl():
        r = request.urlopen("https://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=1168066000")
        xml_text = r.read().decode()
        xml_root = ET.fromstring(xml_text)
        announce_time = datetime.strptime(xml_root.find('.//tm').text, '%Y%m%d%H%M')
        result = xml_root.find('.//item/category').text + '\n'
        result += f"발표: {announce_time.strftime('%Y-%m-%d %H:%M')}" + '\n'
        for each in xml_root.findall('.//data'):
            if each.find('day').text == '2':
                continue
            forecast_date = announce_time + timedelta(days=int(each.find('day').text))
            result += f"{forecast_date.strftime('%d')}일 " \
                      f"{each.find('hour').text:>02}시, " \
                      f"{float(each.find('temp').text):.0f}℃, " \
                      f"{each.find('wfKor').text}, " \
                      f"강수확률: {each.find('pop').text}, " \
                      f"습도: {each.find('reh').text}, " \
                      f"풍속: {float(each.find('ws').text):.1f}" \
                      '\n'
        return result
    
    
    def repeat():
        weather_text = crawl()
        lbl.config(text=weather_text)
        root.after(5_400_000, repeat)  # 1.5시간 = 90분 = 5400초
    
    
    root = tk.Tk()
    root.geometry('360x240')
    root.title('오늘의 날씨')
    lbl = tk.Label(root, text='')
    lbl.pack()
    repeat()
    root.mainloop()
    root.after(5_400_000, repeat, root, lbl)
    
    # after함수는 root의 mainloop에 실행 예약을 한다. 
    # 5_400 초 뒤에, repeat 함수에 root, lbl을 넣어서 (= repeat(root, lbl)) 실행해줘.

    간단하게 작동만 확인해 보았다. 아래 페이지를 참고하여 화면을 수정해 보자. 

    https://comdoc.tistory.com/entry/tkinter-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0-%EC%A2%8B%EC%9D%80-%EB%B8%94%EB%A1%9C%EA%B7%B8

    전역변수는 일반적으로 피하는 게 좋다.  
    전역변수를 사용하지 않고 코딩한다면 이렇게...  

    import tkinter as tk
    import xml.etree.ElementTree as ET
    from datetime import datetime, timedelta
    from urllib import request
    
    
    def crawl():
        r = request.urlopen("https://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=1168066000")
        xml_text = r.read().decode()
        xml_root = ET.fromstring(xml_text)
        announce_time = datetime.strptime(xml_root.find('.//tm').text, '%Y%m%d%H%M')
        result = xml_root.find('.//item/category').text + '\n'
        result += f"발표: {announce_time.strftime('%Y-%m-%d %H:%M')}" + '\n'
        for each in xml_root.findall('.//data'):
            if each.find('day').text == '2':
                continue
            forecast_date = announce_time + timedelta(days=int(each.find('day').text))
            result += f"{forecast_date.strftime('%d')}일 " \
                      f"{each.find('hour').text:>02}시, " \
                      f"{float(each.find('temp').text):.0f}℃, " \
                      f"{each.find('wfKor').text}, " \
                      f"강수확률: {each.find('pop').text}, " \
                      f"습도: {each.find('reh').text}, " \
                      f"풍속: {float(each.find('ws').text):.1f}" \
                      '\n'
        return result
    
    
    def repeat(root, lbl):
        weather_text = crawl()
        lbl.config(text=weather_text)
        root.after(5_400_000, repeat, root, lbl)  # 1.5시간 = 90분 = 5400초
    
    
    def main():
        root = tk.Tk()
        root.geometry('360x240')
        root.title('오늘의 날씨')
        lbl = tk.Label(root, text='')
        lbl.pack()
        repeat(root, lbl)
        root.mainloop()
    
    
    if __name__ == '__main__':
        main()

     

    GUI로써의 기본적인 외형 + 가독성은 갖추어야 되겠다 싶어...

    import tkinter as tk
    import xml.etree.ElementTree as ET
    from datetime import datetime, timedelta
    from urllib import request
    
    
    def crawl():
        r = request.urlopen("https://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=1168066000")
        xml_text = r.read().decode()
        xml_root = ET.fromstring(xml_text)
        announce_time = datetime.strptime(xml_root.find('.//tm').text, '%Y%m%d%H%M')
        result = xml_root.find('.//item/category').text + '\n'
        result += f"발표: {announce_time.strftime('%Y-%m-%d %H:%M')}" + '\n'
        for each in xml_root.findall('.//data'):
            # if each.find('day').text == '2':
            #     continue
            forecast_date = announce_time + timedelta(days=int(each.find('day').text))
            result += f"{forecast_date.strftime('%d')}일 " \
                      f"{each.find('hour').text:>02}시, " \
                      f"{float(each.find('temp').text):.0f}℃, " \
                      f"{each.find('wfKor').text}, " \
                      f"강수확률: {each.find('pop').text}, " \
                      f"습도: {each.find('reh').text}, " \
                      f"풍속: {float(each.find('ws').text):.1f}" \
                      '\n'
        return result
    
    
    def repeat(root, txt):
        root.after(5_400_000, repeat, root, txt)  # 1.5시간 = 90분 = 5400초
        weather_text = crawl()
        weather_text += f"최종 확인: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        txt.delete(1.0, tk.END)
        txt.insert(tk.CURRENT, weather_text)
    
    
    def main():
        root = tk.Tk()
        root.geometry('400x360')
        root.title('오늘의 날씨')
        txt = tk.Text(root, height=24, font=('맑은 고딕', 9))
        txt.pack()
        repeat(root, txt)
        root.mainloop()
    
    
    if __name__ == '__main__':
        main()
    반응형
Designed by Tistory.