본문 바로가기

Python_Intermediate/Pandas

Pandas - 영화 진흥원 API 상영 순위 분석

반응형

1. Site

http://www.kobis.or.kr/kobisopenapi/homepg/main/main.do


2. API 발급


3. Code

import datetime as dt
import requests
import json
import pandas
from pandas import DataFrame
from matplotlib import pyplot
from print_df import print_df

kobis_api_key = "API"

kobis_api_url = "http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=%s&targetDt=%s"

today = dt.datetime.now()
delta = dt.timedelta(days=-1)
yesterday = today + delta
yesterday_str = yesterday.strftime("%Y%m%d")

api_url = kobis_api_url % (kobis_api_key, yesterday_str)
print_df(api_url)

r = requests.get(api_url)

if r.status_code != 200:
print("[%d Error] %s" % (r.status_code, r.reason))
quit()

r.encoding = "utf-8"
result = json.loads(r.text)

df = DataFrame(result['boxOfficeResult']['dailyBoxOfficeList'])

df = df.filter(items=['movieNm', 'audiCnt'])

movie_list = list(df['movieNm'])
index_dict = {}

for i, v in enumerate(movie_list):
index_dict[i] = v

df.rename(index=index_dict, columns={'audiCnt': '관람객수'}, inplace=True)

df.drop('movieNm', axis=1, inplace=True)

df['관람객수'] = df['관람객수'].apply(pandas.to_numeric)

df.sort_values('관람객수', inplace=True, ascending=True)

df = df.dropna()
empty_sum = df.isnull().sum()

pyplot.rcParams["font.family"] = 'NanumGothic'
pyplot.rcParams["font.size"] = 14
pyplot.rcParams["figure.figsize"] = (16, 8)

df.plot.barh()
pyplot.grid()
pyplot.title("%s 박스오피스 순위" % yesterday_str)
pyplot.legend()
pyplot.savefig('boxoffice_daily.png', dpi=200)
pyplot.show()
pyplot.close()


4. Code 풀이

- Import Module

import datetime as dt
import requests
import json
import pandas
from pandas import DataFrame
from matplotlib import pyplot
from print_df import print_df


- 영화 진흥원 API

kobis_api_key = "API"


- request 주소

kobis_api_url = "http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=%s&targetDt=%s"


- 인터페이스 / 응답구조 확인

  • 요청 인터페이스
    요청 변수설명
    key문자열(필수)발급받은키 값을 입력합니다.
    targetDt문자열(필수)조회하고자 하는 날짜를 yyyymmdd 형식으로 입력합니다.
    itemPerPage문자열결과 ROW 의 개수를 지정합니다.(default : “10”, 최대 : “10“)
    multiMovieYn문자열다양성 영화/상업영화를 구분지어 조회할 수 있습니다.
    “Y” : 다양성 영화 “N” : 상업영화 (default : 전체)
    repNationCd문자열한국/외국 영화별로 조회할 수 있습니다.
    “K: : 한국영화 “F” : 외국영화 (default : 전체)
    wideAreaCd문자열상영지역별로 조회할 수 있으며, 지역코드는 공통코드 조회 서비스에서 “0105000000” 로서 조회된 지역코드입니다. (default : 전체)
  • 응답 구조
    응답 필드설명
    boxofficeType문자열박스오피스 종류를 출력합니다.
    showRange문자열박스오피스 조회 일자를 출력합니다.
    rnum문자열순번을 출력합니다.
    rank문자열해당일자의 박스오피스 순위를 출력합니다.
    rankInten문자열전일대비 순위의 증감분을 출력합니다.
    rankOldAndNew문자열랭킹에 신규진입여부를 출력합니다.
    “OLD” : 기존 , “NEW” : 신규
    movieCd문자열영화의 대표코드를 출력합니다.
    movieNm문자열영화명(국문)을 출력합니다.
    openDt문자열영화의 개봉일을 출력합니다.
    salesAmt문자열해당일의 매출액을 출력합니다.
    salesShare문자열해당일자 상영작의 매출총액 대비 해당 영화의 매출비율을 출력합니다.
    salesInten문자열전일 대비 매출액 증감분을 출력합니다.
    salesChange문자열전일 대비 매출액 증감 비율을 출력합니다.
    salesAcc문자열누적매출액을 출력합니다.
    audiCnt문자열해당일의 관객수를 출력합니다.
    audiInten문자열전일 대비 관객수 증감분을 출력합니다.
    audiChange문자열전일 대비 관객수 증감 비율을 출력합니다.
    audiAcc문자열누적관객수를 출력합니다.
    scrnCnt문자열해당일자에 상영한 스크린수를 출력합니다.
    showCnt문자열해당일자에 상영된 횟수를 출력합니다.


- 연동 사전 작업 - 하루 전 날짜값 생성

today = dt.datetime.now()
delta = dt.timedelta(days=-1)
yesterday = today + delta
yesterday_str = yesterday.strftime("%Y%m%d")


- API와 날짜로 접속할  URL 생성

api_url = kobis_api_url % (kobis_api_key, yesterday_str)
print_df(api_url)


- URL에 접속해 결과 생성

r = requests.get(api_url)


- 가져온 데이터를 딕셔너리 형태로 변환

r.encoding = "utf-8"
result = json.loads(r.text)

{'boxOfficeResult': {'boxofficeType': '일별 박스오피스', 'showRange': '20190601~20190601', 'dailyBoxOfficeList': [{'rnum': '1', 'rank': '1', 'rankInten': '0', 'rankOldAndNew': 'OLD', 'movieCd': '20183782', 'movieNm': '기생충', 'openDt': '2019-05-30', 'salesAmt': '9907985973', 'salesShare': '68.8', 'salesInten': '4014544998', 'salesChange': '68.1', 'salesAcc': '20479182448', 'audiCnt': '1126561', 'audiInten': '458769', 'audiChange': '68.7', 'audiAcc': '2372310', 'scrnCnt': '1926', 'showCnt': '9902'}, {'rnum': '2', 'rank': '2', 'rankInten': '0', 'rankOldAndNew': 'OLD', 'movieCd': '20183867', 'movieNm': '알라딘', 'openDt': '2019-05-23', 'salesAmt': '2610974127', 'salesShare': '18.1', 'salesInten': '1526830402', 'salesChange': '140.8', 'salesAcc': '15416106172', 'audiCnt': '306058', 'audiInten': '182790', 'audiChange': '148.3', 'audiAcc': '1842303', 'scrnCnt': '965', 'showCnt': '3451'}, {'rnum': '3', 'rank': '3', 'rankInten': '1', 'rankOldAndNew': 'OLD', 'movieCd': '20184865', 'movieNm': '고질라: 킹 오브 몬스터', 'openDt': '2019-05-29', 'salesAmt': '738268373', 'salesShare': '5.1', 'salesInten': '377834088', 'salesChange': '104.8', 'salesAcc': '1994088788', 'audiCnt': '78282', 'audiInten': '40563', 'audiChange': '107.5', 'audiAcc': '236118', 'scrnCnt': '610', 'showCnt': '1615'}, {'rnum': '4', 'rank': '4', 'rankInten': '-1', 'rankOldAndNew': 'OLD', 'movieCd': '20188941', 'movieNm': '악인전', 'openDt': '2019-05-15', 'salesAmt': '567294200', 'salesShare': '3.9', 'salesInten': '158201730', 'salesChange': '38.7', 'salesAcc': '27995218090', 'audiCnt': '62886', 'audiInten': '16299', 'audiChange': '35', 'audiAcc': '3230927', 'scrnCnt': '569', 'showCnt': '1628'}, {'rnum': '5', 'rank': '5', 'rankInten': '0', 'rankOldAndNew': 'OLD', 'movieCd': '20188144', 'movieNm': '0.0MHz', 'openDt': '2019-05-29', 'salesAmt': '220796140', 'salesShare': '1.5', 'salesInten': '87998700', 'salesChange': '66.3', 'salesAcc': '658800680', 'audiCnt': '25945', 'audiInten': '11024', 'audiChange': '73.9', 'audiAcc': '85289', 'scrnCnt': '409', 'showCnt': '673'}, {'rnum': '6', 'rank': '6', 'rankInten': '0', 'rankOldAndNew': 'OLD', 'movieCd': '20184889', 'movieNm': '어벤져스: 엔드게임', 'openDt': '2019-04-24', 'salesAmt': '114133860', 'salesShare': '0.8', 'salesInten': '41070580', 'salesChange': '56.2', 'salesAcc': '121245236720', 'audiCnt': '13071', 'audiInten': '4875', 'audiChange': '59.5', 'audiAcc': '13832015', 'scrnCnt': '158', 'showCnt': '207'}, {'rnum': '7', 'rank': '7', 'rankInten': '12', 'rankOldAndNew': 'OLD', 'movieCd': '20192001', 'movieNm': '피터팬: 후크 선장과 결투의 날', 'openDt': '2019-05-30', 'salesAmt': '33691000', 'salesShare': '0.2', 'salesInten': '30547000', 'salesChange': '971.6', 'salesAcc': '39119000', 'audiCnt': '4394', 'audiInten': '4002', 'audiChange': '1020.9', 'audiAcc': '5094', 'scrnCnt': '129', 'showCnt': '157'}, {'rnum': '8', 'rank': '8', 'rankInten': '-1', 'rankOldAndNew': 'OLD', 'movieCd': '20184082', 'movieNm': '걸캅스', 'openDt': '2019-05-09', 'salesAmt': '36020220', 'salesShare': '0.3', 'salesInten': '4075210', 'salesChange': '12.8', 'salesAcc': '13711831634', 'audiCnt': '4258', 'audiInten': '413', 'audiChange': '10.7', 'audiAcc': '1614861', 'scrnCnt': '164', 'showCnt': '215'}, {'rnum': '9', 'rank': '9', 'rankInten': '0', 'rankOldAndNew': 'OLD', 'movieCd': '20199448', 'movieNm': '어린 의뢰인', 'openDt': '2019-05-22', 'salesAmt': '22468160', 'salesShare': '0.2', 'salesInten': '9399450', 'salesChange': '71.9', 'salesAcc': '1583056500', 'audiCnt': '2725', 'audiInten': '1055', 'audiChange': '63.2', 'audiAcc': '191510', 'scrnCnt': '144', 'showCnt': '189'}, {'rnum': '10', 'rank': '10', 'rankInten': '-2', 'rankOldAndNew': 'OLD', 'movieCd': '20198597', 'movieNm': '교회오빠', 'openDt': '2019-05-16', 'salesAmt': '20487930', 'salesShare': '0.1', 'salesInten': '6179030', 'salesChange': '43.2', 'salesAcc': '512607320', 'audiCnt': '2372', 'audiInten': '669', 'audiChange': '39.3', 'audiAcc': '61958', 'scrnCnt': '65', 'showCnt': '86'}]}}


- 영화 목록 배열을 데이터 프레임으로 변환
df = DataFrame(result['boxOfficeResult']['dailyBoxOfficeList'])
+---+---------+------------+---------+-----------+----------+------------------------+------------+------+-----------+---------------+------+-------------+------------+-------------+------------+------------+---------+---------+
|   | audiAcc | audiChange | audiCnt | audiInten | movieCd  |        movieNm         |   openDt   | rank | rankInten | rankOldAndNew | rnum |   salesAcc  |  salesAmt  | salesChange | salesInten | salesShare | scrnCnt | showCnt |
+---+---------+------------+---------+-----------+----------+------------------------+------------+------+-----------+---------------+------+-------------+------------+-------------+------------+------------+---------+---------+
| 0 | 2372310 |    68.7    | 1126561 |   458769  | 20183782 |         기생충         | 2019-05-30 |  1   |     0     |      OLD      |  1   | 20479182448 | 9907985973 |     68.1    | 4014544998 |    68.8    |   1926  |   9902  |
| 1 | 1842303 |   148.3    |  306058 |   182790  | 20183867 |         알라딘         | 2019-05-23 |  2   |     0     |      OLD      |  2   | 15416106172 | 2610974127 |    140.8    | 1526830402 |    18.1    |   965   |   3451  |
| 2 |  236118 |   107.5    |  78282  |   40563   | 20184865 | 고질라: 킹 오브 몬스터 | 2019-05-29 |  3   |     1     |      OLD      |  3   |  1994088788 | 738268373  |    104.8    | 377834088  |    5.1     |   610   |   1615  |
| 3 | 3230927 |     35     |  62886  |   16299   | 20188941 |         악인전         | 2019-05-15 |  4   |     -1    |      OLD      |  4   | 27995218090 | 567294200  |     38.7    | 158201730  |    3.9     |   569   |   1628  |
| 4 |  85289  |    73.9    |  25945  |   11024   | 20188144 |         0.0MHz         | 2019-05-29 |  5   |     0     |      OLD      |  5   |  658800680  | 220796140  |     66.3    |  87998700  |    1.5     |   409   |   673   |
+---+---------+------------+---------+-----------+----------+------------------------+------------+------+-----------+---------------+------+-------------+------------+-------------+------------+------------+---------+---------+


- 영화 제목 / 관람객 수 필터링 작업

df = df.filter(items=['movieNm', 'audiCnt'])

+---+------------------------+---------+

|   |        movieNm         | audiCnt |

+---+------------------------+---------+

| 0 |         기생충         | 1126561 |

| 1 |         알라딘         |  306058 |

| 2 | 고질라: 킹 오브 몬스터 |  78282  |

| 3 |         악인전         |  62886  |

| 4 |         0.0MHz         |  25945  |

+---+------------------------+---------+


- 영화 이름 리스트 작업

movie_list = list(df['movieNm'])
index_dict = {}

for i, v in enumerate(movie_list):
index_dict[i] = v


- 컬럼이름 변경

df.rename(index=index_dict, columns={'audiCnt': '관람객수'}, inplace=True)


- 필요없는 열 삭제 후 결과 확인

df.drop('movieNm', axis=1, inplace=True)

+------------------------+----------+

|                        | 관람객수 |

+------------------------+----------+

|         기생충         | 1126561  |

|         알라딘         |  306058  |

| 고질라: 킹 오브 몬스터 |  78282   |

|         악인전         |  62886   |

|         0.0MHz         |  25945   |

+------------------------+----------+


- 관람객 수 확인

print(df['관람객수'])

기생충                  1126561

알라딘                   306058

고질라: 킹 오브 몬스터          78282

악인전                    62886

0.0MHz                 25945

어벤져스: 엔드게임             13071

피터팬: 후크 선장과 결투의 날       4394

걸캅스                     4258

어린 의뢰인                  2725

교회오빠                    2372

Name: 관람객수, dtype: object


- 관람객수 열의 타입 숫자로 변환 후 재 확인

df['관람객수'] = df['관람객수'].apply(pandas.to_numeric)

기생충                  1126561

알라딘                   306058

고질라: 킹 오브 몬스터          78282

악인전                    62886

0.0MHz                 25945

어벤져스: 엔드게임             13071

피터팬: 후크 선장과 결투의 날       4394

걸캅스                     4258

어린 의뢰인                  2725

교회오빠                    2372

Name: 관람객수, dtype: int64


- 관람객수 기준으로 오름차순 정렬

df.sort_values('관람객수', inplace=True, ascending=True)

+-------------------------------+----------+

|                               | 관람객수 |

+-------------------------------+----------+

|            교회오빠           |   2372   |

|          어린 의뢰인          |   2725   |

|             걸캅스            |   4258   |

| 피터팬: 후크 선장과 결투의 날 |   4394   |

|       어벤져스: 엔드게임      |  13071   |

|             0.0MHz            |  25945   |

|             악인전            |  62886   |

|     고질라: 킹 오브 몬스터    |  78282   |

|             알라딘            |  306058  |

|             기생충            | 1126557  |

+-------------------------------+----------+


- 결측치 확인 후 삭제

df = df.dropna()
empty_sum = df.isnull().sum()

<class 'pandas.core.series.Series'>

관람객수    0

dtype: int64


- 시각화 작업

pyplot.rcParams["font.family"] = 'NanumGothic'
pyplot.rcParams["font.size"] = 14
pyplot.rcParams["figure.figsize"] = (16, 8)

df.plot.barh()
pyplot.grid()
pyplot.title("%s 박스오피스 순위" % yesterday_str)
pyplot.legend()
pyplot.savefig('boxoffice_daily.png', dpi=200)
pyplot.show()
pyplot.close()


반응형