본문 바로가기
Study/Python

공공 데이터 포털에서 "공휴일" 정보 받기

by 개발새-발 2021. 7. 15.
반응형

공휴일을 모아놓은 데이터가 필요할 때가 있다. 어떤 데이터에 날짜가 포함되어있는데 우리가 예측하려는 값이 휴일과 관계가 있을지도 모르기 때문이다. 공공 데이터 포털에서는 공휴일 정보를 받을 수 있는 api도 제공하고 있다. 이 api를 사용해 보자.

API 사용 전 준비

회원가입

Api를 사용하기 전, 공공데이터 포털에서 회원가입을 하여야 한다. 공공데이터 포털 우측 상단에 회원가입 버튼을 눌러 회원가입부터 진행해 주자.

활용신청

Api 활용신청을 해주자. 우리가 사용할 데이터는 한국 천문연구원의 특일정보 데이터이다. 데이터 검색화면에 특일로 검색하여 특일정보 데이터의 활용신청을 해주자.

  • 검색

  • 검색결과

  • 활용신청

활용 신청 후 승인이 되었다면 마이페이지 > 개발계정에서 다음과 같은 화면을 볼 수 있다. 여기서 한국천문연구원_특일정보 를 눌러주면 개발계정 상세페이지로 들어갈 수 있다.

  • 개발 계정 화면

  • 개발계정 상세 페이지

개발계정 상세페이지에서는 데이터와 관련된 문서를 받을 수 있다. 이 문서를 활용하면 개발이나 분석 시에 데이터를 이해하거나 사용하는 데에 더 도움이 될 것이다. 이 페이지에서 Encode 된 인증키, Decode 된 인증키를 볼 수 있는데, 우리는 Decode된 인증키를 사용할 것이다.

API 사용하기

import requests
import json

url

특일정보 API에서는 국경일, 공휴일, 기념일, 24절기, 잡절에 대한 정보를 받아올 수 있다. 각각의 정보를 받기 위해 필요한 operation이 다른데, operation에 따라 요청 url 뒤에 붙는 내용이 달라진다. 우리는 공휴일 정보를 불러올 것이기 때문에 getRestDeInfo를 사용할 것이다. 따라서 쿼리를 넣기 전 요청 url 형태는 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo' 이 된다.

url = 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo'

쿼리

  • solYear, solMonth
    데이터 요청 시에 쿼리로 solYear로 정보를 얻고자 하는 연도를, solMonth로 월을 입력해야 한다. '연도'는 4자리 여야 하고 ''은 2자리 여야 한다. 만약 1월이라면 '1'이 아닌 '01'을 입력하여야 한다.
  • numOfRows
    한 페이지에 표시되는 결과물의 수이다. 기본값은 10이며, 만약 그 달에 공휴일이 10일이 넘을 것으로 예상되면 이 값을 10보다 더 큰 값으로 변경하여 받아오자.
  • ServiceKey
    인증키이다. Decode 된 인증키를 입력하여 주자.
  • _type
    데이터를 불러올 형식xmljson에서 고를 수 있다. 기본값은 xml이다. 그러나 python에서는 json으로 처리하는 것이 좀 더 편하기 때문에 이 예제에서는 json형식으로 불러오도록 하겠다.
# DO NOT EXPOSE THIS KEY TO OUTSIDE
AUTH_KEY = 'keykeykeykeykeykeykeykeykeykey'
params = {
    'solYear':str(2020),
    'solMonth':str(1).zfill(2),
    '_type':'json',
    'ServiceKey' : AUTH_KEY
}

응답받기

위 url과 params로 requests.get을 이용하여 응답을 받아보자.

response =  requests.get(url,params=params)
print(response.text)
{"response":{"header":{"resultCode":"00","resultMsg":"NORMAL SERVICE."},"body":{"items":{"item":[{"dateKind":"01","dateName":"1월1일","isHoliday":"Y","locdate":20200101,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20200124,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20200125,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20200126,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20200127,"seq":1}]},"numOfRows":10,"pageNo":1,"totalCount":5}}}

위 결과를 json 모듈을 이용하여 python dictionary 형태로 만들자.

result = json.loads(response.text)

아래 코드는 결과물을 쉽게 읽을 수 있게 한다.

print(json.dumps(result,indent=4,ensure_ascii=False))
{
    "response": {
        "header": {
            "resultCode": "00",
            "resultMsg": "NORMAL SERVICE."
        },
        "body": {
            "items": {
                "item": [
                    {
                        "dateKind": "01",
                        "dateName": "1월1일",
                        "isHoliday": "Y",
                        "locdate": 20200101,
                        "seq": 1
                    },
                    {
                        "dateKind": "01",
                        "dateName": "설날",
                        "isHoliday": "Y",
                        "locdate": 20200124,
                        "seq": 1
                    },
                    {
                        "dateKind": "01",
                        "dateName": "설날",
                        "isHoliday": "Y",
                        "locdate": 20200125,
                        "seq": 1
                    },
                    {
                        "dateKind": "01",
                        "dateName": "설날",
                        "isHoliday": "Y",
                        "locdate": 20200126,
                        "seq": 1
                    },
                    {
                        "dateKind": "01",
                        "dateName": "설날",
                        "isHoliday": "Y",
                        "locdate": 20200127,
                        "seq": 1
                    }
                ]
            },
            "numOfRows": 10,
            "pageNo": 1,
            "totalCount": 5
        }
    }
}

위 결과물을 보면, 정상적으로 수행된 경우 ['response']['body']['items']['item']의 리스트 내부에 각각의 공휴일이 dictionary 형태로 담겨있는 모습을 볼 수 있다. 그렇다면 공휴일이 없는 월에 대해서는 어떨까?

params = {
    'solYear':str(2020),
    'solMonth':str(2).zfill(2),
    '_type':'json',
    'ServiceKey' : AUTH_KEY
}

response =  requests.get(url,params=params)
result = json.loads(response.text)
print(json.dumps(result,indent=4,ensure_ascii=False))
{
    "response": {
        "header": {
            "resultCode": "00",
            "resultMsg": "NORMAL SERVICE."
        },
        "body": {
            "items": "",
            "numOfRows": 10,
            "pageNo": 1,
            "totalCount": 0
        }
    }
}

items가 비어있는 것을 볼 수 있다. 공휴일이 하루인 월에 대해서 불러오는 경우는 아래처럼 리스트 안에 연휴의 dictionary가 있지 않고 dictionary가 ['response']['body']['items']['item']에 위치하게 된다.

params = {
    'solYear':str(2020),
    'solMonth':str(12).zfill(2),
    '_type':'json',
    'ServiceKey' : AUTH_KEY
}

response =  requests.get(url,params=params)
result = json.loads(response.text)
print(json.dumps(result,indent=4,ensure_ascii=False))
{
    "response": {
        "header": {
            "resultCode": "00",
            "resultMsg": "NORMAL SERVICE."
        },
        "body": {
            "items": {
                "item": {
                    "dateKind": "01",
                    "dateName": "기독탄신일",
                    "isHoliday": "Y",
                    "locdate": 20201225,
                    "seq": 1
                }
            },
            "numOfRows": 10,
            "pageNo": 1,
            "totalCount": 1
        }
    }
}

위 내용을 고려하여 연도와 월을 입력하면 공휴일의 리스트를 반환하는 코드를 작성해보자. 주어진 연도와 월에 공휴일이 없는 경우에는 빈 리스트를 반환하도록 하였다.

def getHolidays(year,month,key):
    url = 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo'

    params = {
        'solYear':str(year),
        'solMonth':str(month).zfill(2),
        '_type':'json',
        'ServiceKey' : key
    }

    res = requests.get(url,params=params)
    dic = json.loads(res.text)
    counts = dic['response']['body']['totalCount']

    if counts < 1 :
        return []

    item =  dic['response']['body']['items']['item']

    if counts == 1:
        return [item]

    return item
getHolidays(2020,1,AUTH_KEY)
[{'dateKind': '01',
  'dateName': '1월1일',
  'isHoliday': 'Y',
  'locdate': 20200101,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20200124,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20200125,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20200126,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20200127,
  'seq': 1}]

여러 월에 대해 공휴일 불러오기

만약 더 긴 시간 범위에 대해서 공휴일을 불러오고자 한다면 반복문을 이용하여 여러 번 불러와야 한다. 예를 들어 2016년도부터 2021년도의 공휴일 데이터를 얻고자 한다면 '연도'는 2016부터 2021까지, 월은 1월부터 12월까지 반복문을 돌려주면서 결과를 불러와 합쳐주어야 한다.

holidays =[]
for year in range(2016,2022):
    for month in range(1,13):
        holidays.extend(getHolidays(year,month,AUTH_KEY))

위 코드에서 만든 'holidays'를 확인하여 보자.

len(holidays)
103
holidays[:10]
[{'dateKind': '01',
  'dateName': '신정',
  'isHoliday': 'Y',
  'locdate': 20160101,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20160207,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20160208,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '설날',
  'isHoliday': 'Y',
  'locdate': 20160209,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '대체공휴일',
  'isHoliday': 'Y',
  'locdate': 20160210,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '삼일절',
  'isHoliday': 'Y',
  'locdate': 20160301,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '국회의원선거일',
  'isHoliday': 'Y',
  'locdate': 20160413,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '어린이날',
  'isHoliday': 'Y',
  'locdate': 20160505,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '석가탄신일',
  'isHoliday': 'Y',
  'locdate': 20160514,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '현충일',
  'isHoliday': 'Y',
  'locdate': 20160606,
  'seq': 1}]
holidays[-10:]
[{'dateKind': '01',
  'dateName': '어린이날',
  'isHoliday': 'Y',
  'locdate': 20210505,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '부처님오신날',
  'isHoliday': 'Y',
  'locdate': 20210519,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '현충일',
  'isHoliday': 'Y',
  'locdate': 20210606,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '광복절',
  'isHoliday': 'Y',
  'locdate': 20210815,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '추석',
  'isHoliday': 'Y',
  'locdate': 20210920,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '추석',
  'isHoliday': 'Y',
  'locdate': 20210921,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '추석',
  'isHoliday': 'Y',
  'locdate': 20210922,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '개천절',
  'isHoliday': 'Y',
  'locdate': 20211003,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '한글날',
  'isHoliday': 'Y',
  'locdate': 20211009,
  'seq': 1},
 {'dateKind': '01',
  'dateName': '기독탄신일',
  'isHoliday': 'Y',
  'locdate': 20211225,
  'seq': 1}]

재사용을 위해 csv 형태로 저장하기

이후 공휴일 데이터가 필요할 때마다 API를 호출하는 것은 비효율적이다. 이를 위해 방금 가져온 2016년도부터 2021년도의 공휴일 데이터를 csv형태로 만들어 저장하자.

import pandas as pd

df_holiday = pd.DataFrame(holidays,columns=['locdate','dateName'])
df_holiday.to_csv('./holiday.csv',index=None)
df_holiday
  locdate dateName
0 20160101 신정
1 20160207 설날
2 20160208 설날
3 20160209 설날
4 20160210 대체공휴일
... ... ...
98 20210921 추석
99 20210922 추석
100 20211003 개천절
101 20211009 한글날
102 20211225 기독탄신일

103 rows × 2 columns

마치며

공공 데이터 포털을 이용하여 공휴일 정보를 불러와 보았다. 공공데이터 포털에서는 공휴일, 특일 뿐만이 아닌 다양한 정보를 제공하고 있다. 필요하다면 공공 데이터 포털에서 공유되는 정보를 이용하는 것도 좋은 선택이 될 것이다. 또, 위 글에서 다룬 정보에 대해 더 자세한 내용은 위에서 언급한 개발정보 상세페이지의 참고 문서에서 확인할 수 있다.

반응형

댓글