ValueError Content-Type header is text/html; charset=utf-8 not application/json

requests는 파이썬으로 HTTP 통신이 필요한 프로그램을 작성할 때 가장 많이 사용되는 라이브러리입니다. 특히 원격에 있는 API를 호출할 때 유용하게 사용할 수 있는데요. 이번 포스팅에서는 requests 라이브러리를 사용하는 방법에 대해서 알아보겠습니다.

패키지 설치

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

$ pip install requests Collecting requests Downloading requests-2.25.0-py2.py3-none-any.whl (61 kB) |████████████████████████████████| 61 kB 3.1 MB/s Collecting urllib3<1.27,>=1.21.1 Downloading urllib3-1.26.2-py2.py3-none-any.whl (136 kB) |████████████████████████████████| 136 kB 6.1 MB/s Collecting certifi>=2017.4.17 Downloading certifi-2020.12.5-py2.py3-none-any.whl (147 kB) |████████████████████████████████| 147 kB 4.9 MB/s Collecting idna<3,>=2.5 Downloading idna-2.10-py2.py3-none-any.whl (58 kB) |████████████████████████████████| 58 kB 8.6 MB/s Collecting chardet<4,>=3.0.2 Downloading chardet-3.0.4-py2.py3-none-any.whl (133 kB) |████████████████████████████████| 133 kB 6.7 MB/s Installing collected packages: urllib3, certifi, idna, chardet, requests Successfully installed certifi-2020.12.5 chardet-3.0.4 idna-2.10 requests-2.25.0 urllib3-1.26.2

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

$ python Python 3.9.0 (default, Oct 17 2020, 15:32:58) [Clang 11.0.3 (clang-1103.0.32.62)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import requests >>> requests.get("//google.com") <Response [200]>

requests 라이브러리로 구글에 접속을 해보니 상태 코드 200이 응답이 되는 것을 볼 수 있습니다. 🎉

requests 라이브러리는 매우 직관적인 API를 제공하는데요. 어떤 방식(method)의 HTTP 요청을 하느냐에 따라서 해당하는 이름의 함수를 사용하면 됩니다.

  • GET 방식: requests.get()
  • POST 방식: requests.post()
  • PUT 방식: requests.put()
  • DELETE 방식: requests.delete()

응답 상태

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

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

>>> response = requests.get("//jsonplaceholder.typicode.com/users/1") >>> response.status_code 200 >>> response = requests.get("//jsonplaceholder.typicode.com/users/100") >>> response.status_code 404 >>> response = requests.post("//jsonplaceholder.typicode.com/users") >>> response.status_code 201

응답 전문

요청이 정상적으로 처리가 된 경우, 응답 전문(response body/payload)에 요청한 데이터가 담겨져 옵니다. 응답 전문은 크게 3가지 방식으로 읽어올 수 있습니다.

>>> response = requests.get("//jsonplaceholder.typicode.com/users/1")

첫 번째는 content 속성을 통해 바이너리 원문을 얻을 수 있습니다.

>>> response.content b'{\n "id": 1,\n "name": "Leanne Graham",\n "username": "Bret",\n "email": "",\n "address": {\n "street": "Kulas Light",\n "suite": "Apt. 556",\n "city": "Gwenborough",\n "zipcode": "92998-3874",\n "geo": {\n "lat": "-37.3159",\n "lng": "81.1496"\n }\n },\n "phone": "1-770-736-8031 x56442",\n "website": "hildegard.org",\n "company": {\n "name": "Romaguera-Crona",\n "catchPhrase": "Multi-layered client-server neural-net",\n "bs": "harness real-time e-markets"\n }\n}'

두 번째는 text 속성을 통해 UTF-8로 인코딩된 문자열을 얻을 수 있습니다.

>>> response.text '{\n "id": 1,\n "name": "Leanne Graham",\n "username": "Bret",\n "email": "",\n "address": {\n "street": "Kulas Light",\n "suite": "Apt. 556",\n "city": "Gwenborough",\n "zipcode": "92998-3874",\n "geo": {\n "lat": "-37.3159",\n "lng": "81.1496"\n }\n },\n "phone": "1-770-736-8031 x56442",\n "website": "hildegard.org",\n "company": {\n "name": "Romaguera-Crona",\n "catchPhrase": "Multi-layered client-server neural-net",\n "bs": "harness real-time e-markets"\n }\n}'

마지막으로, 응답 데이터가 JSON 포멧이라면 json() 함수를 통해 사전(dictionary) 객체를 얻을 수 있습니다.

>>> response.json() {'id': 1, 'name': 'Leanne Graham', 'username': 'Bret', 'email': '', 'address': {'street': 'Kulas Light', 'suite': 'Apt. 556', 'city': 'Gwenborough', 'zipcode': '92998-3874', 'geo': {'lat': '-37.3159', 'lng': '81.1496'}}, 'phone': '1-770-736-8031 x56442', 'website': 'hildegard.org', 'company': {'name': 'Romaguera-Crona', 'catchPhrase': 'Multi-layered client-server neural-net', 'bs': 'harness real-time e-markets'}}

응답 헤더

응답에 대한 메타 데이터를 담고 있는 응답 헤더는 headers 속성을 통해 사전의 형태로 얻을 수 있습니다.

>>> response = requests.get("//jsonplaceholder.typicode.com/users/1") >>> response.headers {'Date': 'Sat, 12 Dec 2020 21:10:49 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Set-Cookie': '__cfduid=d92df672c0d58b06b245dd0b8d317e2141607807449; expires=Mon, 11-Jan-21 21:10:49 GMT; path=/; domain=.typicode.com; HttpOnly; SameSite=Lax', 'X-Powered-By': 'Express', 'X-Ratelimit-Limit': '1000', 'X-Ratelimit-Remaining': '999', 'X-Ratelimit-Reset': '1607798104', 'Vary': 'Origin, Accept-Encoding', 'Access-Control-Allow-Credentials': 'true', 'Cache-Control': 'max-age=43200', 'Pragma': 'no-cache', 'Expires': '-1', 'X-Content-Type-Options': 'nosniff', 'Etag': 'W/"1fd-+2Y3G3w049iSZtw5t1mzSnunngE"', 'Via': '1.1 vegur', 'CF-Cache-Status': 'HIT', 'Age': '9352', 'cf-request-id': '06fa63d142000038668b9a7000000001', 'Expect-CT': 'max-age=604800, report-uri="//report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', 'Report-To': '{"endpoints":[{"url":"\\/\\/a.nel.cloudflare.com\\/report?s=Vk2e8Q%2FBK4wOSQc12Cgs0nqvZHpyUb4c0o%2BWCgLzwdqPLQM2HJ5hNHW%2Fmc8fNr73wqQ2LrzEdScfv5bejTYVrXRlUfAyPcGnv1me%2BHlZKc7IKKOi%2BO%2ButGVrdkWS"}],"group":"cf-nel","max_age":604800}', 'NEL': '{"report_to":"cf-nel","max_age":604800}', 'Server': 'cloudflare', 'CF-RAY': '600a6f2edfe23866-IAD', 'Content-Encoding': 'gzip'} >>> response.headers['Content-Type'] 'application/json; charset=utf-8'

요청 쿼리

GET 방식으로 HTTP 요청을 할 때는 쿼리 스트링(query string)을 통해 응답받을 데이터를 필터링하는 경우가 많습니다.

params 옵션을 사용하면 쿼리 스크링을 사전의 형태로 넘길 수 있습니다.

# //jsonplaceholder.typicode.com/posts?userId=1 >>> response = requests.get("//jsonplaceholder.typicode.com/posts", params={"userId": "1"}) >>> [post["id"] for post in response.json()] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

요청 전문

POST나 PUT 방식으로 HTTP 요청을 할 때는 보통 요청 전문(request body/payload)에 데이터를 담아서 보내는데요.

data 옵션을 사용하면, HTML 양식(form) 포멧의 데이터를 전송할 수 있으며, 이 때 Content-Type 요청 헤더는 application/x-www-form-urlencoded로 자동 설정됩니다.

>>> requests.post("//jsonplaceholder.typicode.com/users", data={'name': 'Test User'}) <Response [201]>

json 옵션을 사용하면, REST API로 JSON 포멧의 데이터를 전송할 수 있으며, 이 때 Content-Type 요청 헤더는 application/json로 자동 설정됩니다.

>>> requests.post("//jsonplaceholder.typicode.com/users", json={'name': 'Test User'}) <Response [201]>

요청 헤더

headers 옵션을 사용하면 요청 헤더도 직접 설정할 수 있는데요. 인증 토큰을 보낼 때 유용하게 사용할 수 있습니다.

>>> requests.post("//jsonplaceholder.typicode.com/users", headers={'Authorization': 'Bearer 12345'}) <Response [201]>

전체 코드

본 포스팅에서 제가 작성한 코드는 아래에서 직접 확인하고 실행해보실 수 있습니다.

//dales.link/wxd

마치면서

이상으로 파이썬의 requests 라이브러리의 기본적인 사용 방법을 살펴보았습니다. 파이썬에 내장된 모듈은 아니지만 HTTP 호출을 할 때 거의 표준처럼 사용되는 라이브러리이기 때문에 사용법을 잘 숙지해두시면 큰 도움이 되실 겁니다.

Toplist

Neuester Beitrag

Stichworte