공공데이터 API 기반 크롤링

leban·2022년 5월 16일
0

데이터분석

목록 보기
2/6

# 공공데이터 활용 신청

1. 공공데이터포털 회원가입하기

: www.data.go.kr

2. 출입국관광통계서비스 검색하기

3. [오픈 API] - [출입국관광통계서비스] 클릭

4. OpenAPI 개발계정 신청하기

5. 활용 목적 입력

6. 활용 신청 완료

7. OpenAPI 개발계정 발급받기

8. 오픈 API 사용 방법 확인하기



# 공공데이터 크롤링

: 2017년부터 현재까지 우리나라에 입국한 중국인의 수 크롤링

1. 전체 작업 설계하기

작업 설계사용할 코드
1. 데이터를 수집할 국가코드와 연도 입력하기national_code, nStartYear, nEndYear
2. 데이터 수집 요청하기getTourismStatsService
2.1 url 구성하여 데이터 요청하기getTourismStatsItem
2.2 url 접속하고 요청하기getRequestUrl
2.3 응답 데이터를 리스트로 구성하기jsonResult, result
3. 데이터를 JSON 파일과 CSV 파일로 저장하기json.dumps(), to_csv()

2. 프로그램 구성 설계하기

def main()

1. 수집할 국가 코드와 연도 입력

2. 데이터 수집 요청

3. 파일 저장

3. 함수 설계하기

  1. [CODE 0] 전체 작업 스토리를 구성한다.
01 def main():
02     jsonResult = []
03     result = []
04 
05     print("<< 국내 입국한 외국인의 통계 데이터를 수집합니다. >>")
06     nat_cd = input('국가 코드를 입력하세요(중국: 112 / 일본: 130 / 미국: 275) : ')
07     nStartYear = int(input('데이터를 몇 년부터 수집할까요? : '))
08     nEndYear = int(input('데이터를 몇 년까지 수집할까요? : '))
09     ed_cd = "E"     #E : 방한외래관광객, D : 해외 출국
10 
11     jsonResult, result, natName, ed, dataEND = getTourismStatsService(nat_cd, ed_cd, nStartYear, nEndYear)
12
13     # 파일 저장 1 :json 파일
14     with open('./%s_%s_%d_%s.json' % (natName, ed, nStartYear, dataEND), 'w', encoding='utf8') as outfile:
15         jsonFile = json.dumps(jsonResult, indent = 4, sort_keys = True, ensure_ascii = False)
16        outfile.write(jsonFile)
17     #파일 저장 2 : csv 파일
18     columns = ["입국자국가", "국가코드", "입국연월", "입국자 수"]
19     result_df = pd.DataFrame(result, columns = columns)
20     result_df.to_csv('./%s_%s_%d_%s.csv' % (natName, ed, nStartYear, dataEND), index = False, encoding = 'cp949')

06행 : 데이터를 수집할 국가 코드를 입력받는다.
07행 : 데이터를 수집할 시작 연도를 입력받는다.
08행 : 데이터를 수집할 마지막 연도를 입력받는다.
11행 : getTourismStatsService() 함수를 호출하여 반환받은 수집 데이터를 jsonResult, result, natName, dataEND에 저장한다.
14~16행 : 수집 데이터를 딕셔너리의 리스트로 저장한 jsonResult를 json.dumps()를 통해 json 객체로 변환한 후 JSON 파일에 저장한다.
18행 : 데이터프레임에 만들 컬럼명을 리스트로 만든다.
19헹 : 수집 데이터를 리스트로 저장한 result를 데이터프레임으로 변환한다.
20행 : 데이터프레임 객체인 result_df를 CSV 파일로 저장한다.

  1. [CODE 1] url 접속을 요청하고 응답을 받아서 반환한다.
01 def getRequestUrl(url):
02     req = urllib.request.Request(url)
03     try:
04         response = urllib.request.urlopen(req)
05         if response.getcode() == 200:
06             print("[%s] Url Request Success" % datetime.datetime.now())
07             return response.read().decode('utf-8')
08     except Exception as e:
09         print(e)
10         print("[%s] Error for URL : %s" % (datetime.datetime.now(), url))
11         return None

02행 : 매개변수로 받은 url에 대한 요청을 보낼 객체를 생성한다.
04행 : 요청 객체를 보내서 받은 응답 데이터를 response 객체에 저장한다.
05~07행 : response 객체에 저장된 코드를 확인한다. 코드가 200이면 요청을 정상 처리한 것이므로 성공 메시지와 현재 시간을 파이썬 셸 창에 출력하고 응답을 utf-8 형식으로 디코딩하여 반환한다.
08~11행 : 요청이 처리되지 않은 예외 사항이 발생하면 에러 메시지를 파이썬 셸 창에 출력한다.

  1. [CODE 2] 출입국관광통계서비스의 오픈 API를 사용하여 데이터 요청 url을 만들고 [CODE 1]의 getRequestUrl(url)을 호출해서 받은 응답 데이터를 반환한다.
01 def getTourismStatsItem(yyyymm, national_code, ed_cd):
02     service_url = "http://openapi.tour.go.kr/openapi/service/EdrcntTourismStatsService/getEdrcntTourismStatsList"
03     parameters = "?_type=json&serviceKey=" + ServiceKey   # 인증키
04     parameters += "&YM=" + yyyymm
05     parameters += "&NAT_CD=" + national_code
06     parameters += "&ED_CD=" + ed_cd
07
08     url = service_url + parameters
09     print(url)   # 액세스 거부 여부 확인용 출력
10     responseDecode = getRequestUrl(url)   # [CODE 1]
11
12     if(responseDecode == None):
13         return None
14     else:
15         return json.loads(responseDecode)

02~08행 : 출입국관광통계서비스의 오픈 API 상세정보 페이지에서 찾은 서비스 URL, 요청 매개변수 정보, 발급받은 인증키를 사용하여 데이터 요청 URL을 구성한다.
10행 : 구성한 url로 getRequestUrl() 함수를 호출해서 받은 응답(utf-8로 디코드됨)을 responseDecode에 저장한다.
15행 : 서버에서 받은 JOSN 형태의 응답 객체를 파이썬 객체로 로드하여 반환한다.

  1. [CODE 3] 수집 기간 동안 월 단위로 [CODE 2]의 getTourismStatsItem()을 호출해 받은 데이터를 리스트로 묶어 반환한다.
01 def getTourismStatsService(nat_cd, ed_cd, nStartYear, nEndYear):
02     jsonResult = []
03     result = []
04     natName = ''
05     dataEND = "{0}{1:0>2}".format(str(nEndYear), str(12))
06     isDataEnd = 0
07     for year in range(nStartYear, nEndYear+1):
08         for month in range(1, 13):
09             if(isDataEnd == 1): break
10             yyyymm = "{0}{1:0>2}".format(str(year), str(month))
11             jsonData = getTourismStatsItem(yyyymm, nat_cd, ed_cd)   #[CODE 2]
12             if(jsonData['response']['header']['resultMsg'] == 'OK'):
13                 if jsonData['response']['body']['items'] == '':
14                    isDataEnd = 1
15                    dataEND = "{0}{1:0>2}".format(str(year), str(month-1))
16                    print("데이터 없음.... \n 제공되는 통계 데이터는 %s년 %s월까지입니다." %(str(year), str(month-1)))
17                    break
18                 print(json.dumps(jsonData, indent = 4, sort_keys = True, ensure_ascii = False))
19                 natName = jsonData['response']['body']['items']['item']['natKorNm']
20                 natName = natName.replace(' ', '')
21                 num = jsonData['response']['body']['items']['item']['num']
22                 ed = jsonData['response']['body']['items']['item']['ed']
23                 print(' [%s_%s : %s ]' %(natName, yyyymm, num))
24                 print('---------------------------------------------')
25                 jsonResult.append({'nat_name':natName, 'nat_cd':nat_cd, 'yyyymm':yyyymm, 'visit_cnt':num})
26                 result.append([natName, nat_cd, yyyymm, num])
27     return (jsonResult, result, natName, ed, dataEND)

05행 : 수집할 데이터의 끝 날짜인 dataEND를 nEndYear의 12월로 설정한다.
06행 : 수집한 데이터의 끝인지 확인하기 위한 플래그인 isDataEnd를 0으로 설정한다.
09행 : 데이터 끝 플래그인 isDataEnd를 확인하여 플래그가 설정되어 있으면 작업을 중단한다.
10행 : 수집할 연도와 월을 여섯 자리로 맞추어 yyyymm에 저장한다.
11행 : getTourismStatsItem()을 호출해 받은 월 데이터를 jsonData에 저장한다.
12행 : 응답 데이터가 정상인지 확인한다.
13~17행 : ['items'] 항목에 값이 없으면 출입국관광통계 데이터가 아직 들어가지 않은 월이므로, isDataEnd를 1로 설정한다. 그리고 마지막 데이터가 있는 날짜(month-1)를 dataEND에 저장하고 데이터 수집 작업을 중단한다.
18행 : 수집한 월 데이터인 jsonData 내용을 확인할 수 있게 파이썬 셸 창에 출력한다.
19~20행 : 수집한 국가 이름인 ['natKorNm'] 항목이 값에서 띄어쓰기를 제거하고 natName에 저장한다.
21행 : 수집한 월의 데이터 수인 ['num'] 항목의 값을 num에 저장한다.
22행 : 수집한 출입국 구분 데이터인 ['ed'] 항목의 값을 ed에 저장한다.
25행 : 수집한 국가 이름(natName), 국가 코드(nat_cd), 날짜(yyyymm), 데이터 수(num)를 딕셔너리 자료형으로 구성하여 jsonResult 리스트에 원소로 추가한다.
26행 : 수집한 국가 이름(natName), 국가 코드(nat_cd), 날짜(yyyymm), 데이터 수(num)를 result 리스트에 원소로 추가한다.
27행 : 수집하여 정리한 데이터를 반환한다.

# 전체 프로그램 작성

import os
import sys
import urllib.request
import datetime
import time
import json
# import pandas as pd

ServiceKey = "인증키"


#[CODE 1]
def getRequestUrl(url):
    req = urllib.request.Request(url)
    try:
        response = urllib.request.urlopen(req)
        if response.getcode() == 200:
            print("[%s] Url Request Success" % datetime.datetime.now())
            return response.read().decode('utf-8')
    except Exception as e:
        print(e)
        print("[%s] Error for URL : %s" % (datetime.datetime.now(), url))
        return None

#[CODE 2]
def getTourismStatsItem(yyyymm, national_code, ed_cd):
    service_url = "http://openapi.tour.go.kr/openapi/service/EdrcntTourismStatsService/getEdrcntTourismStatsList"

    parameters = "?_type=json&serviceKey=" + ServiceKey   # 인증키
    parameters += "&YM=" + yyyymm
    parameters += "&NAT_CD=" + national_code
    parameters += "&ED_CD=" + ed_cd

    url = service_url + parameters
    print(url)   # 액세스 거부 여부 확인용 출력
    responseDecode = getRequestUrl(url)   # [CODE 1]

    if(responseDecode == None):
        return None
    else:
        return json.loads(responseDecode)

#[CODE 3]
def getTourismStatsService(nat_cd, ed_cd, nStartYear, nEndYear):
    jsonResult = []
    result = []
    natName = ''
    dataEND = "{0}{1:0>2}".format(str(nEndYear), str(12))
    isDataEnd = 0
    for year in range(nStartYear, nEndYear+1):
        for month in range(1, 13):
            if(isDataEnd == 1): break
            yyyymm = "{0}{1:0>2}".format(str(year), str(month))
            jsonData = getTourismStatsItem(yyyymm, nat_cd, ed_cd)   #[CODE 2]
            if(jsonData['response']['header']['resultMsg'] == 'OK'):
                if jsonData['response']['body']['items'] == '':
                   isDataEnd = 1
                   dataEND = "{0}{1:0>2}".format(str(year), str(month-1))
                   print("데이터 없음.... \n 제공되는 통계 데이터는 %s년 %s월까지입니다." %(str(year), str(month-1)))
                   break
                print(json.dumps(jsonData, indent = 4, sort_keys = True, ensure_ascii = False))
                natName = jsonData['response']['body']['items']['item']['natKorNm']
                natName = natName.replace(' ', '')
                num = jsonData['response']['body']['items']['item']['num']
                ed = jsonData['response']['body']['items']['item']['ed']
                print(' [%s_%s : %s ]' %(natName, yyyymm, num))
                print('---------------------------------------------')
                jsonResult.append({'nat_name':natName, 'nat_cd':nat_cd, 'yyyymm':yyyymm, 'visit_cnt':num})
                result.append([natName, nat_cd, yyyymm, num])
    return (jsonResult, result, natName, ed, dataEND)

#[CODE 0] 
def main():
    jsonResult = []
    result = []

    print("<< 국내 입국한 외국인의 통계 데이터를 수집합니다. >>")
    nat_cd = input('국가 코드를 입력하세요(중국: 112 / 일본: 130 / 미국: 275) : ')
    nStartYear = int(input('데이터를 몇 년부터 수집할까요? : '))
    nEndYear = int(input('데이터를 몇 년까지 수집할까요? : '))
    ed_cd = "E"     #E : 방한외래관광객, D : 해외 출국
    jsonResult,result,natName,ed,dataEND = getTourismStatsService(nat_cd, ed_cd, nStartYear, nEndYear)

    # 파일 저장 1 :json 파일
    with open('./%s_%s_%d_%s.json' % (natName, ed, nStartYear, dataEND), 'w',
              encoding='utf8') as outfile:
        jsonFile = json.dumps(jsonResult, indent = 4, sort_keys = True, ensure_ascii = False)
        outfile.write(jsonFile)
    #파일 저장 2 : csv 파일
    columns = ["입국자국가", "국가코드", "입국연월", "입국자 수"]
    result_df = pd.DataFrame(result, columns = columns)
    result_df.to_csv('./%s_%s_%d_%s.csv' % (natName, ed, nStartYear, dataEND),
                     index = False, encoding = 'cp949')

if __name__ == '__main__':
    main()
    

-> 파일을 실행시키면 파이썬 셸 창에 값을 입력하라는 메시지가 나타난다.
국가 코드는 '112', 데이터 수집 연도는 '2017', 데이터 수집 마지막 연도는 '2021'을 각각 입력한다.
실행이 끝나면 JSON 파일과 CSV 파일이 생성된다.

※ 실행 결과에서 JsonData 내용 없이 Url Request Success만 출력되고 마지막에 UnboundLocalError가 출력되었다.
-> 이와 같은 경우는 액세스가 거부되어 응답 데이터를 받지 못한 상태이며, 액세스 거부는 인증키 승인이 아직 되지 않았거나 하루 사용량을 초과한 경우에 발생한다고 한다.


.json 파일과 .csv 파일 모두 제대로 생성된 것을 볼 수 있다.

0개의 댓글