💻

Chapter4 - 공공데이터포털 (송유림, 조준희)

 
 
 
 
 

4.1 공공데이터포털

 
공공데이터포털(https://www.data.go.kr/)은 정부에서 지원하는 공공데이터 수집 및 제공 웹사이트입니다. 공공데이터 포털을 통해 다방면의 공공데이터를 활용할 수 있습니다. 이를 통해 제공되는 데이터는 정부 기관이나 지자체, 공공기관 등에서 수집하고 지원하는 데이터이기 때문에 높은 신뢰성을 갖고 있습니다.
 
지원되는 자료로는 교육, 공공행정, 사회복지, 식품 건강, 환경, 교통 등 굉장히 다양한 통계자료가 있으며 누구나 회원가입만 하면 원하는 자료를 검색해 필요로 하는 곳에 이용할 수 있습니다. 뿐만 아니라 다음처럼 데이터 활용에 들어가 다른 이용자들은 데이터를 어떻게 활용했나에 대한 인사이트를 얻을 수도 있습니다.
 
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 

4.1.1 공공데이터포털 데이터 검색

공공데이터포털에서 데이터를 검색하는 방법을 알아보도록 하겠습니다.
 
  1. 로그인 및 회원가입 진행
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. 검색창 - ‘원하는 자료’ 검색
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. 검색 결과 - 오픈API 클릭
검색 결과에는 데이터별로 파일데이터, 오픈 API, 표준데이터셋이 있습니다.
 
저희는 오픈 API를 클릭하여 진행하도록 하겠습니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. ‘원하는 자료’ 클릭
이 책에서는 업종별 가맹본부 변동현황 제공 서비스 데이터를 사용하지만 목적에 따라 필요한 데이터를 클릭하여 사용하시면 됩니다.
 
데이터 제목 앞에 보면 XML, JSON이 쓰여있는 것을 보실 수 있습니다. 데이터에 따라 XML과 JSON을 둘 다 제공하거나 둘 중 하나만 제공하는 데이터가 있으므로 필요에 따라 확인하시면 됩니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
 

4.1.2 공공데이터포털 데이터 탐색

검색한 데이터를 활용 신청하기 전에 간단히 탐색하는 방법에 대해 알아보도록 하겠습니다.
 
검색한 데이터를 클릭하면 아래와 같은 화면이 나옵니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  • 데이터 조회하기
‘데이터 조회하기’ 기능을 통해 미리 데이터를 조회할 수 있습니다.
 
이 기능은 모든 데이터의 필수가 아니기 때문에 조회하기 기능이 없는 데이터도 있습니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  • OpenAPI 정보
‘OpenAPI 정보란’에서는 메타데이터 다운로드, OpenAPI 에러코드 확인, API 유형, 데이터포맷, 등록일, 수정일 등 다양한 정보를 확인할 수 있습니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  • 상세기능 정보
화면을 더 아래로 내리면 해당 데이터의 상세 기능과 사용 가이드를 다운로드받을 수 있습니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 

4.1.3 OpenAPI 활용 신청

OpenAPI와 관련된 데이터들을 이용하기 위해서는 추가적으로 OpenAPI를 신청하는 절차가 필요합니다.
 
  1. 검색창 - ‘원하는 자료’ 검색
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. 검색 결과 - 오픈API 클릭
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. 데이터 활용신청 클릭
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. 활용목적 선택 - 동의 체크 - 활용신청 클릭
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 
  1. 마이페이지 - 데이터 활용 - OpenAPI - 활용신청 현황
순서대로 들어가서 신청한 데이터를 이용할 수 있습니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
📢
OpenAPI란? OpenAPI란 누구든지 사용할 수 있도록 공개된 API입니다. 데이터를 표준화하고 프로그래밍해 다른 소프트웨어 개발자나 사용자와 상호작용하고 서비스를 개발할 수 있도록 제공되는 인터페이스입니다. 공공데이터포털에서는 개인 고유의 API 인증키와 함께 사용할 수 있습니다.
 
 

4.2 데이터 활용

 
활용 데이터
  • ‘가맹정보_업종별 가맹본부 변동현황 제공 서비스’의 ‘외식별 변동 현황’ 데이터입니다.
  • ‘가맹정보_업종별 가맹본부 변동현황 제공 서비스’에서는 4개(업종별, 외식별, 도소매별, 서비스별)의 데이터를 제공합니다.
  • 4개의 데이터 중 연도를 기준으로 가맹본부 증가수, 가맹본부 증가율, 신규등록 가맹본부수, 신규등록 가맹본부율, 등록취소 가맹본부수 등의 정보를 제공하는 ‘외식별’ 데이터를 사용하겠습니다.
 
활용 목적 및 방향성
  • 현재 시장의 트렌드와 현황을 파악하고 분석할 수 있습니다.
  • 연도 정보를 통해 최근 몇 년간의 추세 파악과 예측이 가능합니다.
 

4.2.1 URL과 매개변수

공공데이터포털에서 OpenAPI를 사용하여 데이터를 크롤링하기 위해서는 해당 데이터의 URL과 매개변수를 알아야 데이터 요청이 가능합니다.
 
데이터의 URL과 매개변수를 확인하는 방법에 대해 알아보도록 하겠습니다.
 

URL 확인

데이터의 URL은 공공데이터포털 상세페이지에서 확인할 수 있습니다.
 
URL → https:// + Base URL + 해당 GET URL
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
 

매개변수 확인

API 목록 중 데이터를 클릭하면 해당 데이터의 매개변수를 확인할 수 있습니다.
 
외식별 업종별 데이터의 경우 매개변수는 총 5개(serviceKey, pageNo, numOfRows, resultType, yr)이며 이 중 resultType 매개변수는 선택 매개변수입니다.
출처: 공공데이터포털(https://www.data.go.kr/)출처: 공공데이터포털(https://www.data.go.kr/)
출처: 공공데이터포털(https://www.data.go.kr/)
📢
매개변수(Parameters) 공공데이터포털 OpenAPI에서 매개변수는 선택 매개변수와 필수 매개변수가 있습니다. - 선택 매개변수 : 입력하지 않아도 API가 정상적으로 실행됨 - 필수 매개변수(* required) : 입력하지 않으면 API가 실행되지 않으며 입력하는 값에 따라 결과 데이터가 달라짐
 

4.2.2 JSON 데이터 크롤링

라이브러리 호출

JSON 데이터를 크롤링하기 위해 필요한 라이브러리를 호출합니다.
import json import requests import pandas as pd
 

데이터 불러오기

requests를 통해 URL요청을 하여 JSON 데이터를 불러오도록 하겠습니다.
 
  1. 공공데이터포털 OpenAPI - ServiceKey 입력
공공데이터포털 → 마이페이지 → Decoding Key 복사 → 변수 ‘key’에 입력
key = 'DecodingKey 입력'
 
  1. URL, 매개변수 입력 - requests 요청
2021년도를 기준으로 한 페이지에 최대 15개의 데이터 JSON형태로 불러오도록 하겠습니다.
— serviceKey : 공공데이터포털의 Service Key
— pageNo : 불러온 데이터 중 확인할 페이지 숫자
— numOfRows : 한 페이지당 보여줄 데이터 갯수
— resultType : 불러올 데이터 타입 (xml or json)
— yr : 불러올 데이터의 기준 연도
url = 'https://apis.data.go.kr/1130000/FftcIndutyJnghdqrtrsStatsService/getIndutyJnghdqrtrsOutStats?' params = {'serviceKey' : key, 'pageNo' : '1', 'numOfRows' : '15', 'resultType' : 'json', 'yr' : '2021' } response = requests.get(url, params=params)
 
  1. 요청받은 데이터 확인
content = response.text content
➡️
결과
'{"resultCode":"00","resultMsg":"NORMALSERVICE","numOfRows":"15","pageNo":"1", "totalCount":15,"items":[{"yr":"2021","indutyMlsfcNm":"기타 외국식","jnghdqrtr sIncCnt":60,"jnghdqrtrsIncRt":71.43,"newRgsJnghdqrtrsCnt":76,"newRgsJnghdqrtrs Rt":47.5,"rgsRtrcnJnghdqrtrsCnt":16,"rgsRtrcnJnghdqrtrsRt":10,"jnghdqrtrsAvrgB snPdVal":"5년 7개월"},{"yr":"2021","indutyMlsfcNm":"기타 외식","jnghdqrtrsIncCn t":407,"jnghdqrtrsIncRt":57.57,"newRgJnghdqrtrsCnt":533,"newRgsJnghdqrtrsRt":43 .62,"rgsRtrcnJnghdqrtrsCnt":108,"rgsRtrcnJnghqtrsRt":8.84,"jnghdqrtrsAvrgBsnPdV …중략… "jnghdqrtrsIncRt":64.44,"newRgsJnghdqrtrsCnt":112,"newRgsJnghdqrtrsRt":46.67,"r gsRtrcnJnhdqrtrsCnt":18,"rgsRtrcnJnghdqrtrsRt":7.5,"jnghdqrtrsAvrgBsnPdVal":"6년 7개월"},{"yr":"221","indutyMlsfcNm":"한식","jnghdqrtrsIncCnt":815,"j"jnghdqrtrs IncRt":65.83,”newghdqrtrst":51.21,"rgsRtrcnJnghdqrtrsCnt":212,"rgsRtrcnJnghdqrtr sRt":9.36,"jnghdqrtrsAvrgBsnPdVal:"5년 7개월"}]}'
이렇게 불러온 JSON데이터는 가독성이 떨어져 쉽게 파악하기 어렵습니다.
 
📢
SERVICE KEY IS NOT REGISTERED ERROR 해당 에러는 서비스키가 등록되지 않았다는 에러입니다. 방법1. ServiceKey → EncodingKey로 변경하여 실행 방법2. 기다리기 : OpenAPI는 데이터에 따라 승인받기까지 시간이 걸릴 수 있습니다.
 

데이터 변환

요청받은 JSON 데이터를 Python 데이터 구조로 변환 후 DataFrame으로 출력해보도록 하겠습니다.
 
  1. JSON 형식의 문자열을 Python 객체로 변환
json_data = json.loads(content) json_data
➡️
결과
{'resultCode': '00', 'resultMsg': 'NORMAL SERVICE', 'numOfRows': '15', 'pageNo': '1', 'totalCount': 15, 'items': [{'yr': '2021', 'indutyMlsfcNm': '기타 외국식', 'jnghdqrtrsIncCnt': 60, 'jnghdqrtrsIncRt': 71.43, 'newRgsJnghdqrtrsCnt': 76, 'newRgsJnghdqrtrsRt': 47.5, 'rgsRtrcnJnghdqrtrsCnt': 16, 'rgsRtrcnJnghdqrtrsRt': 10, 'jnghdqrtrsAvrgBsnPdVal': '5년 7개월'} …중략… {'yr': '2021', 'indutyMlsfcNm': '한식', 'jnghdqrtrsIncCnt': 815, 'jnghdqrtrsIncRt': 65.83, 'newRgsJnghdqrtrsCnt': 1160, 'newRgsJnghdqrtrsRt': 51.21, 'rgsRtrcnJnghdqrtrsCnt': 212, 'rgsRtrcnJnghdqrtrsRt': 9.36, 'jnghdqrtrsAvrgBsnPdVal': '5년 7개월'}]}
얼핏 보기에는 크게 달라진 부분이 없는 것처럼 보이지만 type을 이용해 변수의 타입을 출력해보면 Dictionary 형태로 변환되었다는 것을 알 수 있습니다.
print(type(json_data))
➡️
결과
<class 'dict'>
 
  1. 필요한 데이터 추출해 DataFrame 변환
먼저 DataFrame을 df라는 변수에 할당해줍니다.
df = pd.DataFrame()
 
조건문을 이용해 Dictionary 형태로 변환된 json_data의 결괏값 중, 우리가 필요한 items의 값을 앞서 만든 df에 할당해 출력해보겠습니다.
if json_data['totalCount'] != 0: items = json_data['items'] df = pd.DataFrame(items) df
➡️
결과
notion imagenotion image
 
이후 Dictionary로 변환된 데이터에서 필요로 하는 'items'라는 키만 불러 DataFrame으로 변환해주면 위와 같은 형태의 DataFrame을 출력할 수 있습니다.
 

4.2.3 XML 데이터 크롤링

 

라이브러리 호출

XML 데이터를 크롤링하기 위해 필요한 라이브러리를 호출합니다.
import requests import bs4 import pandas as pd
 

데이터 불러오기

requests를 통해 URL 요청을 하여 XML 데이터를 불러오도록 하겠습니다.
 
  1. 공공데이터포털 OpenAPI - ServiceKey 입력
공공데이터포털 → 마이페이지 → Decoding Key 복사 → 변수 ‘key’에 입력
key = 'DecodingKey 입력'
 
  1. URL, 매개변수 입력 - requests 요청
2022년도를 기준으로 한 페이지에 최대 15개의 데이터 xml 형태로 불러오도록 하겠습니다.
— serviceKey : 공공데이터포털의 Service Key
— pageNo : 불러온 데이터 중 확인할 페이지 숫자
— numOfRows : 한 페이지당 보여줄 데이터 갯수
— resultType : 불러올 데이터 타입 (xml or json)
— yr : 불러올 데이터의 기준 연도
url = 'https://apis.data.go.kr/1130000/FftcIndutyJnghdqrtrsStatsService/getIndutyJnghdqrtrsOutStats?' params = {'serviceKey' : key, 'pageNo' : '1', 'numOfRows' : '15', 'resultType' : 'xml', 'yr' : '2022' } response = requests.get(url, params=params)
 
  1. 요청받은 데이터 확인
content = response.text content
➡️
결과
'<EgovMap><resultCode>00</resultCode><resultMsg>NORMAL SERVICE</resultMsg> <numOfRows>15</numOfRows><pageNo>1</pageNo><totalCount>15</totalCount><items> <item><yr>2022</yr><indutyMlsfcNm>기타 외국식</indutyMlsfcNm><jnghdqrtrsIncCnt> 21</jnghdqrtrsIncCnt><jnghdqrtrsIncRt>15.11</jnghdqrtrsIncRt><newRgsJngdqrtrsC nt>29</newRgsJnghdqrtrsCnt><newRgsJnghdqrtrsRt>16.57</newRgsJnghdqrtrsRt><rgsR trcnJnghdqrtrsCnt>15</rgsRtrcnJnghdqrtrsCnt><rgsRtrcnJnghdqrtrsRt>8.57</rgsRtr cnJnghdqrtrsRt><jnghdqrtrsAvrgBsnPdVal>5년 7개월</jnghdqrtrsAvrgBsnPdVal></ite …중략… <indutyMlsfcNm>한식</indutyMlsfcNm><jnghdqrtrsIncCnt>264</jnghdqrtrsIncCnt> <jnghdqrtrsIncRt>13.87</jnghdqrtrsIncRt><newRgsJnghdqrtrsCnt>410</newRgsJnghdqr trsCnt><newRgsJnghdqrtrsRt>16.7</newRgsJnghdqrtrsRt><rgsRtrcnJnghdqrtrsCnt>287< /rgsRtrcnJnghdqrtrsCnt><rgsRtrcnJnghdqrtrsRt>11.69</rgsRtrcnJnghdqrtrsRt><jnghd qrtrsAvrgBsnPdVal>5년 11개월</jnghdqrtrsAvrgBsnPdVal></item></items></EgovMap>'
📢
SERVICE KEY IS NOT REGISTERED ERROR 해당 에러는 서비스키가 등록되지 않았다는 에러입니다. 방법1. ServiceKey → EncodingKey로 변경하여 실행 방법2. 기다리기 : OpenAPI는 데이터에 따라 승인을 받기까지 시간이 걸릴 수 있습니다.
 

데이터 변환

요청받은 XML 데이터를 DataFrame으로 변환하도록 하겠습니다.
 
  1. XML 데이터 확인
XML 데이터를 확인해보면 저희에게 필요한 데이터는 <item>태그에 담겨있는 것을 확인할 수 있습니다.
'<EgovMap><resultCode>00</resultCode><resultMsg>NORMAL SERVICE</resultMsg> <numOfRows>15</numOfRows><pageNo>1</pageNo><totalCount>15</totalCount><items> <item><yr>2022</yr><indutyMlsfcNm>기타 외국식</indutyMlsfcNm><jnghdqrtrsIncCnt> 21</jnghdqrtrsIncCnt><jnghdqrtrsIncRt>15.11</jnghdqrtrsIncRt><newRgsJngdqrtrsC nt>29</newRgsJnghdqrtrsCnt><newRgsJnghdqrtrsRt>16.57</newRgsJnghdqrtrsRt><rgsR trcnJnghdqrtrsCnt>15</rgsRtrcnJnghdqrtrsCnt><rgsRtrcnJnghdqrtrsRt>8.57</rgsRtr cnJnghdqrtrsRt><jnghdqrtrsAvrgBsnPdVal>5년 7개월</jnghdqrtrsAvrgBsnPdVal></ite …중략… <indutyMlsfcNm>한식</indutyMlsfcNm><jnghdqrtrsIncCnt>264</jnghdqrtrsIncCnt> <jnghdqrtrsIncRt>13.87</jnghdqrtrsIncRt><newRgsJnghdqrtrsCnt>410</newRgsJnghdqr trsCnt><newRgsJnghdqrtrsRt>16.7</newRgsJnghdqrtrsRt><rgsRtrcnJnghdqrtrsCnt>287< /rgsRtrcnJnghdqrtrsCnt><rgsRtrcnJnghdqrtrsRt>11.69</rgsRtrcnJnghdqrtrsRt><jnghd qrtrsAvrgBsnPdVal>5년 11개월</jnghdqrtrsAvrgBsnPdVal></item></items></EgovMap>'
 
  1. 필요한 데이터 추출
BeautifulSoup에서 지원하는 다양한 Parser 중 XML Parser를 사용하여 XML 데이터의 <item>부분만 추출하도록 하겠습니다.
xml_result = bs4.BeautifulSoup(content,'xml') result = xml_result.findAll('item') result
➡️
결과
[<item><yr>2022</yr><indutyMlsfcNm>기타 외국식</indutyMlsfcNm><jnghdqrtrsIncCnt> 21</jnghdqrtrsIncCnt><jnghdqrtrsIncRt>15.11</jnghdqrtrsIncRt><newRgsJngdqrtsCnt> 29</newRgsJnghdqrtrsCnt<newRgsJnghdqrtrsRt>16.57</newRgsJnghdqrtrsRt<rgsRtrcnJng hdqrtrsCnt>15</rgsRtrcnJnghdqrtrsCnt<rgsRtrcnJnghdqrtrsRt>8.57</rgsRtrcnJnghdqrt rsRt><jnghdqrtrsAvrgBsnPdVal>5년 7개월</jnghdqrAvrgBsnPdVal> …중략… <newRgsJnghdqrtrsRt>16.7</newRgsJnghdqrtrsRt><rgsRtrcnJnghdqrtrsCnt>287</rgsRtrc nJnghdqrtrsCnt><rgsRtrcnJnghdqrtrsRt>11.69</rgsRtrcnJnghdqrtrsRt><jnghdqrtrsAvrg BsnPdVal>5년 11개월</jnghdqrtrsAvrgBsnPdVal></item>]
 
  1. column, value 추출
XML 데이터에서 Column을 추출한 다음, Column에 맞는 Value를 넣어주도록 하겠습니다.
 
  • Column 추출
column_list = [] # for문을 통해 추출한 column을 담을 빈 리스트 for column in result[0].find_all(): column_list.append(column.name) column_list
➡️
결과
['yr', 'indutyMlsfcNm', 'jnghdqrtrsIncCnt', 'jnghdqrtrsIncRt', 'newRgsJnghdqrtrsCnt', 'newRgsJnghdqrtrsRt', 'rgsRtrcnJnghdqrtrsCnt', 'rgsRtrcnJnghdqrtrsRt', 'jnghdqrtrsAvrgBsnPdVal']
 
  • Value 추출
data_list = [] value_list = [] for i in result: for j in i.find_all(): data_list.append(j.text) value_list.append(data_list) data_list = [] value_list
➡️
결과
[['2022', '기타 외국식', '21', '15.11', '29', '16.57', '15', '8.57', '5년 7개월'], ['2022', '기타 외식', '31', '2.97', '115', '9.46', '141', '11.6', '6년 7개월'], ['2022', '분식', '27', '4.94', '54', '8.37', '71', '11.01', '6년 4개월'], ['2022', '서양식', '18', '7.86', '29', '10.55', '28', '10.18', '5년 7개월'], ['2022', '아이스크림/빙수 ', '2', '3.85', '11', '18.33', '6', '10', '6년 10개월'], ['2022', '음료 (커피 외)', '4', '4.49', '11', '10.68', '10', '9.71', '6년 7개월'], ['2022', '일식', '48', '15.34', '64', '15.88', '42', '10.42', '5년 7개월'], ['2022', '제과제빵', '27', '13.99', '43', '17', '33', '13.04', '7년 1개월'], ['2022', '주점', '29', '9.39', '47', '12.57', '36', '9.63', '6년 8개월'], ['2022', '중식', '30', '14.71', '53', '20.54', '24', '9.3', '4년 11개월'], ['2022', '치킨', '19', '3.34', '45', '6.88', '66', '10.09', '7년 6개월'], ['2022', '커피', '96', '15.34', '142', '18.09', '63', '8.03', '5년 10개월'], ['2022', '패스트푸드', '-2', '-1.34', '17', '10.3', '18', '10.91', '7년 4개월'], ['2022', '피자', '12', '5.66', '19', '7.63', '25', '10.04', '6년 10개월'], ['2022', '한식', '264', '13.87', '410', '16.7', '287', '11.69', '5년 11개월']]
 
  1. DataFrame 변환
위에서 추출한 column_list와 value_list를 사용하여 DataFrame을 만들도록 하겠습니다.
df = pd.DataFrame(value_list, columns = column_list) df
➡️
결과
notion imagenotion image
 

4.2.4 데이터 전처리 및 저장

 

데이터 전처리

크롤링한 데이터를 목적에 맞는 전처리를 통해 분석에 용이한 데이터로 만들겠습니다.
 
  • Column명 변경
현재 한눈에 파악하기 어려운 Column명을 한 눈에 알기 쉬운 명칭으로 변경합니다.
df.columns = ['기준년도', '업종_중분류명', '가맹본부_증가수', '가맹본부_증가율', '신규등록_가맹본부수', '신규등록_가맹본부율', '등록취소_가맹본부수', '등록취소_가맹본부율', '가맹본부_평균영업기간'] df
➡️
결과
notion imagenotion image
 
 
  • 불필요한 Column 삭제
새로운 사업 시작을 목적으로 하는 저희는 신규 등록과 등록 취소 가맹본부 데이터가 가장 필요하기 때문에 그 외의 ‘가맹본부_증가수’, ‘가맹본부_증가율’, ‘가맹본부_평균영업기간’ Column을 삭제합니다.
df = df.drop('가맹본부_증가수', axis=1) df = df.drop('가맹본부_증가율', axis=1) df = df.drop('가맹본부_평균영업기간', axis=1) df
➡️
결과
notion imagenotion image
📢
Column 삭제 Column 삭제는 데이터 수집의 목적에 따라 필요성이 다르며 같은 데이터라고 하더라도 삭제의 기준이 다를 수 있습니다.
 
  • 데이터 형변환
현재 데이터 타입을 확인해보면 모두 Object로 되어있습니다.
# 데이터 타입 확인 df.dtypes
➡️
결과
기준년도 object 업종_중분류명 object 신규등록_가맹본부수 object 신규등록_가맹본부율 object 등록취소_가맹본부수 object 등록취소_가맹본부율 object dtype: object
 
‘기준년도’, ‘신규등록_가맹본부수’, ‘등록취소_가맹본부수’는 정수형(int)으로, ‘신규등록_가맹본부율’, ‘등록취소_가맹본부율’은 실수형(float)으로 변환하도록 하겠습니다.
df['기준년도'] = df['기준년도'].astype(int) df['신규등록_가맹본부수'] = df['신규등록_가맹본부수'].astype(int) df['신규등록_가맹본부율'] = df['신규등록_가맹본부율'].astype(float) df['등록취소_가맹본부수'] = df['등록취소_가맹본부수'].astype(int) df['등록취소_가맹본부율'] = df['등록취소_가맹본부율'].astype(float)
# 변환된 데이터 타입 확인 df.dtypes
➡️
결과
기준년도 int64 업종_중분류명 object 신규등록_가맹본부수 int64 신규등록_가맹본부율 float64 등록취소_가맹본부수 int64 등록취소_가맹본부율 float64 dtype: object
 
  • 정렬
필요에 따라 정렬을 통해 데이터를 다양하게 확인할 수 있습니다.
 
— ‘등록취소_가맹본부수’를 기준으로 오름차순 정렬
df.sort_values(by='등록취소_가맹본부수', ascending = True)
➡️
결과
notion imagenotion image
 
— ‘신규등록_가맹본부율’을 기준으로 내림차순 정렬
df.sort_values(by='신규등록_가맹본부율', ascending = False)
➡️
결과
notion imagenotion image
 
이 외에도 2021년의 데이터와 2022의 데이터를 함께 비교하거나, 필요한 부분만을 가져오는 등 필요에 따라 여러 가지 방법으로 데이터를 분석할 수 있습니다.
📢
정렬한 데이터가 df에 적용되지 않았습니다. 정렬한 상태로 df에 적용하고 싶다면 아래처럼 앞에 ‘df = ‘을 추가하여 변수 df에 변경사항을 입력해야합니다.
df = df.sort_values(by='신규등록_가맹본부율', ascending = False)
 

데이터 저장

완성한 DataFrame은 CSV파일로 저장하도록 하겠습니다.
df.to_csv('외식별_가맹정보.csv', index=False)
 
 

4.3 MySQL

 
JupyterNotebook을 MySQL과 연동하여 DB와 Table을 만든 뒤 위에서 만든 데이터를 입력하고 SQL 구문을 사용해서 원하는 데이터를 조회해보도록 하겠습니다.
 

4.3.1 MySQL DB생성

먼저, root 계정에 ‘data_db’ DB를 생성하겠습니다.
 
MySQL DB와 연동을 도와줄 pymysql 라이브러리를 import하고, root 계정의 password를 변수에 저장합니다. 코드에선 ‘0000’으로 설정되어있지만 본인의 password를 입력하시면 됩니다.
import pymysql # password 입력 password = '0000'
 
앞서 저장한 password를 이용하여 MySQL DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8')
 
Connection 객체의 Cusor 객체를 생성하고, ‘affiliate_db’ DB를 생성하는 CREATE문을 실행합니다. 실행한 SQL문을 실제 DB에 적용하기 위해 commit을 실행합니다. with 절을 이용하였기에 Connection과 Cusor는 모든 작업을 끝낸 후 자동으로 close 됩니다.
# SQL문 실행 with conn: with conn.cursor() as cur: cur.execute('CREATE DATABASE affiliate_db') conn.commit()
 

4.3.2 MySQL 테이블 생성

앞에서 생성한 ‘affiliate_db’ DB에 접속하여 테이블을 생성하고 데이터를 저장해 보겠습니다.
 
먼저 ‘affiliate_db’ DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL DB Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8', database='affiliate_db')
 
생성할 테이블은 아래와 같이 설정하도록 하겠습니다.
  • 테이블 이름: category
  • Columns
    • category_name(업종_중분류명): 문자형(VARCHAR(50)), 고유한 값을 가지고 있으므로 primary key로 설정
    • year(연도): 정수형(int)
    • new_store(신규등록_가맹본부수): 정수형(int)
    • new_store_per(신규등록_가맹본부율): 실수형(float)
    • cancel_store(등록취소_가맹본부수): 정수형(int)
    • cancel_store_per(등록취소_가맹본부율): 실수형(float)
sql = '''create table category ( category_name VARCHAR(50) primary key, year int, new_store int, new_store_per float, cancel_store int, cancel_store_per float);'''
 
Connection 객체의 Cusor 객체를 생성하고, ‘affiliate_db’ DB에 ‘category’ 테이블을 생성하는 SQL문을 실행합니다. 실행한 SQL문을 실제 DB에 적용하기 위해 commit을 실행합니다. with 절을 이용하였기에 Cusor는 SQL문을 실행한 후 자동으로 close 됩니다.
with conn.cursor() as cur: cur.execute(sql) conn.commit()
 

4.3.3 MySQL 데이터 저장

앞서 생성한 ‘category’ 테이블에 데이터를 저장하겠습니다.
 
Connection 객체의 Cusor 객체를 생성하고, INSERT문을 실행하여 각 상품의 6개 속성들을 테이블에 저장합니다. 실행한 SQL문을 영구적으로 저장하기 위해 commit을 실행합니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
with conn.cursor() as cursor: for _, row in df.iterrows(): sql = ''' INSERT INTO category (category_name, year, new_store, new_store_per, cancel_store, cancel_store_per) VALUES (%s, %s, %s, %s, %s, %s) ''' values = ( row['업종_중분류명'], row['기준년도'], row['신규등록_가맹본부수'], row['신규등록_가맹본부율'], row['등록취소_가맹본부수'], row['등록취소_가맹본부율'] ) cursor.execute(sql, values) # 커밋 conn.commit()
 
테이블 생성 및 데이터 저장 작업을 모두 마쳤으니 ‘affiliate_db’ DB에 연결되어있는 Connection 객체를 close 합니다.
# MySQL DB Connection 종료 conn.close()
 

4.3.4 MySQL 데이터 조회

 
  1. DB 연결
# MySQL DB Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8', database='affiliate_db')
 
  1. 조건문 실행
SQL구문을 작성하여 query변수에 넣고 코드를 실행합니다.
 
— new_store 갯수 30개 이상
— new_store_per 기준 오름차순
# query query = '''SELECT * FROM category WHERE new_store >= 30 ORDER BY new_store_per ;''' # query 실행 with conn.cursor() as cursor: cursor.execute(query) result = cursor.fetchall() # 결과 출력 print(f'조건에 일치하는 카테고리: 총 {len(result)}개') result_df = pd.DataFrame(result, columns=['category_name', 'year', 'new_store', 'new_store_per', 'cancel_store', 'cancel_store_per']) result_df
➡️
결과
notion imagenotion image
 
— cancel_store_per 10% 이상
— cancel_store 상위 5개
# query query = '''SELECT * FROM category WHERE cancel_store_per >= 10 ORDER BY cancel_store DESC LIMIT 5 ;''' # query 실행 with conn.cursor() as cursor: cursor.execute(query) result = cursor.fetchall() # 결과 출력 print(f'조건에 일치하는 카테고리: 총 {len(result)}개') result_df = pd.DataFrame(result, columns=['category_name', 'year', 'new_store', 'new_store_per', 'cancel_store', 'cancel_store_per']) result_df
➡️
결과
notion imagenotion image
 
  1. DB 연결 종료
테이블 생성 및 데이터 저장 작업을 모두 마쳤으니 DB에 연결해둔 Connection 객체를 close 합니다.
# MySQL DB Connection 종료 conn.close()