Programming/Python

[Python] 공공데이터 오픈 API를 활용한 기상청 ASOS 데이터 파싱하기

* 해당 글은 본인 스스로 정리하는 글이며 자세한 과정에 대해서는 다루고 있지 않음.

 

공공데이터

  • 공공데이터 포털에서는 국가 보유 데이터를 『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방 중이며 공공데이터(Dataset)와 Open API로 제공하고 있다.

 

공공데이터 (Dataset)

  • 일일이 내려받기를 통해 다운로드 가능하다.
  • 파일 안에 자료가 정해져 있기 때문에 일단 내려받은 후 필요한 데이터만 가공하여 사용 가능하다.
  • 활용 신청 과정이 불필요하므로 내려받기가 간편하다. 
  • 아무래도 일일이 다운받다가 착각으로 인한 실수(?)를 할 가능성이 존재한다.

 

공공데이터 Open API

Open API란?
누구나 사용할 수 있도록 공개된 API(Open Application Programming Interface)를 말한다. 즉, 하나의 웹사이트에서 자신이 가지고 있는 기능을 이용할 수 있도록 공개한 것이며 대표적으로 네이버 지도, 구글 맵 등이 있다.

 

  • 데이터를 받을 때 부터 필요한 데이터만 요청을 통해 추출 후 *파싱 가능하다.
  • 활용 신청 과정을 통해 서비스 키를 발급받아야 하는 번거로움이 있다.
  • 코드를 통해 요청 가능하므로 하나하나 내려받는 경우에 비해 노동력이 절감된다.

* 파싱(parsing) : 데이터를 조립해 원하는 데이터를 추출하는 과정

 

오픈 API를 통한 공공데이터 수집 과정

  1. 필요한 공공데이터 Open API 존재 여부 확인
  2. 활용 신청을 통한 서비스 키 발급
  3. 코드를 통해 데이터 요청 및 출력
  4. 원하는 형식으로 가공

 

수집하고자 하는 공공데이터

  • 기상청_지상(종관, ASOS) 일 자료  

* 종관기상관측장비(綜觀氣象觀測裝備, ASOS: Automated Surface Observing System)는 주로 지방 기상청, 기상대, 기상관측소 등의 기상 관서에서 운용하며, 자동기상관측장비의 일종이지만 더 많은 기상 관측 요소를 관측한다.

 

 

샘플 코드 수정 부분

공공데이터 포털에서 제공하는 Python 샘플 코드는 현재 약간의 수정이 필요하다.

# 수정 전
from urllib2 import Request, urlopen
from urllib import urlencode, quote_plus

# 수정 후 
from urllib.request import Request, urlopen
from urllib.parse import urlencode, quote_plus

 

데이터 파싱 예시 코드

한 번의 실행으로 999건에 대한 데이터를 요청하는 함수를 만들어 이 코드를 반복 실행할 예정이다. 

아래의 코드는 상황에 따라 지역과 시작 날짜를 정해줄 수 있다.

 

# 입력 매개변수
# stnIds : 지역 (108 # 서울)
# startDt : 날짜 범위 시작 (19870101)

def get_data(startDt_, stnIds_):
  from urllib.request import Request, urlopen
  from urllib.parse import urlencode, quote_plus
  import bs4
  import pandas as pd
  import numpy as np
  from datetime import timedelta

  url = 'http://apis.data.go.kr/1360000/AsosDalyInfoService/getWthrDataList'
  my_key = '서비스 키'
  
  # 날짜 범위의 최대값을 '20210501'로 설정
  queryParams = '?' + urlencode({ quote_plus('ServiceKey') : my_key, quote_plus('pageNo') : '1', quote_plus('numOfRows') : '999', quote_plus('dataType') : 'XML', quote_plus('dataCd') : 'ASOS', quote_plus('dateCd') : 'DAY', quote_plus('startDt') : startDt_ , quote_plus('endDt') : '20210501', quote_plus('stnIds') : stnIds_ })

  request = Request(url + queryParams)
  request.get_method = lambda: 'GET'
  response_body = urlopen(request).read()

  xmlobj = bs4.BeautifulSoup(response_body, 'lxml-xml')
  items = xmlobj.findAll('item')

  ## 원하는 형태로 가공
  asos = pd.DataFrame()
  for i in items:
    dic_ = {}
    for j in i.find_all():
      dic_[j.name] = [j.text]
    asos = pd.concat([asos,pd.DataFrame(dic_)],axis=0)
  
  # 한번에 1000건 이상 요청이 불가능한 상황에 대한 조치
  max_ = pd.to_datetime(asos['tm'], format = '%Y-%m-%d').max()
  max_1 = max_ + timedelta(days=1)

  set_startDt_ = str(max_1.date()).replace('-','')

  # 저장된 데이터 프레임과 다음 요청 시 시작될 날짜를 반환
  return asos, set_startDt_

해당 코드를 while 문을 통해 반복 실행하여 데이터를 추출할 수 있다. 

startDt_ = '19870101'
stnIds_ = '108'

날짜 범위를 '19870101'에서 '20210502'까지로 설정하고 데이터를 추출하기 위해 아래와 같은 코드를 설계하였다.

asos_ = pd.DataFrame()
while startDt_ != '20210502':
    temp_asos_, startDt_ = get_data(startDt_,stnIds_)
    asos_ = pd.concat([asos_,temp_asos_],axis=0)
    print(startDt_)

추출된 데이터는 asos_ 데이터프레임에 저장되며 상황에 맞게 필요한 변수만 따로 가공하여 사용이 가능하다.

 


[ASOS 정의 참고] https://ko.wikipedia.org/wiki/%EC%9E%90%EB%8F%99%EA%B8%B0%EC%83%81%EA%B4%80%EC%B8%A1%EC%9E%A5%EB%B9%84