: 2017년부터 현재까지 우리나라에 입국한 중국인의 수 크롤링
작업 설계 | 사용할 코드 |
---|---|
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() |
def main()
1. 수집할 국가 코드와 연도 입력
2. 데이터 수집 요청
3. 파일 저장
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 파일로 저장한다.
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행 : 요청이 처리되지 않은 예외 사항이 발생하면 에러 메시지를 파이썬 셸 창에 출력한다.
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 형태의 응답 객체를 파이썬 객체로 로드하여 반환한다.
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 파일 모두 제대로 생성된 것을 볼 수 있다.