Learn & Record

Python (공공데이터활용, 서버-데이터 수신 방식, JSON, openweathermap, 공공데이터포털, JSON과 API, API 이용하기, 공공데이터 활용하기) 본문

Dev/Python

Python (공공데이터활용, 서버-데이터 수신 방식, JSON, openweathermap, 공공데이터포털, JSON과 API, API 이용하기, 공공데이터 활용하기)

Walker_ 2024. 3. 5. 17:38

1. 공공데이터활용

import csv
from typing import List

# example_file = open('./input/아파트(매매)_실거래가_20240304154554.csv')
# example_reader = csv.DictReader(example_file)
# print('출력')
# for row in example_reader:
#     print(f'{row}')
# print('=' * 20)

# 조건 : 래미안 단지만 검색
# 출력조건: 지역, 단지명, 크기, 층수, 거래금액
# 저장 파일명 : apt_2403_조건1.csv
# with로 파일 처리

new_datas: List[dict] = list()
with open('./input/아파트(매매)_실거래가_20240304154554.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        row: dict
        if row['단지명'].find('래미안') != -1:
            print(f'{row["단지명"]}')
            new_data: dict = dict()
            new_data['시군구'] = row['시군구']
            new_data['단지명'] = row['단지명']
            new_data['전용면적(㎡)'] = row['전용면적(㎡)']
            new_data['층'] = row['층']
            new_data['거래금액(만원)'] = row['거래금액(만원)']
            new_datas.append(new_data)
print(new_datas)
header: list = list(new_datas[0].keys())

file_name = 'apt_2403_조건1.csv'
with open(f'./output/{file_name}', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=header)
    writer.writeheader()
    for data in new_datas:
        writer.writerow(data)
print(f'{file_name}파일이 생성 되었습니다.')


# 조건 : 롯데캐슬 거래건만 검색
# 출력조건 : 구(구만 나오도록 할 것), 단지명, 전용면적, 층, 거래금액
# 저장 파일명 : apt_2403_조건3.csv
# with로 파일 처리
# 예) 달서구, 롯데캐슬레이크, 84, 91, 3, "32.109"

new_datas: List[dict] = list()
with open('./input/아파트(매매)_실거래가_20240304154554.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        row: dict
        if row['단지명'].find('롯데캐슬') >= 0:
            print(f'{row["시군구"].split(" ")[1]} / {row["단지명"]}')
            new_data: dict = dict()
            new_data['구'] = row['시군구'].split(" ")[1]
            new_data['시군구'] = row['시군구']
            new_data['단지명'] = row['단지명']
            new_data['전용면적(㎡)'] = row['전용면적(㎡)']
            new_data['층'] = row['층']
            new_data['거래금액(만원)'] = row['거래금액(만원)']
            new_datas.append(new_data)
print(new_datas)
header: list = list(new_datas[0].keys())

file_name = 'apt_2403_조건2.csv'
with open(f'./output/{file_name}', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=header)
    writer.writeheader()
    for data in new_datas:
        writer.writerow(data)
print(f'{file_name}파일이 생성 되었습니다.')

# 조건 : 각 구별 롯데캐슬 거래건수 검색
# 출력조건 : 구(구만 나오도록 할 것), 거래건수
# 저장 파일명 : apt_2403_조건3.csv
# with로 파일처리

import csv
from typing import List, Dict

# 중구 : 110 형태의 데이터가 필요하기때문에 dict데이터를 사용. key는 문자열, value는 정수.
new_datas: Dict[str, int] = dict()

with open('./input/아파트(매매)_실거래가_20240304154554.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        row: dict
        if row['단지명'].find('롯데캐슬') >= 0:
            new_key: str = row['시군구'].split(" ")[1] # 구를 추출
            if new_key in new_datas: # 구이름의 키가 있는 경우, 기존 값 1을 더함
                new_datas[new_key] = new_datas[new_key] + 1
            else: # 구이름의 키가 없는 경우, 새로 생성 후 1을 입력
                new_datas[new_key] = 1
print(new_datas)

header: list = ['구', '거래건수']

file_name = 'apt_2404_조건3.csv'
with open(f'./output/{file_name}', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=header)
    writer.writeheader()
    for key, value in new_datas.items():
        writer.writerow({header[0]: key, header[1]: value})
        # print([key, value])
print(f'{file_name}파일이 생성 되었습니다.')

 

2. 서버에서 데이터(회원 아이디와 이름)을 실시간으로 받을 때 방식

 

 1) 아이디와 이름 순서대로 데이터를 전달

  test, tom

  > 인덱스를 사용하는 것처럼 위치값으로 파악

  빈값이나, 값에 구분자가 들어가는 경우 어려움등의 문제로 거의 사용하지 않음

 

  2) xml 방식

  <id>test</id>

  <name>tom</name>

  > 마크업 방식, 속성값이 반복되어서 상대적으로 데이터의 크기가 커짐

 

  3) json방식

  'id' : 'test',

  'name' : 'tom'

  > 객체 속성 표현 방식, 상대적으로 간결한 표현

 

3. JSON

 - 데이터를 전달할 때 사람이 읽고 쓰기 쉽도록 텍스트 형식을 정해 놓은 개방형 데이터 표준 형식

 - 많은 양의 데이터를 처리할 때는 속도가 느린 단점이 있으므로 경량의 데이터를 주고 받을 때 주로 사용

 - JavaScript Object Notation의 약자로 JavaScript의 객체 표현 방법이라는 뜻

 

 - JSON 데이터의 형식

 - 속성 attribute - 값 value 쌍으로 구성된 데이터들이 순서없이 모여 있는 구조

 - 중괄호를 이용해서 전체 JSON 데이터를 묶어 주고 속성과 값 사이에 클론(:)을 붙여 줌

 - 속성과 값으로 구성된 각 데이터의 구분을 쉼표(,)를 이용

 - 즉, 파이썬의 딕셔너리와 같은 모습

 

 - JSON 파일 생성

 - JSON 데이터 처리를 위해서 json 모듈을 제공

 - json 모듈을 import 하면 리스트나 딕셔너리와 같은 파이썬 객체를 JSON 데이터로 변환하거나,

 - JSON 데이터를 다시 파이썬 객체로 변환할 수 있음

 

 - 파이썬 객체를 JSON 문자열로 변환하는 것을 JSON 인코딩 encording 이라고 함

 - JSON 인코딩을 이용해 파이썬 객체를 JSON 문자열로 변환하고, 변환된 문자열을 파일에 기록하면 확장자가

 - json인 JSON 파일 (*.json)을 만들 수 있음

 

 - json.dumps() 메소드를 이용해서 JSON 인코딩을 처리할 수 있음

 

import json
from typing import List

dict_list = [
    {
        'name' : 'james',
        'age' : 20,
        'spec' : [
            175.5,
            70.5
        ]
    },
    {
        'name': 'alice',
        'age':21,
        'spec': [
            168.5,
            60.5
        ]
    }
]

json_string: str = json.dumps(dict_list)
print(json_string)

with open('./output/dict_list_01.json', 'w') as file:
    file.write(json_string)

print('dict_list_01.json 파일이 생성되었습니다.')

 

 - JSON 데이터는 네트워크를 주고받는 데이터의 표준 형식

 - 파일의 크기를 줄이기 위해서 의도적으로 불필요한 공백 문자 while space가 제거된 상태로 만들어 지기 때문에

 - 사람들이 직접 그 내용을 해석하기에는 불편

 

 - 보기 좋은 형식으로 만들기 위해서 json.dump() 메소드에 indent 옵션을 추가 할 수 있음

 - indent 옵션을 추가하면 JSON 데이터에 들여쓰기와 줄 바꿈이 추가되면서 보기 좋은 형식으로 만들어짐

json_stirng = json.dumps(dict_list, indent=4) # indent 옵션 사용

with open('./output/dict_list_02.json', 'w') as file:
    file.write(json_string)

print('dict_list_02.json 파일이 생성되었습니다.')

 

 - indent=4는 들여쓰기로 공백문자 4개로 처리 한다는 의미

 - 하지만 indent 옵션을 사용하면 파일의 크기가 커지기 때문에 네트워크 전송이 목적이라면

 - 사용하지 않는 것이 좋음

 - JSON 데이터는 문자열을 큰따옴표로 묶어서 처리

 

 - JSON 파일읽기

 - 인코딩된 JSON 데이터를 다시 파이썬 객체로 변경하는 것을 JSON 디코딩 decording

 - 디코딩을 처리하는 메소드는 json.loads()

with open('./output/dict_list_02.json', 'r') as file:
    json_reader = file.read()
    dict_list = json.loads(json_reader)
    print(dict_list)
    print(type(dict_list)) # <class 'list'>
    
for dic in dict_list:
    print('이름: {}'.format(dic['name']))
    print('나이: {}'.format(dic['age']))
    print('키: {}'.format(dic['spec'][0]))
    print('몸무게: {}'.format(dic['spec'][1]))
    print()

 

 - json 파일을 read() 메소드로 한 번에 전체를 읽어 들여 json_reader에 저장

 - json.loads(json_reader)를 통해서 디코딩이 이뤄지면 파이썬 객체인 dict_list가 생성

 - dict_list는 리스트 내부에 2개의 딕셔너리가 저장된 구조이기 때문에 for문을 이용해서 그 내용을 모두 처리 가능 

 

4. openweathermap

https://openweathermap.org/

 

Сurrent weather and forecast - OpenWeatherMap

OpenWeather Weather forecasts, nowcasts and history in a fast and elegant way

openweathermap.org

 

 - 접속 > 회원가입 > 이메일 인증 > 로그인

 

 - 구독 > API 활용 키발급

 

5. 공공데이터 포털 

 - https://www.data.go.kr/

 

공공데이터 포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

www.data.go.kr

 - 접속 > 회원가입 > 검색 > 공공API 클릭 > 원하는 데이터 활용신청

- 마이페이지 확인 

 

 - 참고문서 클릭 > 다운로드 > 참고문서 확인

 

 - 필요 시 상세설명 > 샘플코드 확인

 

6. JSON과 API

 - JSON은 자주 사용되는 데이터 형식으로, 데이터를 사람이 읽을 수 있는 문자열로 나타내는 방식

 - -> 바이너리가 아니라 텍스트

 - 많은 웹 사이트에서 프로그램이 웹 사이트와 상호 작용하는 데 JSON 형식의 내용을 제공

 - 이를 API 제공이라고 하고, API로 접근하는 것은 URL을 통해 일반 웹 페이지에 접근하는 것과 동일

 - 차이점은 API가 반환하는 값은 기계를 위한 JSON 형식으로 되어 있음

 

 - 원하는 데이터를 얻으려면 1) 프로그램이 요청해야 할 URL뿐만 아니라 2) 반환되는 JSON 데이터 구조와 일반적인 

 - 형식에 대한 문서도 찾아 봐야 됨. API를 제공하는 모든 사이트는 이 문서를 제공

 

 - 1) JSON 모듈

 - JSON 모듈은 json.loads(), json.dumps() 함수로 JSON 데이터와 파이썬 값을 서로 반환하는 모든 세부 사항을 처리

 - JSON은 모든 종류의 파이썬 값을 저장할 수 없음

 - 문자열, 정수, 부동 소수점 수, 불, 리스트(배열), 딕셔너리(객체), NoneType 자료형 값만 저장 가능

import json

 

 - 2) loads() 함수를 사용하여 JSON 읽기.

 - JSON 데이터가 들어 있는 문자열을 파이썬 값으로 반환하려면, 이를 json.loads() 함수에 전달

 - JSON 문자열은 큰 따옴표를 사용

string_json_data :str = '{"name": "Zopgie", "isCat" : true, "miceCaught" : 0, "felineIQ" : null}'
json_data_as_python_value = json.loads(string_json_data)
print(type(json_data_as_python_value)) # <class 'dict'>
print(json_data_as_python_value)
#{'name': 'Zopgie', 'isCat': True, 'miceCaught' : 0, 'felineIQ': None}

 

- 3) 온라인에서 JSON파일 열기

import urllib.request as request
json_data = request.urlopen('https://jsonplaceholder.typicode.com/todos/1').read()
print(type(json_data)) # <class 'bytes'>
python_data = json.loads(json_data)
print(type(python_data)) # <class 'dict'>
print(python_data)
# {'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}

 

 - 4) dumps() 함수를 이용하여 JSON 작성하기

 - 파이썬 값을 JSON 형식 데이터 문자열로 변환

python_data: dict = {'name': 'Zopgie', 'isCat':True, 'miceCaught':0, 'felineIQ':None}
json_data : str = json.dumps(python_data)
print(type(json_data)) # <class 'str'>
print(json_data) 
# {"name": "Zopgie", "isCat": true, "miceCaught": 0, "felineIQ": null}

 

- 5) CCTV JSON파일의 설치목적구분의 종류를 (중복없이) 출력

import json
with open('./input/cctv.json', 'r', encoding='utf-8') as jsonfile:
    json_data = jsonfile.read()
    cctv_list = json.loads(json_data)
    cctv_purpose = set()
    for place in cctv_list:
        cctv_purpose.add(place['설치목적구분'])

print(cctv_purpose)

 

7. API 이용하기

 - openweathermap 마이페이지에서 키값 복사

 - 사용할려는 API 코드 복사

import urllib.request as request
import json
import datetime

# API 이용하기
# openweathermap

api_key: str = '7bcfe93058cb697de719461d5eac0e1c'

# 대구 위치 정보
city:str = 'Taegu'
url: str = f'http://api.openweathermap.org/geo/1.0/direct?q={city}&limit=5&appid={api_key}'
# print(url)

json_data = request.urlopen(url).read()
dict_data = json.loads(json_data)
lat = dict_data[0]['lat']
lon = dict_data[0]['lon']
print(f'{lat}/{lon}')

# 현재 날씨
# https://openweathermap.org/current
# &lang=kr: 한국어 사용
# &units=metric : 섭씨 사용

url = f'https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={api_key}&lang=kr&units=metric'
print(url)
json_data = request.urlopen(url).read()
dict_data = json.loads(json_data)
json_data = json.dumps(dict_data, indent=4)
print(json_data)

print(f'도시 : {dict_data["name"]}')
print(f'현재날씨 : {dict_data["weather"][0]["description"]}')
print(f'최저기온 : {dict_data["main"]["temp_min"]}')
print(f'최고기온 : {dict_data["main"]["temp_max"]}')
print(f'일출시간 : {datetime.datetime.fromtimestamp(dict_data["sys"]["sunrise"])}')
print(f'일몰시간 : {datetime.datetime.fromtimestamp(dict_data["sys"]["sunset"])}')

# 날씨 예보 : 5 day / 3 Hour
# https://openweathermap.org/forecast5
url = f'https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={api_key}&lang=kr&units=metric'
json_data = request.urlopen(url).read()
dict_data = json.loads(json_data)
json_data = json.dumps(dict_data, indent=4)
print(json_data)

weather_list = dict_data['list'] # json 데이터 중에서 list key에 해당하는 목록 가져오기
print(weather_list)

for item in weather_list:
    print(f'시간: {datetime.datetime.fromtimestamp(item["dt"])}', end=',')
    print(f'최고기온: {item["main"]["temp_max"]}', end=',')
    print(f'날씨 : {item["weather"][0]["main"]}', end=', ')
    print(f'날씨 : {item["weather"][0]["description"]}')
    print()

 

 

8. 공공데이터 활용하기

# 공공데이터포탈 data.go.kr
# 기상청_단기예보 ((구)_동네예보) 조회서비스
# https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15084084

# 주소 : http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst
# 요청 변수
# 항목명(영문) / 항목명(국문) / 항목크기 / 항목구분 / 샘플데이터 / 항목설명
# serviceKey / 인증키 / 100 / 1 / 인증키 / (URL Encode) / 공공데이터포털에서 발급받은 인증키
# numOfRows / 한 페이지 결과 수 / 4 / 1 / 10 / 한 페이지 결과 수 Default: 10
# pageNo / 페이지 번호 / 4 / 1 / 1 / 페이지 번호 Default: 1
# dataType/ 응답자료형식 / 4 / 0 / XML / 요청자료형식(XML/JSON) Default: XML
# base_date / 발표일자 / 8 / 1 / 20210628 / ‘21년 6월 28일 발표
# base_time / 발표시각 / 4 / 1 / 0600 / 06시 발표(정시단위) -매시각 40분 이후 호출
# nx / 예보지점 X 좌표 / 2 / 1 / 55 /예보지점의 X 좌표값 *별첨 엑셀 자료 참조
# ny / 예보지점 Y 좌표 / 2 / 1 / 127 / 예보지점의 Y 좌표값 *별첨 엑셀 자료 참조
# ※ 항목구분 : 필수(1), 옵션(0), 1건 이상 복수건(1..n), 0건 또는 복수건(0..n)
# ※ 발표일자와 발표시각은 현재 기준으로 24시간 이내여야 함.

import requests
import datetime
import json

# 1. 초기값 설정
# 1) 서비스 키 : requests 사용시 자동으로 인코딩되어서 Decoding된 키를 사용
service_key : str = '1wMGYoH1onj8LIYDjyTfyuVPZLQc6F31PLdZjBj6jxjEi5P5suF4F9tGV2d38RvWOUj0tpiv6/OmN0NsBd93gg=='

# 2) 날짜 및 시간 설정
now: datetime = datetime.datetime.now() # 현재 날짜 및 시간 변환
base_date: str = '{:%Y%m%d}'.format(now) # base_date에 날짜를 입력하기 위해 날짜를 출력 형식으로 지정
base_time: str = '{:02}00'.format(now.hour) if now.minute > 30 else '{:02}00'.format(now.hour - 1)
# 현재 분이 30분 이전이면 이전 시간(정시)을 설정

# 3) 위치 값 (정부 엑셀 확인)
# 대구광역시 중구 삼덕동 89 90
nx = 89
ny = 90

# 4) 응답자료형식
data_type = 'JSON'

# 5) url 설정
url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst'
parameter = f'?serviceKey={service_key}&base_date={base_date}&base_time={base_time}&nx={nx}&ny={ny}&dataType={data_type}'
print(url + parameter)

# 2. 서버에서 요청값을 받은 후 파싱
# 딕셔너리 데이터를 분석해서 원하는 값을 추출
response = requests.get(url + parameter)
json_data = response.text
dict_data = json.loads(json_data)
print(dict_data)

weather_items = dict_data['response']['body']['items']['item']
print(f'[발표 날짜: {weather_items[0]["baseDate"]}]')
print(f'[발표 시간: {weather_items[0]["baseTime"]}]')

for k in range(len(weather_items)):
    weather_item = weather_items[k]
    obsr_value = weather_item['obsrValue']
    # T1H: 기온, RN1: 1시간 강수량, REH: 습도 %
    if weather_item['category'] == 'T1H':
        print(f' * 기온: {obsr_value}[℃]')
    elif weather_item['category'] == 'REH':
        print(f' * 습도: {obsr_value} [%]')
    elif weather_item['category'] == 'RN1':
        print(f' * 1시간 강수량: {obsr_value} [mm]')