Project

Python / 제주 데이터허브 API를 이용해 일일 버스이용자 데이터 가져오기

*첫 작성일 20210428

*미세수정 20210521

 

0. 제주 데이터허브

 

제주데이터허브

 

www.jejudatahub.net

데이터분석에 관심이 있다면 공공데이터포털이나 서울 열린데이터 광장을 둘러본 경험도 한번쯤은 있을 것이다. 이런 사이트들의 제주도 버전이 바로 제주데이터허브다. 제주에서 IT 산업 육성에 굉장히 신경쓰고 있다던데, 그래서인지 제주데이터허브도 각종 데이터를 제공하고 있다. 회원가입만으로 사용이 가능하다.

 

1. 데이터 구성 

www.jejudatahub.net/data/view/data/613

 

제주데이터허브

 

www.jejudatahub.net

이 데이터의 API를 이용해보려고 한다. 일일 정류소별 버스 이용자 데이터로, 이 데이터에서 받아볼 수 있는 column들은 다음과 같다. 

1) baseDate : 날짜(해당일, yyyymmdd)

2) stationId : 정류소ID

3) stationName : 정류소명

4) moveType : 탑승구분(*승차, 환승만 나오는 것에 유의한다)

5) priceType : 요금제(일반/청소년/어린이/기타)

6) payType : 지불방법(카드/현금)

7) userCount : 이용자수

 

즉 해당일에 어느 정류소에서 몇명이 탔는지를 파악할 수 있는 흥미로운 자료다. 안타깝게도 이 데이터에서 하차까지는 확인할 수 없다. 나의 팀원님은 환승이 많은 정류소들이 모여있는 지역이 교통 거점이 아니겠냐고 했는데 확실히 그렇게도 볼 수 있을 것 같다. 

 

2. API 구성 

https://open.jejudatahub.net/api/proxy/1b1ta1a1ba6t6a1ttt3bD6b1tb1t1D3t/{your_projectKey}?{params(key=value)}

요청해야할 URL은 이렇게 생겼다. 채워넣으면 되는 부분은 your_projectKey와 파라미터들이다. API는 미리보기도 가능하다.

 

1) your_projectKey

데이터 사용을 위해선 먼저 '프로젝트 담기'를 해야 한다. 프로젝트를 담아야 API 요청을 통해 해당 데이터에 접근할 수 있다. (담지않으면 권한이 없어서 API 요청 시 403 Forbidden 에러가 발생한다. 네이버 API 사용시 인증키를 넣는 것과 같은 의미.) 

 

2) Params

요청할 수 있는 변수들은 다음과 같다. 

이름 데이터타입 필수입력여부 샘플데이터 항목설명 
startDate string Y YYYYMMDD 분석 시작일(필수)
endDate string Y YYYYMMDD 분석 종료일(필수)
moveType string N 승차, 환승 탑승구분
priceType string N 일반, 청소년, 어린이, 기타 요금제
payType string N 카드, 현금 지불방법

추가적으로, API 설명 문서에 적혀있는 API 공통 변수들은 다음과 같다. (문서는 hwp로 첨부되어 있다. IT를 육성하고자 함에도 버릴 수 없는 hwp. 적어도 pdf로 올려주면 좋겠다.) 아무튼 페이지별 데이터 수가 정해져 있어서, 만약 불러온 값이 페이지별 데이터 수를 넘어간다면 페이지 번호를 다음 번호로 넘겨주어야 한다. 참고로 그렇다고 해서 대략적인 페이지 번호를 추정할 필요는 없다. 반환되는 데이터 중 이후 페이지 데이터 유무를 알려주는 "hasMore"가 있기 때문에 이를 이용하고자 한다.  

이름 데이터타입 항목설명 참고
number int 페이지번호 default=1
limit int 페이지별 데이터 수  default=10, max=100

 

3. 코드 

 

에러 표시가 서툴러서 시간관계상 제외했고, 팀 공유용이라 주석을 라인별로 달았다. 전체 데이터가 다 필요해서 날짜 외에는 파라미터를 굳이 설정하지 않았지만, 선택한 데이터만 가져오고 싶다면 파라미터를 추가하면 될것이다. (ex : payType=카드&priceType=일반) 

import pandas as pd
import os
import sys
import urllib.request
import json

def jejubus(startDate, endDate):
    total = [] #값을 붙여넣을 빈 리스트
    pagenum = 1 # 첫번째 페이지 번호 
    key = "##input your project key##" # my project Key
    url = ("https://open.jejudatahub.net/api/proxy/1b1ta1a1ba6t6a1ttt3bD6b1tb1t1D3t/" + key + 
    "?startDate=" + str(startDate) + "&endDate=" + str(endDate) + "&number=" + str(pagenum)) # API url 구성
    
    request = urllib.request.Request(url)
    response = urllib.request.urlopen(request)
    result= json.loads(response.read().decode('utf-8')) #url 가져오기
    
    total.append(result) #크롤링된 결과를 total에 붙인다 
    
    while True: 
        if result["hasMore"] == True: #다음 페이지가 있다면 
            pagenum += 1 #페이지 넘기기 
            url = ("https://open.jejudatahub.net/api/proxy/1b1ta1a1ba6t6a1ttt3bD6b1tb1t1D3t/" + key + 
            "?startDate=" + str(startDate) + "&endDate=" + str(endDate) + "&number=" + str(pagenum))# API url 구성
    
            request = urllib.request.Request(url)
            response = urllib.request.urlopen(request)
            result= json.loads(response.read().decode('utf-8')) #다음 페이지 데이터 가져오기
            total.append(result)#가져온 다음 페이지의 데이터를 total에 붙인다
   
        else : #다음 페이지가 없다면 종료
            break
    return total
    
#크롤링 실행 
#시작날짜, 종료날짜를 넣는다 
result = jejubus(20180101, 20201215)

위에서 가져온 result는 다음과 같이 pandas DataFrame 형태로 만들어 주었다. 

# 크롤링 결과에서 값을 가져올 준비
# 일단 개별 값을 가져오기 위해 리스트를 생성해주고 (이 리스트를 pandas dataframe의 column으로 넣을 예정)
# for 문을 사용하기 위해 개별 값의 key(dict type이므로)리스트와 리스트들의 리스트를 만들어준다
date = []
stn_id = []                                          
stn_name = []
movetype = []                                                                                                                                         
pricetype = []
paytype = []
usercount = []

var_list = [date, stn_id, stn_name, movetype, pricetype, paytype, usercount]
dict_list = ['baseDate', 'stationId', 'stationName', 'moveType', 'priceType', 'payType', 'userCount']

# 크롤링 결과에서 개별 값을 가져와서 리스트로 만들기 

for i in range(len(result)):
    for index, key in enumerate(dict_list):
        var_list[index].extend([each[dict_list[index]] for each in result[i]["data"]])
        
#데이터프레임 생성 

df_result = pd.DataFrame({"baseDate" : date,
                         "stationId" : stn_id,
                         "stationName" : stn_name, 
                         "moveType" : movetype, 
                         "priceType" : pricetype, 
                         "payType" : paytype,
                         "userCount" : usercount})

그러면 아래와 같은 데이터프레임을 그릴 수 있다!

4. Outro 

 

데이터를 빠르게 확인해야 할 필요가 있어서 짰던 코드인지라 이런저런 부가기능(?)까지는 못 넣었다는 게 문제다. 어느날의 값을 가져오고 있는지 표시한다든가... 하여튼 좀더 세심하게 만들 필요가 있다.  

이렇게 만들어놓고 받아보니 우리 주제에는 데이터가 약간 미흡해서 활용은 못하고 다른 팀한테 공유해줬다. 참고로 2020년 1월 1일부터는 데이터 수가 이상할 정도로 급감해있는데(API 미리보기로 가져와도 같은 현상), 데이터가 누락되어 있지 않나 싶다. 활용하려면 2019년까지만 보는 게 확실할 것 같다. 아무튼 혼자 힘으로 구글링 안하고 여기까지 짤 수 있어서 조금은 뿌듯하다:)     


아직 뉴비여서 여러모로 미흡한 점이 많지만, 추후 조금씩 수정하기로 하고 일단 올려봅니다. 일단 이런 코드 공유글 자체가 처음이라서 코드의 수준도 제 설명도 많이 불친절합니다. 그래도 혹시나 저처럼 첫 API 사용으로 애를 먹는 분께는 도움이 되기를 바라고, 지나가던 고수분께서는 어여삐 봐주시고 리팩토링에 대한 조언을 주실 수 있다면 정말 감사히 받겠습니다.

 

위 코드는 제 github에서도 확인하실 수 있습니다. 

github.com/JeonghyunKo/jejutour_regression/blob/dec6f5c3c5300f3bbfe1512aaccb6df11ce438ab/jeju_api.ipynb

 

JeonghyunKo/jejutour_regression

Contribute to JeonghyunKo/jejutour_regression development by creating an account on GitHub.

github.com

 

+ 추가. 제주데이터허브 측에 요청해 API 형태가 아닌 대용량의 원본 데이터를 따로 받을 수 있었는데, 원본 데이터에서도 API로 받아왔을 때와 같이 2020년부터 승차정보량 자체가 급감해 있었다. 그래서 분석용으로는 일단 2019년 데이터까지 사용하였다.   

'Project' 카테고리의 다른 글

Python/crawling - Youtube 채널 데이터 크롤링  (0) 2021.06.20
Python / DASK(2) - use case : 불러오기, 인덱싱  (1) 2021.05.21
Python / DASK(1)  (0) 2021.05.14