[TIL] 22.09.29

문종현·2022년 9월 29일
0

TIL

목록 보기
6/119

👉 오늘 한 일

  • 주간 키워드 리뷰 (requests)
  • API 학습

주간 키워드 리뷰(request)

1. requests란 ?

Requests is an elegant and simple HTTP library for Python, built for human beings.

  • Python에서 특정 웹사이트에 HTTP 요청을 보내는 모듈
  • 특정 웹사이트에 HTTP 요청을 보내 HTML 문서를 받아올 수 있는 라이브러리
    • 정확히는 HTML 문서가 아닌 단순한 String 형식의 자료
      • BeautifulSoup과 같은 라이브러리를 통해 HTML 문서로 바꿀 수 있다.

2. requests 라이브러리 설치 및 로드

  • Requests officially supports Python 3.7+.
    • Python 3.7 이상의 버전에서 지원된다.

기본적으로 pip를 이용해 설치하며, 이후 import를 통해 호출할 수 있다.

-m pip install requests
!pip install requests

설치방법

Environments - base (root) - Open Terminal - conda install requests

아나콘다를 이용하여 설치할 경우, 아래의 절차를 따른다.

  1. 아나콘다를 실행하여 environments에서 터미널을 실행한다.

  1. 터미널에서 conda install -c anaconda requests를 입력하여 설치한다.

3. http 통신 방식

http란?

  • 클라이언트가 웹 서버에게 사용자 요청의 목적이나 행동에 대해 알리는 수단.
  • 최초의 http에는 GET() 하나만 사용, 이후 다양한 메서드 개발

    http 메서드의 종류 5가지

    GET : 필요한 데이터를 Query String 에 담아 전송하며, 리소스를 조회하는데 사용한다.
    POST : 요청한 데이터를 처리하며, 데이터를 등록하는 데 주로 사용한다. PUT : POST와 유사한 전송 구조를 가지지만 보통 내용을 갱신하는 위주로 사용된다.
    PATCH : 리소스를 부분적으로 변경한다.
    DELETE : 웹 리소스를 제거한다.

http 상태코드

클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능이며, 100 ~ 500번대의 넘버를 사용한다.

💡 상태코드 종류

1xx (정보) : 요청을 받았으며 프로세스를 계속한다.

2xx (성공) : 요청을 성공적으로 받았으며 인식했고 수용하였다.

3xx (리다이렉션) : 요청 완료를 위해 추가 작업 조치가 필요하다.

4xx (클라이언트 오류) : 요청의 문법이 잘못되었거나 요청을 처리할 수 없다.

5xx (서버 오류) : 서버가 명백히 유효한 요청에 대해 충족을 실패했다.

→ 데이터 수집시에는 `200 OK` 가 나오는지 확인 하는 것이 중요!!
import requests
   
# 네이버 금융 일별시세 사이트
url = "https://finance.naver.com/item/sise_day.naver?code=005930&page=10"
   
response = requests.get(url, headers = {"user-agent":"Mozilla/5.0"})
response
   
> <Response [200]>

4. requests 메서드

Response()

requests.Response()

  • HTTP 요청에 대한 서버의 응답을 포함한 객체
  • 해당 객체에는 HTTP 요청에 대한 서버의 응답을 반환한다.
    • requests 모듈을 이용하여 요청 한 다음 그에 대한 Response 결과들이 사용자가 좀 더 편하게 볼 수 있도록 request.modules.Response 클래스를 이용하여 좀 더 편하게 정리.
  • Class requests.modules.Response

대부분의 예시는 하단의 코드로 대체

import requests
r **=** requests.get("https:/example.com")
import requests

response = requests.get("https://www.naver.com/")

# 서버 요청 결과 출력
print(response)
>>> <Response [200]>

# http 응답 코드 출력
print(response.status_code)
>>> 200

# 요청 / 응답 본문을 str 타입으로 반환
response.text

# 요청 / 응답 본문을 byte 타입으로 반환
response.content

# 요청 / 응답 본문을 json 형식으로 디코딩하여 반환
# 만약 올바른 json 형식이 아닌 경우 에러
response.json()

# 요청한 뒤 응답의 최종 URL을 반환
print(response.url)
>>> 'https://www.naver.com/'

# 요청 / 응답 코드가 200이면 True 아니면 False
print(response.ok)
>>> True

# 요청 / 응답 인코딩을 반환
print(response.apparent_encoding)
>>> 'Windows-1254'

requests.get(url, params=None, **kwargs)

def get(url, params=None, **kwargs):
    r"""Sends a GET request.

    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary, list of tuples or bytes to send
        in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    return request("get", url, params=params, **kwargs)
# https://jsonplaceholder.typicode.com/guide/
# JSONPlacehoder : 시물의 조회, 생성, 수정, 삭제를 테스트할 수 있는 API를 제공
# request를 이용한 테스팅

# 게시물 1건 조회(GET)
import requests

url = 'https://jsonplaceholder.typicode.com/posts/1'
res = requests.get(url)
print(res.json())

# 사용자 번호(userId)가 1인 게시물만 조회
## https://jsonplaceholder.typicode.com/posts?userId=1와 같은 URL을 사용할 수도 있지만,
## URL에 직접 인수를 포함하는 방식은 URL 인코딩 오류가 발생할 수도 있다.
## 따라서, get함수의 파라미터를 이용한다.
import requests

url = 'https://jsonplaceholder.typicode.com/posts'
params = {'userId': 1}
res = requests.get(url, params=params)
print(res.json())

requests.put(url, data=None, **kwargs)

def put(url, data=None, **kwargs):
    r"""Sends a PUT request.

    :param url: URL for the new :class:`Request` object.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    return request("put", url, data=data, **kwargs)
# 게시물 수정(PUT)
import requests
import json

url = 'https://jsonplaceholder.typicode.com/posts/1'
headers = {'Content-type': 'application/json; charset=utf-8'}
data = {
    'id': 1,
    'title': '제목을 수정',
    'body': '내용을 수정',
    'userId': 1,
}
res = requests.put(url, headers=headers, data=json.dumps(data))
print(res.json())

requests.post(url, data=None, json=None, **kwargs)

def post(url, data=None, json=None, **kwargs):
    r"""Sends a POST request.

    :param url: URL for the new :class:`Request` object.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    return request("post", url, data=data, json=json, **kwargs)
# 게시물 저장(POST)
import requests
import json

url = 'https://jsonplaceholder.typicode.com/posts'
headers = {'Content-type': 'application/json; charset=utf-8'}
data = {
    'title': 'foo',
    'body': 'bar',
    'userId': 1,
}

res = requests.post(url, headers=headers, data=json.dumps(data))
print(res.json())

requests.patch(url, data=None, **kwargs)

def patch(url, data=None, **kwargs):
    r"""Sends a PATCH request.

    :param url: URL for the new :class:`Request` object.
    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
        object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    return request("patch", url, data=data, **kwargs)
# 게시물 일부 속성 수정(PATCH)
import requests
import json

url = 'https://jsonplaceholder.typicode.com/posts/1'
headers = {'Content-type': 'application/json; charset=utf-8'}
data = {
    'title': 'foo',
}
res = requests.patch(url, headers=headers, data=json.dumps(data))
print(res.json())

requests.delete(url, **kwargs)

def delete(url, **kwargs):
    r"""Sends a DELETE request.

    :param url: URL for the new :class:`Request` object.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    return request("delete", url, **kwargs)
# 게시물 삭제(DELETE)
import requests

url = 'https://jsonplaceholder.typicode.com/posts/1'
res = requests.delete(url)
print(res.json())

5. requests 활용

http 상태값 반환

  • if 문 을 이용해 요청을 정상적으로 받아왔는지 판단할 수 있음
    if response.status_code in range(200, 300):
        print("정상적으로 데이터를 수집하였습니다.")
    else:
        print(f"비정상 [코드 : {response.status_code}]")

API호출 & 데이터 수집

  • API에서 선택적으로 데이터를 긁어올 수 있음
    (서울 미세먼지 데이터 open API에서 구 이름과 미세먼지 양만 뽑아옴)

    import requests # requests 라이브러리 설치 필요
     
     r = **requests.get**('http://openapi.seoul.go.kr:8088/6d4d776b466c656533356a4b4b5872/json/RealtimeCityAir/1/99')
     rjson = r.json()
     
     citys = rjson["RealtimeCityAir"]["row"]
     
     for city in citys:
     		gu_name = city["MSRSTE_NM"]
     		gu_mise = city["IDEX_MVL"]
     		print(gu_name, gu_mise)
  • openweathermap API를 이용한 날씨 확인하기(1주차 실습)

    
    import requests
    import json
    
    city = "Seoul"
    apikey = "개인 key"
    lang = "kr"
    
    api = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={apikey}&lang={lang}&units=metric"
    
    result = requests.get(api)
    data = json.loads(result.text)
    
    print(data["name"],"의 날씨입니다.")
    print("날씨는 ",data["weather"][0]["description"],"입니다.")
    print("현재 온도는 ",data["main"]["temp"],"입니다.")
    print("하지만 체감 온도는 ",data["main"]["feels_like"],"입니다.")
    print("최저 기온은 ",data["main"]["temp_min"],"입니다.")
    print("최고 기온은 ",data["main"]["temp_max"],"입니다.")
    print("습도는 ",data["main"]["humidity"],"입니다.")
    print("기압은 ",data["main"]["pressure"],"입니다.")
    print("풍향은 ",data["wind"]["deg"],"입니다.")
    print("풍속은 ",data["wind"]["speed"],"입니다.")
  • 위키독스의 특정 페이지를 파일로 저장할 수 있음

    
    import requests
    
    def get_wikidocs(page):
        url = 'https://wikidocs.net/{}'.format(page)
        res = requests.get(url)
        with open('wikidocs_%s.html' % page, 'w', encoding='utf-8') as f:
            f.write(res.text)
    
    get_wikidocs(2)

API 학습

정의
API(Application Programming Interface) : 클라이언트, 서버와 같은 서로 다른 프로그램에서 요청과 응답을 주고 받을 수 있게 만든 체계. 예를 들어 기상청의 소프트웨어 시스템에는 일일 기상 데이터가 들어 있다. 휴대폰의 날씨 앱은 API를 통해 이 시스템과 "대화"하고 휴대폰에 매일 최신 날씨 정보를 표시한다.

요청과 응답(request, response)

클라이언트는 요청(request)을 보내고, 서버는 요청을 받아서 응답(response)을 준다. API를 통해 요청과 응답을 주고 받을 때에는 데이터도 같이 담긴다.
요청은 크게 4가지 요소로 나눌 수 있는데, 이를 CRUD라고 부른다.

CRUD의 정의와 메소드

C (Create) : 데이터를 올리는 요청 → POST

R (Read) : 데이터를 불러오는 요청 → GET

U (Update) : 데이터를 바꾸는 요청 → PUT(전체), PATCH(일부)

D (Delete) : 데이터를 지우는 요청 → DELETE

Response의 대표적인 예시

200: 성공했다 (201, 202 … 등)

400: 클라이언트의 요청이 잘못됐다 (대표적인 오류: 404 not found)

500: 서버의 문제로 실패했다 (500, 501 … 등)

GET 방식

GET은 클라이언트에서 서버로 정보를 요청하기 위해 사용되는 메서드이다. GET을 통한 요청은 URL 주소 끝에 파라미터로 포함되어 전송되며, 이 부분을 쿼리 스트링(query string) 이라고 부른다. URL 끝에 " ? " 를 붙이고 그다음 변수명1 = 값1 & 변수명2 = 값2... 형식으로 이어 붙이면 된다. (단, 길이에 제한이 있다.)

POST 방식

POST는 클라이언트에서 서버로 리소스를 생성하거나 업데이트하기 위해 데이터를 보낼 때 사용 되는 메서드이다. POST는 전송할 데이터를 HTTP 메시지 body 부분에 담아서 서버로 보낸다. POST로 데이터를 전송할 땐 길이 제한이 따로 없어, 용량이 큰 데이터를 보낼 때 사용하거나 GET처럼 데이터가 외부적으로 드러나지 않기때문에 보안이 필요한 부분에 많이 사용된다.

GET / POST 방식의 차이점

  • 사용목적 : GET은 서버의 리소스에서 데이터를 요청할 때, POST는 서버의 리소스를 새로 생성하거나 업데이트할 때 사용한다. DB로 따지면 GET은 SELECT에 가깝고, POST는 Create 에 가깝다고 보면 된다.

  • 요청에 body 유무 : GET은 URL 파라미터에 요청하는 데이터를 담아 보내기 때문에 HTTP 메시지에 body가 없다. POST는 body에 데이터를 담아 보내기 때문에 HTTP 메시지에 body가 존재한다.

  • 멱등성 (idempotent) : GET 요청은 멱등이며, POST는 멱등이 아니다.

멱등이란? 멱등의 사전적 정의는 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 의미한다. GET은 리소스를 조회한다는 점에서 여러 번 요청하더라도 응답이 똑같다. 반대로 POST는 리소스를 새로 생성하거나 업데이트할 때 사용되기 때문에 멱등이 아니라고 볼 수 있다.(게시판에 새로운 글을 업로드하는 상황을 떠올리면, 여러번 요청한다면 중복된 글이 계속 올라가는 것을 생각해볼 수 있다.)

REST API

REST API 개요

특징 및 다른 API와의 차이점

  • API는 애플리케이션 소프트웨어를 구축하고 통합하기 위한 정의 및 프로토콜 세트.

    • 자율성이 높지 못함 (정해진 방식 사용)
  • REST는 프로토콜이나 표준이 아닌 아키텍처 제약 조건의 집합. API 개발자는 다양한 방법으로 REST를 구현할 수 있음

    • 자율성이 높음

REST(Representational State Transfer REST)

클라이언트가 서버 데이터에 액세스하는 데 사용할 수 있는 GET, PUT, DELETE 등의 함수 집합을 정의. 클라이언트와 서버는 HTTP를 사용하여 데이터를 교환

  • 추가적으로 액세스에 도움을 줄 뿐 직접적으로 데이터를 넘기는 작업은 하지 않음,

  • 서버에 데이터를 저장하지 않기 때문에 필요한 데이터를 유저가 능동적으로 조절 할 수 있음

공통적으로 API가 수행하는 기능

  • 정보를 검색하거나 기능을 수행하기 위해 컴퓨터 또는 시스템과 상호 작용하려는 경우 API를 사용하면 해당 시스템이 요청을 이해하고 이행할 수 있도록 원하는 것을 해당 시스템과 통신할 수 있음.

  • API는 사용자 또는 클라이언트와 그들이 얻고자 하는 리소스 또는 웹 서비스 사이의 중재자로 생각할 수 있음

  • 클라이언트 요청이 RESTful API를 통해 만들어지면 리소스 상태 표현을 요청자 또는 끝점으로 전송

  • 이 정보 또는 표현은 HTTP를 통해 JSON(Javascript Object Notation), HTML, XLT, Python, PHP 또는 일반 텍스트와 같은 여러 형식 중 하나로 전달

  • 모든 API에서 JSON 형식을 사용하기 때문에 JSON방식은 웹에서 가장 널리 쓰이는 언어

보호 방법

  • 근래의 대부분의 서비스 시스템들은 API 기반으로 통신함
  • 앱과 서버 간의 통신 또는 자바 스크립트 웹 클라이언트와 서버대부분 통신이 이 API들을 이용해서 이루어지기 때문에 한 번 보안이 뚫리면 개인 정보가 탈취되는 것 뿐만 아니라, 더 많은 문제를 일으킬 수 있음

보호 방법

(1) 인증(Authentication)
API를 호출하는 대상을 확인하는 절차

(2) 인가(Authorization)
해당 리소스에 대해 사용자가 그 리소스를 사용할 권한이 있는지 확인하는 과정

(3) 네트워크 레벨 암호화
인증과 인가의 과정이 끝나 API를 호출하게 되면, 네트워크를 통해 데이터가 이동함
누군가 중간에서 이 네트워크 통신을 가져갈 수 없게 해야함
이를 네트워크 프로토콜단에서 처리하는 것을 네트워크 암호화라고 함

(4) 메시지 무결성 보장
메시지 무결성이란 메시지가 해커와 같은 외부 요인에 의해 중간에 변조가 되지 않게 방지하는 것을 말함
무결성을 보장하기 위해서 많이 사용되는 방식은 메시지에 대한 서명(SIgnature)을 생성해서 메시지와 같이 보내고 검증하는 방식
네트워크 레벨의 암호화가 완벽하다면 외부적인 요인을 걱정할 필요가 없으므로 사용하지 않아도 됨

(5) 메시지 본문 암호화
네트워크 레벨의 암호화를 사용할 수 없거나, 이를 신뢰할 수 없는 상황에서 추가로 메시지 자체를 암호화하는 방법을 사용
전체 메시지를 암호화 / 특정 필드만 암호화(일반적으로 사용)

인증 방식의 종류

(1) API Key 방식
API Key란 특정 사용자만 알 수 있는 일종의 문자열
API를 호출하고자 할 때, 사용자는 API 제공사의 포탈 페이지에서 API 키를 발급 받고 API를 호출 할 때 API 키를 메시지 안에 넣어 호출함
서버는 메시지 안에서 API 키를 읽어 누가 API를 호출했는지 인증함

(2) API 토큰 방식
ID, 비밀번호 등으로 사용자를 인증한 다음에 그 사용자가 API 호출에 사용할 기간이 유효한 API 토큰을 발급해서 사용자를 인증하는 방식

API 토큰을 별도로 발급해 사용하는 이유

  • 사용자가 비밀번호를 주기적으로 바꿀 수 있기 때문
  • 매번 네트워크를 통해 사용자 ID와 비밀번호를 보내는 것은 보안상 사용자 계정 정보를 탈취 당할 가능성이 크기 때문
profile
자라나라 새싹새싹🌱

0개의 댓글