본문 바로가기
Spec UP - Frontend/도전! 프로젝트

Python) 공공데이터 포털 API 공휴일 정보를 활용하여 전 영업일 계산하기

by TIS_Ha 2023. 4. 28.
반응형

파이썬을 활용하여 당일 기준 전 영업일을 계산하는 로직을 만들어보자.

  전영업일 : 주말+휴일을 제외하고 영업일 기준으로 전일자.

  Ex) 2023.5.8의 전영업일은 2023.5.4일이다.


한국 공휴일은 매년 달라지므로 공공데이터 포털 API를 활용하여 정보를 받아오도록 하자.

아래 작업을 수행하기 위해서는 먼저 회원가입을 해야한다.

회원가입이 완료되었다면 로그인 후 아래 사이트에 접근한다.

https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15012690 

 

한국천문연구원_특일 정보

(천문우주정보)국경일정보, 공휴일정보, 기념일정보, 24절기정보, 잡절정보를 조회하는 서비스 입니다. 활용시 날짜, 순번, 특일정보의 분류, 공공기관 휴일 여부, 명칭을 확인할 수 있습니다.

www.data.go.kr

[한국천문연구원_특일 정보] 내 [활용 신청]을 클릭하여 양식대로 작성한다.

(실시간으로 승인이 완료된 것으로 보아 작성 내용을 검수하지는 않는 것 같다.)

 

[활용신청]이 완료되었다면 우측 상단의 [마이페이지]로 이동한다.

[마이페이지]-[데이터활용]-[Open API] - [활용신청 현황] 에서 신청내역을 확인할 수 있다.

해당 페이지에서 [한국천문연구원_특일 정보]를 클릭하면 아래와 같이 인증키 정보를 확인할 수 있다.(

활용기간은 2년)

 


 [한국천문연구원_특일 정보]에서 확인한 일반 인증키로 아래와 같이 코드를 작성한다.

import 가 정상적으로 되지 않는 경우 cmd를 실행하여 아래 명령어를 실행한다.

  • pip install pandas
  • pip install requests
from datetime import datetime, date, timedelta
import pandas as pd
import requests
import json
from pandas import json_normalize

class KoreaHolidays:
    def get_holidays(self):
        today = datetime.today().strftime("%Y%m%d")
        today_year = datetime.today().year

        KEY = "인증키 입력"
        url = (
            "http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo?_type=json&numOfRows=50&solYear="
            + str(today_year)
            + "&ServiceKey="
            + str(KEY)
        )
        response = requests.get(url)
        if response.status_code == 200:
            json_ob = json.loads(response.text)
            holidays_data = json_ob["response"]["body"]["items"]["item"]
            dataframe = json_normalize(holidays_data)
        # dateName = dataframe.loc[dataframe["locdate"] == int(today), "dateName"]
        # print(dateName)
        return dataframe["locdate"].to_list()

today_year = datetime.today().year 를 통해 당해년도를 추출한다.

그리고 아래와 같이 URL을 호출하여 공휴일 정보를 받아오게된다.

http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo?_type=json&numOfRows=50&solYear=당해년도&ServiceKey=abcd 

2023년을 기준으로 호출 시 아래와 같이 공휴일 데이터가 불려온다.

{"response":{"header":{"resultCode":"00","resultMsg":"NORMAL SERVICE."},"body":{"items":{"item":[{"dateKind":"01","dateName":"1월1일","isHoliday":"Y","locdate":20230101,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20230121,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20230122,"seq":1},{"dateKind":"01","dateName":"설날","isHoliday":"Y","locdate":20230123,"seq":1},{"dateKind":"01","dateName":"대체공휴일","isHoliday":"Y","locdate":20230124,"seq":1},{"dateKind":"01","dateName":"삼일절","isHoliday":"Y","locdate":20230301,"seq":1},{"dateKind":"01","dateName":"어린이날","isHoliday":"Y","locdate":20230505,"seq":1},{"dateKind":"01","dateName":"부처님오신날","isHoliday":"Y","locdate":20230527,"seq":1},{"dateKind":"01","dateName":"현충일","isHoliday":"Y","locdate":20230606,"seq":1},{"dateKind":"01","dateName":"광복절","isHoliday":"Y","locdate":20230815,"seq":1},{"dateKind":"01","dateName":"추석","isHoliday":"Y","locdate":20230928,"seq":1},{"dateKind":"01","dateName":"추석","isHoliday":"Y","locdate":20230929,"seq":1},{"dateKind":"01","dateName":"추석","isHoliday":"Y","locdate":20230930,"seq":1},{"dateKind":"01","dateName":"개천절","isHoliday":"Y","locdate":20231003,"seq":1},{"dateKind":"01","dateName":"한글날","isHoliday":"Y","locdate":20231009,"seq":1},{"dateKind":"01","dateName":"기독탄신일","isHoliday":"Y","locdate":20231225,"seq":1}]},"numOfRows":50,"pageNo":1,"totalCount":16}}}

 

해당 데이터를 dataframe["locdate"].to_list() 을 통해 리스트 형태로 바꿔줍니다.

여기까지가 공휴일데이터를 받아오는 과정입니다.


이제 받아온 공휴일 정보를 활용하여 휴일 여부를 검증하는 oldday_is_holiday() 메소드를 생성해봅시다.

해당 메소드를 통하여 공휴일 API를 호출하고 올해의 공휴일 정보를 받아올 수 있습니다.

def oldday_is_holiday(tmp_date):
        tmp_date = tmp_date.strftime("%Y%m%d")
        holidays = koreaHolidays.get_holidays()
        is_holiday = False
        if int(tmp_date) in holidays:
            is_holiday = True
        return is_holiday

oldday_is_holiday(tmp_date) : 해당 메소드 호출 시 전달값인 tmp_date를 기준으로 아래의 메소드를 실행합니다.

 1) 위의 호출한 공휴일 데이터를 holidays 변수에 저장합니다.

 2) 전달받은 tmp_date가 holidays 안에 포함된 날짜인지 검증합니다.

 3) 만약 tmp_date가 holidays 안에 없는 날짜이면 false를,

     tmp_date가 holidays 안에 포함된 날짜이면 true를 반환합니다.

 

그런데 위의 공휴일 API에는 5/1일 근로자의 날이 불포함되어 있습니다.

이외에도 창립기념일 등 예외적인 휴일이 발생될 수 있습니다.

이러한 경우에는 위 데이터에 특정일자를 아래와 같이 추가하였습니다.

workersday = str(datetime.today().year) + "0501"
workersday = int(workersday)

def oldday_is_holiday(tmp_date):
        tmp_date = tmp_date.strftime("%Y%m%d")
        holidays = koreaHolidays.get_holidays()
        holidays.append(workersday)
        # holidays.append(20220908)
        is_holiday = False
        if int(tmp_date) in holidays:
            is_holiday = True
        return is_holiday

 

공휴일 데이터 받아온 후 특정 휴일 데이터 추가하여 holidays 변수에 저장한 후 

tmp_date값이 holidays 데이터 내에 있는 경우 is_holiday : true 반환

tmp_date값이 holidays 데이터 내에 없는 경우 is_holiday : false 반환하는 것 까지 설정해보았습니다.


이제 위에서 만들었던 메소드와 변수를 이용해 실제 전 영업일을 구해오도록 해보겠습니다.

tmp_date = date.today() - timedelta(1) 
koreaHolidays = KoreaHolidays()
is_holiday_result = oldday_is_holiday(tmp_date)

tmp_date = date.today() - timedelta(1) // 오늘 날짜에서 하루 전날을 tmp_date로 설정합니다.

is_holiday_result = oldday_is_holiday(tmp_date) // tmp_date를 holidays와 비교하여 반환된 true/false값을 입력합니다.

 

이때 전일자가 주말이거나, 휴일인 경우에 tmp_date는 -1 처리가 반복적으로 수행됩니다.

while True:
    if(tmp_date.weekday()>=5) or (is_holiday_result):
            tmp_date = tmp_date-timedelta(1)
            is_holiday_result = oldday_is_holiday(tmp_date)
    else:
          break

반복문 조건1)

tmp_date.weekday() : 날짜.weekday()의 값이 5나 6인 경우 토요일이나 일요일에 해당됩니다.

tmp_date.weekday()>=5 조건을 통하여 tmp_date가 주말인지 여부를 확인합니다.

반복문 조건2)

is_holiday_result : oldday_is_holiday(tmp_date) 비교하여 반환된 true/false 값을 확인합니다.

tmp_date가 holidays에 포함되었는지 여부를 체크합니다.

 

위 조건 1,2 중 하나라도 성립된다면 tmp_date는 -1일 처리됩니다.

-1일 처리된 tmp_date로 다시 주말여부와 공휴일 여부를 체크하여 적용합니다.

두개의 조건이 모두 false로 응답받게 되면 반복문을 종료됩니다.

 

즉 주말이나 휴일을 제외한 전영업일자를 확인할 수 있습니다.


완성된 전체 코드는 아래와 같습니다.

from datetime import datetime, date, timedelta
import pandas as pd
import requests
import json
from pandas import json_normalize

class KoreaHolidays:
    def get_holidays(self):
        today = datetime.today().strftime("%Y%m%d")
        today_year = datetime.today().year

        KEY = "kpMdx2%2FvhonYM%2FfE0PxjZelH62Up%2Fg8PL0RaNeO4fRaqjE%2F32RxBeVaPtcx0yEa5MY2WZ7WftTQeZJF5dZec3A%3D%3D"
        url = (
            "http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo?_type=json&numOfRows=50&solYear="
            + str(today_year)
            + "&ServiceKey="
            + str(KEY)
        )
        response = requests.get(url)
        if response.status_code == 200:
            json_ob = json.loads(response.text)
            holidays_data = json_ob["response"]["body"]["items"]["item"]
            dataframe = json_normalize(holidays_data)
        # dateName = dataframe.loc[dataframe["locdate"] == int(today), "dateName"]
        # print(dateName)
        return dataframe["locdate"].to_list()

workersday = str(datetime.today().year) + "0501"
workersday = int(workersday)
def oldday_is_holiday(tmp_date):
        tmp_date = tmp_date.strftime("%Y%m%d")
        holidays = koreaHolidays.get_holidays()
        holidays.append(workersday)
        # holidays.append(20220908)
        is_holiday = False
        if int(tmp_date) in holidays:
            is_holiday = True
        return is_holiday

#In[2]
tmp_date = date.today() - timedelta(1) 
koreaHolidays = KoreaHolidays()
is_holiday_result = oldday_is_holiday(tmp_date)

# In[3]:
while True:
    if(tmp_date.weekday()>=5) or (is_holiday_result):
            tmp_date = tmp_date-timedelta(1)
            is_holiday_result = oldday_is_holiday(tmp_date)
    else:
          break

print(date.today())
print(tmp_date)
반응형

댓글