Develop/Python

requests 라이브러리 사용법

Seung Ju 2021. 6. 20. 15:32
반응형

requests란 ?

requests는 Python 용 HTTP 라이브러리 입니다.


패키지 설치

python의 패키지 매니저인 pip를 이용해서 requests 패키지를 설치합니다.

$ pip install requests

 

설치가 잘 되었는지 파이썬 인터프리터를 실행하여 확인해봅니다.

$ python
>>> import requests
>>> requests.get("https://seungjuv.tistory.com")
# <Response [200]>

 

requests 라이브러리를 사용하여 블로그에 접속해보니 상태 코드 200이 응답되는 것을 확인할 수 있습니다.


API

requests 라이브러리는 심플하고 직관적인 API를 제공하는데요, 어떤 HTTP의 Method를 요청하느냐에 따라서 해당되는 Method의 함수를 사용하면 됩니다.

>>> requests.get()    # GET방식
>>> requests.post()   # POST방식
>>> requests.put()    # PUT방식
>>> requests.delete() # DELETE방식

URL에서 매개 변수 전달

URL에서 Query string에 있는 데이터 종류를 보내려는 경우가 많습니다. 만약 URL안에 직접적으로 구성하게 된다면, URL은 ? 뒤에 URL에서 key/value 값으로 구성하게 될 것 입니다. 예를 들면, http://httpbin.org/get?key1=value1과 같이 번거로운 작업이 될 것 입니다. key1=value1, key2=value2와 같이 데이터를 http://httpbin.org/get에 전달하고자 하는 경우 다음 코드를 사용합니다.

>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> response = requests.get('https://httpbin.org/get', params=payload)

 

URL을 확인하면 URL이 올바르게 인코딩 되었음을 알 수 있습니다.

>>> print(response.url)
# https://httpbin.org/get?key2=value2&key1=value1

값이 없는 키는 URL Query string에 추가되지 않습니다.

 

또, list를 값으로 전달할 수 있습니다.

>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}

>>> response = requests.get('https://httpbin.org/get', params=payload)
>>> print(response.url)
# https://httpbin.org/get?key1=value1&key2=value2&key2=value3

Response

요청이 정상적으로 처리된 경우, response body에 요청한 데이터가 담겨져 옵니다.

>>> response = requests.get('https://api.github.com/events')

Response Content

text 속성을 통해 서버의 컨텐츠를 자동으로 디코딩된 문자열을 얻을 수 있습니다.

>>> response.text
# '[{"repository":{"open_issues":0,"url":"https://github.com/...

 

요청을 할 때, requests는 HTTP header를 기반으로 response 인코딩에 대한 추측을 합니다. requests에서 추측한 text 인코딩은 response.text에 접근할 때 사용됩니다. 이는 response객체에 encoding 속성을 사용하여 requests에서 사용중인 인코딩을 확인하고 변경할 수 있습니다.

>>> response.encoding
# 'utf-8'
>>> response.encoding = 'ISO-8859-1'

인코딩을 변경하면 response.text를 호출할 때마다 requests에서 response.encoding의 새 값을 사용합니다. 특별한 로직을 적용하여 Content의 인코딩을 파악할 수 있는 모든 상황에서 이 작업을 수행할 수 있습니다. 예를 들어 HTML과 XML은 본문에서 인코딩을 지정할 수 있습니다. 이런 경우 response.content를 사용하여 인코딩을 찾은 다음 reponse.encoding을 설정해야 합니다. 이렇게 하면 올바른 인코딩으로 response.text를 사용할 수 있습니다.

 

requests시 필요한 이벤트에서도 custom 인코딩을 사용합니다. 자체 인코딩을 생성하여 codecs모듈에 등록한 경우, response.encoding의 값으로 codec이름을 사용하면 요청을 디코딩으로 처리할 수 있습니다.


Binary Response Content

content속성을 통해 텍스트 이외의 요청에 대해서도 response body에 bytes로 접근할 수 있습니다.

>>> response.content
# b'[{"repository":{"open_issues":0,"url":"https://github.com/...

gzipdeflate인코딩은 자동으로 디코딩됩니다.

 

예를 들어 requests로 반환된 이진 데이터에서 이미지를 생성하려면 다음 코드를 사용할 수 있습니다.

>>> from PIL import Image
>>> from io import BytesIO

>>> image = Image.open(BytesIO(response.content))

JSON Response Content

JSON 데이터를 처리할 경우 JSON 디코더도 내장되어있어, json()함수를 통해 JSON 데이터를 디코딩할 수 있습니다.

>>> response = requests.get('https://api.github.com/events')
>>> rsponse.json()
# [{'repository': {'open_issues': 0, 'url': 'https://github.com/...

JSON 디코딩에 실패한 경우, response.json()이 예외를 발생시킵니다. 예를 들어, response에 204(No Content)가 발생하거나 response에 잘못된 JSON이 포함된 경우 response.json()을 시도하게 되면 simplejson이 설치되어 있을 경우에는 simplejson.JSONDecodeError가 발생하게 됩니다. Python 2에서는 ValueError: No JSON object could be decoded가 발생하고, Python 3에서는 json.JSONDecodeError가 발생합니다.

 

response.json()에 대한 호출의 성공이 response의 성공을 나타내지 않는다는 점을 유의해야 합니다. 일부 서버는 실패한 응답으로 JSON객체를 반환할 수 있습니다. (예: HTTP 500의 오류 세부 정보) 이러한 JSON은 디코딩되어 반환합니다. 요청이 성공적인지 확인하려면 response.raise_for_status()또는 response.status_code를 확인해봐야 합니다.


RAW Response Content

드물지만 서버에서 raw socket response를 가져오려는 경우 response.raw로 접근할 수 있습니다. 이 작업을 수행하려면 stream=True로 initial requests를 설정해야합니다. 한 번 하면 다음과 같은 작업을 수행할 수 있습니다.

>>> response = requests.get('https://api.github.com/events', stream=True)

>>> response.raw
# <urllib3.response.HTTPResponse object at 0x101194810>

>>> response.raw.read(10)
# '\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

 

일반적으로는 다음과 같은 패턴을 사용하여 파일로 스트리밍 되는 내용을 저장해야 합니다.

with open(filename, 'wb') as fd:
    for chunk in response.iter_content(chunk_size=128):
        fd.write(chunk)

response.iter_content를 사용하면 response.raw를 직접 사용할 때 처리해야 하는 많은 작업이 처리됩니다. 다운로드를 스트리밍할 때, 위에 내용은 컨텐츠를 검색하는 권장되는 방법입니다. chunk_size는 이에 더 잘 맞는 숫자로 자유롭게 조정할 수 있습니다.

 

참고: response.iter_contentresponse.raw 사용에 대한 중요한 참고사항, response.iter_content는 자동으로 gzip을 디코딩하고 인코딩을 감압합니다. response.rawbytesraw stream이며, response body를 변환하지 않습니다. 반환된 bytes에 대한 접근 권한이 필요한 경우 response.raw를 사용하십시오.

Custom Headers

requests에 HTTP Header를 추가하려면 headers 매개 변수에 dict를 전달하면 됩니다.

예를 들어, user-agent를 지정하는 경우입니다.

>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}

>>> requests.get(url, headers=headers)
참고: Custom Headers는 특정 정보 소스보다 우선 순위가 낮습니다. 예를 들어,

.netrc에 자격 증명이 지정된 경우 헤더=로 설정된 권한 부여 헤더가 재정의되고, 인증 헤더는 auth= 매개 변수에 의해 재정의됩니다. 요청은 ~/.netrc, ~/_netrc 또는 NETRC 환경 변수에 의해 지정된 경로에서 netrc 파일을 검색합니다.
Off-host로 리디렉션되면 권한 부여 헤더가 제거됩니다.
Proxy-Authorization headers는 URL에 제공된 프록시 자격 증명에 의해 재정의됩니다.
내용 길이를 결정할 수 있을 때 내용 길이 헤더가 재정의됩니다.

더 나아가 요청은 지정된 사용자 지정 헤더에 따라 동작을 전혀 변경하지 않습니다. 헤더는 단순히 최종 요청으로 전달됩니다.

참고: 모든 헤더 값은 문자열, 테스트 문자열 또는 유니코드여야 합니다. 허용되는 동안에는 유니코드 헤더 값이 전달되지 않도록 하는 것이 좋습니다.

더욱 복잡한 POST 요청

일반적으로 HTML 폼과 마찬가지로 폼 인코딩 데이터를 보내려고 합니다. 이렇게 하려면 사전을 data 인수에 전달하기만 하면 됩니다. 요청이 있을 때 데이터는 자동으로 폼 인코딩됩니다.

>>> payload = {'key1': 'value1', 'key2': 'value2'}

>>> response = requests.post("https://httpbin.org/post", data=payload)
>>> print(response.text)
# {
#   ...
#   "form": {
#     "key2": "value2",
#     "key1": "value1"
#   },
#   ...
# }

 

data 인수는 각 키에 대해 여러 값을 가질 수도 있습니다. tuples list 또는 lists을 값으로 하는 dictionary를 사용하여 데이터를 만들 수 있습니다. 이 기능은 폼에 동일한 키를 사용하는 요소가 여러 개 있을 때 특히 유용합니다.

>>> payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
>>> response_1 = requests.post('https://httpbin.org/post', data=payload_tuples)
>>> payload_dict = {'key1': ['value1', 'value2']}
>>> response_2 = requests.post('https://httpbin.org/post', data=payload_dict)
>>> print(response_1.text)
# {
#   ...
#   "form": {
#     "key1": [
#       "value1",
#       "value2"
#     ]
#   },
#   ...
# }
>>> response_1.text == response_2.text
# True

폼으로 인코딩되지 않은 데이터를 전송하려는 경우가 있습니다. dict 대신 string을 전달하면 해당 데이터가 직접 게시됩니다.

 

예를 들어 GitHub API v3는 JSON-Encoded POST/PATCH 데이터를 허용합니다.

 

>>> import json

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}

>>> requests.post(url, data=json.dumps(payload))

직접 dict를 인코딩하는 대신 json 매개 변수(버전 2.4.2에 추가됨)를 사용하여 직접 전달할 수도 있으며 이 매개 변수는 자동으로 인코딩됩니다.

 

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}

>>> requests.post(url, json=payload)

data 또는 files이 전달된 경우 json 매개 변수는 무시됩니다.

requests에서 json 매개 변수를 사용하면 헤더의 Content-TypeApplication/json으로 변경됩니다.

 


POST a Multipart-Encoded File

Requests 을 통해 Multipart-encoded 파일을 쉽게 얻을 수 있습니다.

 

>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}

>>> response = requests.post(url, files=files)
>>> response.text
# {
#   ...
#   "files": {
#     "file": "<censored...binary...data>"
#   },
#   ...
# }

filename, content_type 및 headers를 명시적으로 설정할 수 있습니다.

>>> url = 'https://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}

>>> response = requests.post(url, files=files)
>>> response.text
# {
#   ...
#   "files": {
#     "file": "<censored...binary...data>"
#   },
#   ...
# }

원하는 경우 파일로 수신할 문자열을 보낼 수 있습니다.

>>> url = 'https://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}

>>> response = requests.post(url, files=files)
>>> response.text
# {
#   ...
#   "files": {
#     "file": "some,data,to,send\\nanother,row,to,send\\n"
#   },
#   ...
# }

대용량 파일을 multipart/form-data요청으로 게시하는 경우 요청을 스트리밍할 수 있습니다. 기본적으로 requests에서는 이 기능을 지원하지 않지만, requests-toolbelt를 지원하는 별도의 패키지가 있습니다. requests-toolbelt의 사용 방법에 대한 자세한 내용은 requests-toolbelt의 설명서를 읽어야 합니다.

 

여러 파일을 하나의 요청으로 보내려면 advanced section을 참조하세요.

경고: 파일을 이진 모드로 여는 것이 좋습니다. 그 이유는 요청이 사용자에게 내용 길이 헤더를 제공하려고 시도할 수 있으며, 이 값이 파일의 바이트 수로 설정되기 때문입니다. 텍스트 모드에서 파일을 열면 오류가 발생할 수 있습니다.

Status Code

HTTP로 호출하게 된다면 반드시 상태코드를 응답 받게 됩니다. 일반적으로 이 상태코드를 보고 요청이 잘 처리가 되었는지 알 수 있습니다.

이에 대해 자세한 내용은 https://developer.mozilla.org/ko/docs/Web/HTTP/Status 에서 확인할 수 있습니다.

 

상태코드는 response객체의 status_code 속성을 통해 간단하게 얻을 수 있습니다.

>>> response = requests.get('https://httpbin.org/get')
>>> response.status_code
# 200

또한 requests에는 쉽게 참조할 수 있도록 상태 코드 조회 개체가 내장되어 있습니다.

>>> response.status_code == requests.codes.ok
# True

만약 잘못된 요청(4XX 클라이언트 오류 또는 5XX 서버 오류 응답)을 한 경우 Response.raise_for_status()로 가져올 수 있습니다.

>>> bad_response = requests.get('https://httpbin.org/status/404')
>>> bad_response.status_code
# 404

>>> bad_response.raise_for_status()
# Traceback (most recent call last):
#   File "requests/models.py", line 832, in raise_for_status
#     raise http_error
# requests.exceptions.HTTPError: 404 Client Error

하지만 responsestatus_code200이었으므로, response에 대한 raise_for_status()를 가져오면,

>>> response.raise_for_status()
# None

잘 되고 있는 것을 확인할 수 있습니다.


Response Headers

Python dictionary를 사용하여 서버의 response headers를 볼 수 있습니다.

>>> response.headers
# {
#     'content-encoding': 'gzip',
#     'transfer-encoding': 'chunked',
#     'connection': 'close',
#     'server': 'nginx/1.0.4',
#     'x-runtime': '148ms',
#     'etag': '"e1ca502697e5c9317743dc078f67693f"',
#     'content-type': 'application/json'
# }

이 response dictionary는 HTTP Headers 전용이지만 특별합니다.

RFC 7230에 따르면, HTTP Header 이름은 대소문자를 구분하지 않습니다.

 

따라서 대문자화(capitalization)를 사용하여 원하는 headers에 접근할 수 있습니다.

>>> response.headers['Content-Type']
# 'application/json'

>>> response.headers.get('content-type')
# 'application/json'

서버가 동일한 헤더를 다른 값으로 여러 번 보낼 수 있다는 점에서도 특별하지만, requests는 RFC 7230에 따라 단일 매핑 내에서 사전에 표현될 수 있도록 이들을 결합합니다.

수신자는 각 필드 값을 쉼표로 구분하여 각 필드 값을 조합된 필드 값에 순서대로 추가하여 메시지의 의미를 변경하지 않고 동일한 필드 이름을 가진 여러 헤더 필드를 하나의 "filed-name:filed-value" 쌍으로 결합할 수 있습니다.

Cookies

response에 일부 쿠키가 포함된 경우 쿠키에 빠르게 액세스할 수 있습니다.

>>> url = 'http://example.com/some/cookie/setting/url'
>>> response = requests.get(url)

>>> response.cookies['example_cookie_name']
# 'example_cookie_value'

자신의 쿠키를 서버로 보내려면 cookies 매개 변수를 사용할 수 있습니다.

>>> url = 'https://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')

>>> response = requests.get(url, cookies=cookies)
>>> response.text
# '{"cookies": {"cookies_are": "working"}}'

쿠키는 RequestsCookieJar로 반환되며, 이는 dict처럼 동작하지만 여러 도메인 또는 경로에서 사용하기에 적합한 보다 완벽한 인터페이스를 제공합니다. Cookie jars를 다음 requests에도 전달할 수 있습니다.

>>> jar = requests.cookies.RequestsCookieJar()
>>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
>>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
>>> url = 'https://httpbin.org/cookies'
>>> response = requests.get(url, cookies=jar)
>>> response.text
# '{"cookies": {"tasty_cookie": "yum"}}'

Redirection and History

기본적으로 requests는 HEAD를 제외한 모든 동사에 대해 location redirection을 수행합니다.

 

requests 객체의 history 속성을 사용하여 redirection을 추적할 수 있습니다.

 

requests.history list에는 requests을 완료하기 위해 생성된 response 객체가 포함됩니다. list는 가장 오래된 requests에서 가장 최근의 requests순으로 정렬됩니다.

 

예를 들어 GitHub는 모든 HTTP 요청을 HTTPS로 리디렉션합니다.

>>> response = requests.get('http://github.com/')

>>> response.url
# 'https://github.com/'

>>> response.status_code
# 200

>>> response.history
# [<Response [301]>]

만약 GET, OPTIONS, POST, PUT, PATCH 또는 DELETE를 사용하는 경우 allow_readirects 매개 변수를 사용하여 리디렉션 처리를 비활성화할 수 있습니다.

>>> response = requests.get('http://github.com/', allow_redirects=False)

>>> response.status_code
# 301

>>> response.history
# []

만약 HEAD를 사용하는 경우 리디렉션을 활성화할 수도 있습니다.

>>> response = requests.head('http://github.com/', allow_redirects=True)

>>> response.url
# 'https://github.com/'

>>> response.history
# [<Response [301]>]

Timeouts

시간 초과 매개 변수를 사용하여 지정된 시간(초) 후에 응답 대기 중지를 요청하도록 지정할 수 있습니다. 거의 모든 프로덕션 코드는 거의 모든 요청에서 이 매개 변수를 사용해야 합니다. 그렇지 않으면 프로그램이 무기한 중단될 수 있습니다.

>>> requests.get('https://github.com/', timeout=0.001)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
timeout은 전체 응답 다운로드에 대한 시간 제한이 아닙니다. 오히려 서버가 시간 초과(더 정확하게는 기본 소켓에서 시간 초과 동안 바이트를 수신하지 않은 경우)에 대해 응답을 발행하지 않은 경우 예외가 발생합니다. timeout를 명시적으로 지정하지 않으면 requests가 timeout되지 않습니다.

Errors and Exceptions

네트워크 문제(예: DNS 오류, 거부된 연결 등)가 발생할 경우, requests에서 ConnectionError 예외가 발생합니다.

HTTP 요청이 실패한 상태 코드를 반환한 경우 response.raise_for_status()가 HTTP 오류를 발생시킵니다.

만약 requests가 timeout되면 timeout exception이 발생합니다.

requests가 구성된 최대 리디렉션 수를 초과하면 TooManyRedirects exception가 발생합니다.

requests가 명시적으로 발생하는 모든 예외는 requests.exceptions.RequestException에서 상속됩니다.

 

더 자세한 내용은 https://docs.python-requests.org/en/master/user/quickstart/

 

Quickstart — Requests 2.25.1 documentation

Eager to get started? This page gives a good introduction in how to get started with Requests. Let’s get started with some simple examples. Passing Parameters In URLs You often want to send some sort of data in the URL’s query string. If you were const

docs.python-requests.org

에서 확인하세요.

반응형