[Python] Web API

is Yoon·2023년 8월 20일

Python & Data

목록 보기
3/4

API : Application Programming Interface, 응용 프로그램 프로그래밍 인터페이스. 응용 프로그램에서 사용할 수 있도록 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스

웹 API : 웹 애플리케이션 개발에서 다른 서비스에 요청을 보내고 응답을 받기 위해 정의된 명세


웹 API

웹 API의 데이터 획득 과정

REST API : Representational State Transfer API, 이미 존재하고 있는 데이터를 공유하는 데 이용되며, 데이터 요청 및 응답 후에 연결이 끊어진다. (대표적)

  • 클라이언트 —요청—> 서버
  • 클라이언트 <—응답 후 연결 닫음 — 서버

Streaming API : 향후 발생할 이벤트에 대해 등록해 놓고 그 이벤트가 발생하면 데이터를 갱신한 후에 응답하며, 응답 이후에도 강제로 연결을 끊기 전까지는 연결을 유지한다.

  • 클라이언트 —요청 —> 서버
  • 클라이언트 <—데이터 갱신 후 응답 — 서버 (반복)

웹 API의 인증 방식

인증이 필요한 웹 API : OAuth 인증 방식 (외부에서 해당 서비스에 접속하는 모바일, 데스크톱, 웹 애플리케이션의 보안 인증을 허용하는 개방형 인증 규약)으로, API 키와 접속 토큰, 그리고 비밀번호를 이용해 애플리케이션별로 인증을 수행하고 서비스를 이용할 수 있는 권한을 얻는다.

  • 서비스를 제공하는 사이트에 사용자 등록을 한 후에 웹 API별로 애플리케이션 혹은 서비스를 신청하면 이용 가능하다.
  • OAuth 이전에는 아이디와 비밀번호 기반의 인증 방식이 있었다.
  • 웹 API에 따라서 요구하는 것이 다르다.
    예를 들어, 트위터에서는 Consumer Key (API 키), Consumer Secret (API 비밀번호), Access Token (접속 토큰), Access Token Secret (접속 토큰 비밀번호)를 사용한다.
  • 속도 제한 : 일정 시간 내에 접속 횟수를 제한하는 것으로, 대부분 웹 API는 접속 횟수가 제한되어 있다. 따라서, 웹 API를 이용한 코드 작성 전에 속도 제한을 미리 확인해야 한다.

응답 데이터의 형식 및 처리

JSON, XML

  • 웹 API로 요청해서 응답받은 데이터의 형식
  • 웹 서버에서 클라이언트로 데이터를 전달하기 위해 만든 구조화된 텍스트 형식

JSON (JavaScript Object Notation) :

  • JSON 홈페이지, JSON 형식 검사 사이트, JSON 온라인 뷰어
  • 하나의 데이터 집합 : {객체}
  • 객체의 구성 : 이름(name):값(value)으로 이뤄진 쌍의 집합
    • 이름 : 문자열
    • 값 : 숫자, 문자열, 배열, 객체
  • JSON 내장 라이브러리 : JSON 형식의 데이터, 파이썬의 데이터 타입으로 쌍방 변환하는 기능
    • json.dumps(python_data [, indent=n, sort_keys=T/F, ensure_ascii=T/F) : 파이썬 데이터를 JSON 형태로 변환
      • indent : n칸만큼 들여쓰기
      • sort_keys : 파이썬 데이터가 딕셔너리 타입일 경우 키를 기준으로 정렬할지 결정
      • ensure_ascii : ASCII 코드로 구성된 문자열인지 설정 (한글이 있는 경우 False로 설정)
    • json.loads(json_data) : JSON 형태의 데이터를 파이썬 데이터 타입으로 변환
import json
python_dict = {'name': '홍길동', 'age': 20, 'etc': {'키':170, '몸무게':70}}

# 1. 파이썬 데이터를 JSON 형태로 변환하기
json_data = json.dumps(python_dict)
type(json_data)   # str : JSON 형태의 데이터로 반환되었기 때문에 딕셔너리 데이터가 문자열로 바뀜

# 하지만 위의 방법은 출력 시 알아보기 힘드므로, 아래처럼 입력해야 한다.
json.dumps(python_dict, indent=3, sort_keys=True, ensure_ascii=False)

# 2. JSON 형태의 데이터를 파이썬 데이터 타입으로 변환하기
json_dict = json.loads(json_data)
type(json_dict)   # dict

# 원하는 정보만 추출할 수도 있다.
json_dict['etc']['키']   # 170

XML (Extensible Markup Language) :

  • 데이터 저장 및 전달을 위해 만든 다목적 마크업 언어
  • 마크업 언어 : 일반적인 텍스트와 구분되는 태그를 이용해 문서나 데이터를 구조화하는 언어 (ex. HTML)
  • 태그를 정의해서 사용 가능
  • XML 문서 규칙 :
    • 요소 : 태그는 <문자열>로 시작해서 </문자열>로 끝나야 한다.
    • 태그는 중첩해 사용할 수 있지만, 순서는 올바르게 지켜야 한다.
    • 반드시 최상위(root) 요소가 있어야 하며, 다른 요소들을 감싸야 한다.
    • <문자열 name='속성'> : 태그에는 속성을 사용할 수 있다.
    • <!—주석—> 존재
  • <?xml version='1.0' encoding='UTF-8' ?>
<?xml version='1.0' encoding='UTF-8' ?>
<사용자 정보><이름>홍길동</이름><나이>20</나이>
<etc><키 unit='cm'>170</><몸무게 unit='kg' >70</몸무게>
</etc></사용자정보>
  • xmltodic 라이브러리 (참고)
    xmltodict.parse(xml_input [, xml_attribs=T/F) - XML형식의 데이터를 파이썬의 딕셔너리 타입으로 변환
    • xml_arrtibs : 속성 처리 여부. True (기본값) or False (속성 무시)
    • True일 경우, 변수[tag]['@name'] 형식으로 속성을, 변수[tag]['#text'] 형식으로 속성을 갖는 태그의 문자열을 가져온다.
import xmltodict

dict_data = xmltodict.parse(xml_data, xml_attribs=Ture)
dict_data['사용자 정보']['이름']   # '홍길동'
dict_data['사용자 정보']['etc']['키']['@unit']   # cm
dict_data['사용자 정보']['etc']['키']['#text']   # ' 170'

dict_data = xmltodict.parse(xml_data, xml_attribs=False)
# True일 때와 달리, unit 등의 정보가 없다. 즉, 속성을 무시한다.



웹 사이트 주소에 부가 정보 추가하기

1) 웹 사이트 주소에 경로 추가하기

import requests

base_url = "https://api.github.com/"
sub_dirs = ["events", "user", "emails"]

for sub_dir in sub_dirs : 
    url_dir = base_url + sub_dir
    r = requests.get(url_dir)
    print(r.url)
>>> https://api.github.com/events
    https://api.github.com/user
    https://api.github.com/emails

2) 웹 사이트 주소에 매개변수 추가하기

  • 웹 사이트 주소/경로 ? key = value & key2 = value2
  • 키와 값 쌍의 순서는 무관하다. (순서 바뀌어도 동일한 결과)
# 키와 값이 포함된 URL 직접 생성
import requests

LAT = "37.57"          # 위도
LON = "126.98"         # 경도
API_KEY = "abu94sbd"   # API 키 (임의의 값)
UNIT = "metric"        # 단위

site_url = "http://api.openweathermap.org/data/2.5/weather"
parameter = "?lat=%s&lon=%s&appid=%s&units=%s"%(LAT, LON, API_KEY, UNIT)
url_para = site_url + parameter
r = requests.get(url_para)

print(r.url)
>>> http://api.openweathermap.org/data/2.5/weather?lat=37.57&lon=126.98&appid=abu94sbd&units=metric
# params 인자를 사용해 딕셔너리 타입으로 키와 값을 전달하기
import requests

LAT = "37.57"          # 위도
LON = "126.98"         # 경도
API_KEY = "abu94sbd"   # API 키 (임의의 값)
UNIT = "metric"        # 단위

req_url = "http://api.openweathermap.org/data/2.5/weather"
req_parameter= {‘lat’: LAT, ‘lon’: LON, ‘appid’: API_KEY, ‘units’: UNIT}
r = requests.get(req_url, params=req_parameter)

print(r.url)
>>> http://api.openweathermap.org/data/2.5/weather?lat=37.57&lon=126.98&appid=abu94sbd&units=metric

요청 주소(url)와 요청 매개변수(params)를 분리해 requests.get()에 입력할 때, URL 인코딩(=퍼센트 인코딩)을 주의해야 한다.

  • URL에 사용하는 문자를 인코딩하는 방법
  • 알파벳, 숫자, 몇몇 기호를 제외한 나머지는 16진수 값으로 변환(인코딩)하여 이용해야 한다.
  • 기본적으로 URL 인코딩이 되지만, 이미 URL 인코딩된 결과가 API 키의 문자열로 제공되는 경우, 인코딩된 API 키를 원래의 API 키 문자열로 되돌리는 디코딩이 필요하다. (requests.utils.unquote() 이용)
import requests

API_KEY = "fwui3nkf%3D%3D"
API_KEY_decode = requests.utils.unquote(API_KEY)

print(API_KEY)
print(API_KEY_decode)
>>> fwui3nkf%3D%3D
    fwui3nkf==

# %2B -> +, %2F -> /, %3D -> =






1) API 키를 사용하지 않고 데이터 가져오기

# (예제) 국제 우주 정거장 위치 보기
import requests
import json

url = "http://api.open-notify.org/iss-now.json"

r = requests.get(url)
json_to_dict = r.json()

print(r.text)
json_to_dict
>>> {"timestamp": 1696842653, "message": "success", "iss_position": {"longitude": "-8.6222", "latitude": "27.1186"}}
# iss_position은 각각 현재 위도, 현재 경도를 나타낸다.

def ISS_Position(iss_position_api_url) :
    json_to_dict = requests.get(iss_position_api_url).json()
    reruen json_to_dict['iss_position']

for k in range(5) :
    print(ISS_Position(url))
    tim.sleep(10)             # 10초 동안 코드 실행을 일시적으로 중단
# 10초 간격으로 국제 우주 정거장의 위치 정보를 갱신한다.
# (예제) 국가 정보 가져오기
import requests

url_temp = 'https://restcountries.eu/rest/v1/name/'
country = 'South Korea'
url = url_temp + country

r = requests.get(url)
json_to_list = requests.get(url).json()
json_to_list[0]['capital']






2) 트위터에 메시지 작성하고 가져오기

API 키 및 접속 토큰 생성

  • 트위터 회원가입 후 Twitter Apps에 접속하여 애플리케이션 생성
  • Consumer Key (API Key), Consumer Secret (API Secret), Access Token, Access Token Secret 이용

Tweepy 설치 및 인증

import tweepy

consumer_key = 'ac24asc'
consumer_secret = 'v23kjs'
access_token = '523s8fsk'
access_secret = '3fskb34'
# 트위터 API 요청 횟수 제한 : 15분 안에 최대 15개 요청 가능

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_secret)

api = tweepy.API(auth)
API.me()        # 트위터 API 사용을 위해 인증된 트위터 사용자의 다양한 정보 반환
API.me().name   # 회원 등록할 때 입력한 이름 출력

# 트윗 작성하기
tweet_update_status = api.update_status('파이썬에서 트위터에 트윗 올리기')
tweet_media_update_status = api.update_with_media('C:/data/img.png', '부가적인 메시지 입력')

# 타임라인에서 메시지 가져오기
for status in tweepy.Cursor(api.home_timeline).items(2) :
    print(status._json['text'])         # 트위터 내용 출력
    print(statue._json['created_at'])   # 메시지 생성 시간 출력

# 키워드를 지정해 데이터 가져오기 (Stream API 이용)
# 인증된 auth 변수를 이용해 트위터 API 클래스 정의
class MyStreamListener(tweepy.StreamListener) :

    def __init__(self, max_num) :
        super().__init__()
        self.tweet_num = 0
        self.max_num = max_num

    def on_statue(self, status) :
        self.tweet_num = self.tweet_num + 1
        file_name = 'C:/data/twitter_stream.txt'
        if(self.tweet_num <= self.max_num) :
            with open(file_name, 'a' encoding='utf-8') as f:
                write_text =***+ status.text + '\n'
                f.write(write_text)
            return True
        else : 
            return False

    def on_error(self, status) :
        print(status)
        return False

if __name__ = '__main__' :
    myStreamListener = MyStreamListener(5)
    myStream = tweepy.Stream(auth, myStreamListener)
    myStream.filter(track = ['머신 러닝', 'Machine Learning'])






3) 정부의 공공 데이터 가져오기

공공 데이터 포털 -> 데이터 셋 -> 오픈 API -> 원하는 서비스 선택 -> 서비스 활용 신청 -> 개발 계정 신청

# 주소 및 우편번호 가져오기 (도로명주소조회서비스) 
import requests
import xmltodict

API_KEY = 'apikey'
API_KEY_decode = requests.utils.unquote(API_KEY)

req_url = 'http://openapi.epost.go.kr/postal/retrieveNewAdressAreaCdService/retrieveNewAdressAreaCdService/getNewAddressListAreaCd'

search_Se = 'road'
srch_wrd = '반포대로 201'

req_parameter = {'ServiceKey':API_KEY_decode, 'searchSe':search_Se, 'srchwrd':srch_wrd}

r = requests.get(req_url, params = req_parameter)
xml_data = r.text

dict_data = xmltodict.parse(xml_data)

adress_list = dict_data['NewAddressListResponse']['newAddressListAreaCd']

print(adress_list['zipNo'])      # 우편번호
print(adress_list['lnmAdres'])   # 도로명 주소
print(adress_list['rnAdres'])    # 지번 주소

# 날씨 정보 가져오기 (동네예보정보조회서비스)
import json
import datetime

now = datetime.datetime.now()
date = "{:%Y%m%d}.format(now)
time = "{:%H00}".format(now)

req_url = "http://newsky2.kma.go.kr/service/SecndSrtpdFrcstInfoService2/ForecastGrib"

baseDate, baseTime = date, time   # 발표 일자, 시간 지정
nx_val, ny_val = 60, 127   # 예보지점 좌표 (서울시 종로구 사직동)
num_of_rows = 6   # 한 페이지에 포함된 결과 수
page_no = 1       # 페이지 번호
output_type = "json"   # 응답 데이터 형식 지정

req_parameter = {"ServiceKey":API_KEY_decode, "nx":nx_val, "ny":ny_val, "base_date":baseDate, "base_time":baseTime, "pageNo":page_no, "num0fRows":num_of_rows, "_type":output_type}

r = requests.get(req_url, params = req_parameter)   # 데이터 요청
dic_data = r.json()   # JSON 형태로 받은 데이터를 딕셔너리 데이터로 변환

weather_items = dict_data['response']['body']['items']['item']
sky_cond = ['맑음', '구름 조금', '구름 많음', '흐림']
rain_type = ['없음', '비', '진눈개비', '눈']

print("발표 날짜 : {}".format(weather_items[0]['baseDate']))

for k in range(len(weather_items)) : 
    weather_item = weather_items[k]
    obsrValue = weather_item['obsrValue']
    if(weather_item['category'] == 'T1H') : 
        print('기온 : {}도'.format(obsrValue))
    elif(weather_item['category'] == 'REH') : 
        print('습도 : {}%'.format(obsrValue))
    elif(weather_item['category'] == 'SKY') : 
        print('하늘 : {}%'.format(sky_cond[obsrValue-1]))
    elif(weather_item['category'] == 'PTY') : 
        print('강수 : {}'.format(rain_type[obsrValue]))






참고 :
데이터 분석을 위한 파이썬 철저 입문

profile
planning design development with data

0개의 댓글