Ошибка response python

Python request module is a simple and elegant Python HTTP library. It provides methods for accessing Web resources via HTTP. In the following article, we will use the HTTP GET method in the Request module. This method requests data from the server and the Exception handling comes in handy when the response is not successful. Here, we will go through such situations. We will use Python’s try and except functionality to explore the exceptions that arise from the Requests module.

  • url: Returns the URL of the response
  • raise_for_status(): If an error occur, this method returns a HTTPError object
  • request: Returns the request object that requested this response
  • status_code: Returns a number that indicates the status (200 is OK, 404 is Not Found)
     

Successful Connection Request

The first thing to know is that the response code is 200 if the request is successful.

Python3

Output:

200

Exception Handling for HTTP Errors

Here, we tried the following URL sequence and then passed this variable to the Python requests module using raised_for_status(). If the try part is successful, we will get the response code 200, if the page that we requested doesn’t exist. This is an HTTP error, which was handled by the Request module’s exception HTTPError and you probably got the error 404.

Python3

import requests

try:

    r = requests.get(url, timeout=1)

    r.raise_for_status()

except requests.exceptions.HTTPError as errh:

    print("HTTP Error")

    print(errh.args[0])

print(r)

Output:

HTTP Error
404 Client Error: Not Found for url: https://www.amazon.com/nothing_here
<Response [404]>

General Exception Handling

You could also use a general exception from the Request module. That is requests.exceptions.RequestException.

Python3

try:

    r = requests.get(url, timeout=1)

    r.raise_for_status()

except requests.exceptions.RequestException as errex:

    print("Exception request")

Output:

Exception request 

Now, you may have noticed that there is an argument ‘timeout’ passed into the Request module. We could prescribe a time limit for the requested connection to respond. If this has not happened, we could catch that using the exception requests.exceptions.ReadTimeout. To demonstrate this let us find a website that responds successfully.

Python3

import requests

try:

    r = requests.get(url, timeout=1)

    r.raise_for_status()

except requests.exceptions.ReadTimeout as errrt:

    print("Time out")

print(r)

Output:

<Response [200]>

If we change timeout = 0.01, the same code would return, because the request could not possibly be that fast.

Time out
<Response [200]>

Exception Handling for Missing Schema

Another common error is that we might not specify HTTPS or HTTP in the URL. For example, We cause use requests.exceptions.MissingSchema to catch this exception.

Python3

url = "www.google.com"

try:

    r = requests.get(url, timeout=1)

    r.raise_for_status()

except requests.exceptions.MissingSchema as errmiss:

    print("Missing schema: include http or https")

except requests.exceptions.ReadTimeout as errrt:

    print("Time out")

Output:

Missing scheme: include http or https

Exception Handling for Connection Error

Let us say that there is a site that doesn’t exist. Here, the error will occur even when you can’t make a connection because of the lack of an internet connection

Python3

try:

  r = requests.get(url, timeout = 1, verify = True)

  r.raise_for_status()

except requests.exceptions.HTTPError as errh:

  print("HTTP Error")

  print(errh.args[0])

except requests.exceptions.ReadTimeout as errrt:

  print("Time out")

except requests.exceptions.ConnectionError as conerr:

  print("Connection error")

Output:

Connection error

Putting Everything Together

Here, We put together everything we tried so far the idea is that the exceptions are handled according to the specificity. 

For example, url =  “https://www.gle.com”,  When this code is run for this URL will produce an Exception request. Whereas, In the absence of connection requests.exceptions.ConnectionError will print the Connection Error, and when the connection is not made the general exception is handled by requests.exceptions.RequestException.

Python3

try:

    r = requests.get(url, timeout=1, verify=True)

    r.raise_for_status()

except requests.exceptions.HTTPError as errh:

    print("HTTP Error")

    print(errh.args[0])

except requests.exceptions.ReadTimeout as errrt:

    print("Time out")

except requests.exceptions.ConnectionError as conerr:

    print("Connection error")

except requests.exceptions.RequestException as errex:

    print("Exception request")

Output:

Note: The output may change according to requests.

 Time out

Last Updated :
23 Jan, 2023

Like Article

Save Article

I’d like to raise a Python-standard exception when an HTTP response code from querying an API is not 200, but what specific exception should I use? For now I raise an OSError:

if response.status_code != 200:
  raise OSError("Response " + str(response.status_code)
                  + ": " + response.content)

I’m aware of the documentation for built-in exceptions.

asked Jun 15, 2015 at 22:01

BoltzmannBrain's user avatar

BoltzmannBrainBoltzmannBrain

4,97210 gold badges45 silver badges77 bronze badges

1

You can simply call Response.raise_for_status() on your response:

>>> import requests
>>> url = 'http://stackoverflow.com/doesnt-exist'
>>> r = requests.get(url)
>>>
>>> print r.status_code
404
>>> r.raise_for_status()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "requests/models.py", line 831, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found

This will raise a requests.HTTPError for any 4xx or 5xx response.

See the docs on Response Status Code for a more complete example.


Note that this does not exactly do what you asked (status != 200): It will not raise an exception for 201 Created or 204 No Content, or any of the 3xx redirects — but this is most likely the behavior you want: requests will just follow the redirects, and the other 2xx are usually just fine if you’re dealing with an API.

answered Jun 15, 2015 at 22:21

Lukas Graf's user avatar

The built-in Python exceptions are probably not a good fit for what you are doing. You will want to subclass the base class Exception, and throw your own custom exceptions based on each scenario you want to communicate.

A good example is how the Python Requests HTTP library defines its own exceptions:

In the event of a network problem (e.g. DNS failure, refused
connection, etc), Requests will raise a ConnectionError exception.

In the rare event of an invalid HTTP response, Requests will raise an
HTTPError exception.

If a request times out, a Timeout exception is raised.

If a request exceeds the configured number of maximum redirections, a
TooManyRedirects exception is raised.

All exceptions that Requests explicitly raises inherit from
requests.exceptions.RequestException.

answered Jun 15, 2015 at 22:10

Martin Konecny's user avatar

Martin KonecnyMartin Konecny

57.2k19 gold badges137 silver badges157 bronze badges

3

Содержание

  1. Введение в тему
  2. Создание get и post запроса
  3. Передача параметров в url
  4. Содержимое ответа response
  5. Бинарное содержимое ответа
  6. Содержимое ответа в json
  7. Необработанное содержимое ответа
  8. Пользовательские заголовки
  9. Более сложные post запросы
  10. Post отправка multipart encoded файла
  11. Коды состояния ответа
  12. Заголовки ответов
  13. Cookies
  14. Редиректы и история
  15. Тайм ауты
  16. Ошибки и исключения

Введение в тему

Модуль python requests – это общепринятый стандарт для работы с запросами по протоколу HTTP.

Этот модуль избавляет Вас от необходимости работать с низкоуровневыми деталями. Работа с запросами становится простой и элегантной.

В этом уроке будут рассмотрены самые полезные функций библиотеки requests и различные способы их использования.

Перед использованием модуля его необходимо установить:

Создание get и post запроса

Сперва необходимо добавить модуль Requests в Ваш код:

Создадим запрос и получим ответ, содержащий страницу и все необходимые данные о ней.


import requests

response = requests.get('https://www.google.ru/')

В переменную response попадает ответ на запрос. Благодаря этому объекту можно использовать любую информацию, относящуюся к этому ответу.

Сделать POST запрос так же очень просто:


import requests

 

response = requests.post('https://www.google.ru/', data = {'foo':3})

Другие виды HTTP запросов, к примеру: PUT, DELETE, и прочих, выполнить ничуть не сложнее:


import requests

 

response = requests.put('https://www.google.ru/', data = {'foo':3})

response = requests.delete('https://www.google.ru/')

response = requests.head('https://www.google.ru/')

response = requests.options('https://www.google.ru/')

Передача параметров в url

Иногда может быть необходимо отправить различные данные вместе с запросом URL. При ручной настройке URL, параметры выглядят как пары ключ=значение после знака «?». Например, https://www.google.ru/search?q=Python. Модуль Requests предоставляет возможность передать эти параметры как словарь, применяя аргумент params. Если вы хотите передать q = Python и foo=’bar’ ресурсу google.ru/search, вы должны использовать следующий код:


import requests

params_dict = {'q':'Python', 'foo':'bar'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.url)

#Вывод:

https://www.google.ru/search?q=Python&foo=bar

Здесь мы видим, что URL был сформирован именно так, как это было задумано.

Пара ключ=значение, где значение равняется None, не будет добавлена к параметрам запроса URL.

Так же есть возможность передавать в запрос список параметров:


import requests

params_dict = {'q':'Python', 'foo':['bar', 'eggs']}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.url)
#Вывод:

https://www.google.ru/search?q=Python&foo=bar&foo=eggs

Содержимое ответа response

Код из предыдущего листинга создаёт объект Response, содержащий ответ сервера на наш запрос. Обратившись к его атрибуту .url можно просмотреть адрес, куда был направлен запрос. Атрибут .text позволяет просмотреть содержимое ответа. Вот как это работает:


import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.text)
#Вывод:<!doctype html><html lang="ru"><head><meta charset="UTF-8"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png"…

Библиотека автоматически пытается определить кодировку ответа основываясь на его заголовках. Узнать, какую кодировку выбрал модуль, можно следующим образом:


import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.encoding)
#Вывод:

windows-1251

Можно так же самостоятельно установить кодировку используя атрибут .encoding.


import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
response.encoding = 'utf-8' # указываем необходимую кодировку вручную
print(response.encoding)
#Вывод:

utf-8

Бинарное содержимое ответа

Существует возможность просмотра ответа в виде байтов:


import requests

params_dict = {'q':'Python'}
response = requests.get('https://www.google.ru/search', params=params_dict)
print(response.content)
#Вывод:

b'<!doctype html><html lang="ru"><head><meta charset="UTF-8"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" …

При передаче со сжатием ответ автоматически декодируется для Вас.

Содержимое ответа в json

Так же в Requests есть встроенная обработка ответов в формате JSON:

import requests
import json

response = requests.get(‘http://api.open-notify.org/astros.json’)
print(json.dumps(response.json(), sort_keys=True, indent=4))
#Вывод:

{

«message»: «success»,

«number»: 10,

«people»: [

{

«craft»: «ISS»,

«name»: «Mark Vande Hei»

},

{

«craft»: «ISS»,

«name»: «Oleg Novitskiy»

},

[/dm_code_snippet]

Если ответ не является JSON, то .json выбросит исключение:


import requests
import json

response = requests.get('https://www.google.ru/search')
print(json.dumps(response.json(), sort_keys=True, indent=4))
#Вывод:

…

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Необработанное содержимое ответа

Если Вам нужно получить доступ к ответу сервера в чистом виде на уровне сокета, обратитесь к атрибуту .raw. Для этого необходимо указать параметр stream=True в запросе. Этот параметр заставляет модуль читать данные по мере их прибытия.


import requests

response = requests.get('https://www.google.ru/', stream=True)
print(response.raw)
print('Q'*10)
print(response.raw.read(15))
#Вывод:

<urllib3.response.HTTPResponse object at 0x000001E368771FA0>

QQQQQQQQQQ

b'x1fx8bx08x00x00x00x00x00x02xffxc5[[sxdb'

Так же можно использовать метод .iter_content. Этот метод итерирует данные потокового ответа и это позволяет избежать чтения содержимого сразу в память для больших ответов. Параметр chunk_size – это количество байтов, которые он должен прочитать в памяти.  Параметр chunk_size можно произвольно менять.


import requests

response = requests.get('https://www.google.ru/', stream=True)
print(response.iter_content)
print('Q'*10)
print([i for i in response.iter_content(chunk_size=256)])
#Вывод:

<bound method Response.iter_content of <Response [200]>>

QQQQQQQQQQ

[b'<!doctype html><html itemscope="" itemtype="http://sche', b'ma.org/WebPage" lang="ru"><head><meta content=…

response.iter_content будет автоматически декодировать сжатый ответ. Response.raw — чистый набор байтов, неизменённое содержимое ответа.

Пользовательские заголовки

Если необходимо установить заголовки в HTTP запросе, передайте словарь с ними в параметр headers. Значения заголовка должны быть типа string, bytestring или unicode. Имена заголовков не чувствительны к регистру символов.
В следующем примере мы устанавливаем информацию об используемом браузере:


import requests

response = requests.get('https://www.google.ru/', headers={'user-agent': 'unknown_browser'})
print(response.request.headers)
# Вывод:

{'user-agent': 'unknown_browser', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

Более сложные post запросы

Существует способ отправить данные так, будто это результат заполнения формы на сайте:


import requests

response = requests.post('https://httpbin.org/post', data={'foo': 'bar'})
print(response.text)
# Вывод:

{

"args": {},

"data": "",

"files": {},

"form": {

"foo": "bar"

},

"headers": {

…

Параметр data может иметь произвольное количество значений для каждого ключа. Для этого необходимо указать data в формате кортежа, либо в виде dict со списками значений.


import requests

response = requests.post('https://httpbin.org/post', data={'foo':['bar', 'eggs']})
print(response.json()['form'])
print('|'*10)
response = requests.post('https://httpbin.org/post', data=[('foo', 'bar'), ('foo', 'eggs')])
print(response.json()['form'])
# Вывод:

{'foo': ['bar', 'eggs']}

||||||||||

{'foo': ['bar', 'eggs']}

Если нужно отправить данные, не закодированные как данные формы, то передайте в запрос строку вместо словаря. Тогда данные отправятся в изначальном виде.


import requests

response = requests.post('https://httpbin.org/post', data={'foo': 'bar'})
print('URL:', response.request.url)
print('Body:', response.request.body)
print('-' * 10)
response = requests.post('https://httpbin.org/post', data='foo=bar')
print('URL:', response.request.url)
print('Body:', response.request.body)
# Вывод:

URL: https://httpbin.org/post

URL: https://httpbin.org/post

Body: foo=bar

----------

URL: https://httpbin.org/post

Body: foo=bar

Post отправка multipart encoded файла

Запросы упрощают загрузку файлов с многостраничным кодированием (Multipart-Encoded):


import requests

url = 'https://httpbin.org/post'

files = {'file': open('report.xls', 'rb')}

response = requests.post(url, files=files)

print(response.text)

# Вывод:

{

...

"files": {

"file": "<censored...binary...data>"

},

...

}

Вы можете установить имя файла, content_type и заголовки в явном виде:


import requests

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)

print(response.text)

# Вывод:

{

...

"files": {

"file": "<censored...binary...data>"

},

...

}

Можете отправить строки, которые будут приняты в виде файлов:


import requests

url = 'https://httpbin.org/post'

files = {'file': ('report.csv', 'some,data,to,sendnanother,row,to,sendn')}

response = requests.post(url, files=files)

print(response.text)

# Вывод:

{

...

"files": {

"file": "some,data,to,send\nanother,row,to,send\n"

},

...

}

Коды состояния ответа

Возможно, наиболее важные данные (первые – уж точно), которые вы можете получить, используя библиотеку requests, является код состояния ответа.

Так, 200 статус означает, что запрос выполнен успешно, тогда как 404 статус означает, что ресурс не найден.

Важнее всего то, с какой цифры начинается код состояния:

  • 1XX — информация
  • 2XX — успешно
  • 3XX — перенаправление
  • 4XX — ошибка клиента (ошибка на нашей стороне)
  • 5XX — ошибка сервера (самые страшные коды для разработчика)

Используя атрибут .status_code можно получить статус, который вернул сервер:


import requests

response = requests.get('https://www.google.ru/')
print(response.status_code)

# Вывод:

200

.status_code вернул 200 — это означает, что запрос успешно выполнен и сервер вернул запрашиваемые данные.

При желании, такую информацию можно применить в Вашем Пайтон скрипте для принятия решений:


import requests

response = requests.get('https://www.google.ru/')
if response.status_code == 200:    print('Успех!')elif response.status_code == 404:    print('Страница куда-то пропала…')

# Вывод:

Успех!

Если код состояния response равен 200, то скрипт выведет «Успех!», но, если он равен 404, то скрипт вернёт «Страница куда-то пропала…».

Если применить модуль Response в условном выражении и проверить логическое значение его экземпляра (if response) то он продемонстрирует значение True, если код ответа находится в диапазоне между 200 и 400, и False во всех остальных случаях.

Упростим код из предыдущего примера:


import requests

response = requests.get('https://www.google.ru/fake/')
if response:
print('Успех!')
else:
print('Хьюстон, у нас проблемы!')
# Вывод:

Хьюстон, у нас проблемы!

Данный способ не проверяет, что код состояния равен именно 200.
Причиной этого является то, что response с кодом в диапазоне от 200 до 400, такие как 204 и 304, тоже являются успешными, ведь они возвращают обрабатываемый ответ. Следовательно, этот подход делит все запросы на успешные и неуспешные – не более того. Во многих случаях Вам потребуется более детальная обработка кодов состояния запроса.

Вы можете вызвать exception, если requests.get был неудачным. Такую конструкцию можно создать вызвав .raise_for_status() используя конструкцию try- except:


import requests

from requests.exceptions import HTTPError

for url in ['https://www.google.ru/', 'https://www.google.ru/invalid']:
try:
response = requests.get(url)

response.raise_for_status()
except HTTPError:
print(f'Возникла ошибка HTTP: {HTTPError}')
except Exception as err:
print(f'Возникла непредвиденная ошибка: {err}')
else:
print('Успех!')
# Вывод:

Успех!

Возникла ошибка HTTP: <class 'requests.exceptions.HTTPError'>

Заголовки ответов

Мы можем просматривать заголовки ответа сервера:


import requests

response = requests.get('https://www.google.ru/')
print(response.headers)
# Вывод:

{'Date': 'Sun, 27 Jun 2021 13:43:17 GMT', 'Expires': '-1', 'Cache-Control': 'private, max-age=0', 'Content-Type': 'text/html; charset=windows-1251', 'P3P': 'CP="This is not a P3P policy! See g.co/p3phelp for more info."', 'Content-Encoding': 'gzip', 'Server': 'gws', 'X-XSS-Protection': '0', 'X-Frame-Options': …

Cookies

Можно просмотреть файлы cookie, которые сервер отправляет вам обратно с помощью атрибута .cookies. Запросы также позволяют отправлять свои собственные cookie-файлы.

Чтобы добавить куки в запрос, Вы должны использовать dict, переданный в параметр cookie.


import requests

url = 'https://www.google.ru/'
headers = {'user-agent': 'your-own-user-agent/0.0.1'}
cookies = {'visit-month': 'February'}

response = requests.get(url, headers=headers, cookies=cookies)

print(response.request.headers)
# Вывод:

{'user-agent': 'your-own-user-agent/0.0.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'visit-month=February'}

Редиректы и история

По умолчанию модуль Requests выполняет редиректы для всех HTTP глаголов, кроме HEAD.

Существует возможность использовать параметр history объекта Response, чтобы отслеживать редиректы.

Например, GitHub перенаправляет все запросы HTTP на HTTPS:


import requests

response = requests.get('https://www.google.ru/')
print(response.url)
print(response.status_code)
print(response.history)
# Вывод:

https://www.google.ru/

200

[]

Тайм ауты

Так же легко можно управлять тем, сколько программа будет ждать возврат response. Время ожидания задаётся параметром timeout. Это очень важный параметр, так как, если его не использовать, написанный Вами скрипт может «зависнуть» в вечном ожидании ответа от сервера. Используем предыдущий код:


import requests

response = requests.get(‘https://www.google.ru/’, timeout=0.001)
print(response.url)
print(response.status_code)
print(response.history)
# Вывод:

raise ConnectTimeout(e, request=request)

requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host=’www.google.ru’, port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000001E331681C70>, ‘Connection to www.google.ru timed out. (connect timeout=0.001)’))

Модуль не ждёт полной загрузки ответа. Исключение возникает, если сервер не отвечает (хотя бы один байт) за указанное время.

Ошибки и исключения

Если возникнет непредвиденная ситуация – ошибка соединения, модуль Requests выбросит эксепшн ConnectionError.

response.raise_for_status() возвращает объект HTTPError, если в процессе произошла ошибка. Его применяют для отладки модуля и, поэтому, он является неотъемлемой частью запросов Python.

Если выйдет время запроса, вызывается исключение Timeout. Если слишком много перенаправлений, то появится исключение TooManyRedirects.


Готовы начать? Эта статья дает достаточное представление о том, как начать работу с Requests. Давайте начнем с нескольких простых примеров.

Создание запроса

Создание запроса с помощью Requests — это очень просто. Начните с импорта модуля:

>>> import requests

Теперь попробуем получить веб-страницу. Например, давайте получим публичный тайм-лайн GitHub.

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

Теперь у нас есть объект Response с именем r. Мы можем получить всю необходимую информацию из этого объекта.

Простой API Requests означает, что все формы HTTP запросов являются очевидными. Например, вот как вы можете сделать HTTP POST запрос:

>>> r = requests.post("http://httpbin.org/post")

Круто? А как насчет других типов HTTP запроса: PUT, DELETE, HEAD и OPTIONS? Их выполнить так же просто:

>>> r = requests.put("http://httpbin.org/put")
>>> r = requests.delete("http://httpbin.org/delete")
>>> r = requests.head("http://httpbin.org/get")
>>> r = requests.options("http://httpbin.org/get")

Это уже хорошо. Даже здорово. Но это далеко не все из того, что может делать Requests.

Передача параметров в URL

Часто вы хотите послать какие-то данные в строке запроса URL. Если вы строите URL вручную, то эти данные будут представлены в нем в виде пар ключ-значение после знака вопроса. Например, httpbin.org/get?key=val. Requests позволяет передать эти аргументы в качестве словаря, используя аргумент params. В качестве примера, если вы хотите передать key1=value1 и key2=value2 ресурсу httpbin.org/get, вы должны использовать следующий код:

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

Вы можете видеть, что URL был закодирован правильно:

>>> print(r.url)
http://httpbin.org/get?key2=value2&key1=value1

Заметим, что любой ключ словаря, значение которого None, не будет добавлен к строке запроса URL.

Содержимое ответа

Мы можем читать содержимое ответа сервера. Рассмотрим тайм-лайн GitHub снова:

>>> import requests
>>> r = requests.get('https://api.github.com/events') 
>>> r.text 
u'[{"repository":{"open_issues":0,"url":"https://github.com/...

Requests будет автоматически декодировать содержимое ответа сервера. Большинство Unicode кодировок без проблем декодируются.

Когда вы делаете запрос, Requests делает предположение о кодировке, основанное на заголовках HTTP. Кодировка текста, угаданная Requests, используется при обращение к r.text. Вы можете узнать, какую кодировку использует Requests, и изменить её воспользовавшись свойством r.encoding:

>>> r.encoding
'utf-8' 
>>> r.encoding = 'ISO-8859-1' 

Если вы измените кодировку, Requests будет использовать новое значение r.encoding всякий раз, когда вы будете использовать r.text. Вы можете сделать это в любой ситуации, где нужна более специализированная логика работы с кодировкой содержимого ответа. Например, в HTML и XML есть возможность задавать кодировку прямо в теле документа. В подобных ситуациях вы должны использовать r.content, чтобы найти кодировку, а затем установить r.encoding. Это позволит вам использовать r.text с правильной кодировкой.

Requests может также использовать пользовательские кодировки в случае, если вы в них нуждаетесь. Если вы создали свою собственную кодировку и зарегистрировали её в модуле codecs, вы можете просто использовать название кодека в качестве значения r.encoding, и Requests будет работать с этой кодировкой для вас.

Бинарное содержимое ответа

Вы также можете получить доступ к телу ответа в виде байтов для нетекстовых запросов:

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

Передача со сжатием gzip и deflate автоматически декодируется.

Например, чтобы создать изображение из бинарных данных, возвращаемых в ответ на запрос, вы можете использовать следующий код:

>>> from PIL import Image 
>>> from StringIO import StringIO 
>>> i = Image.open(StringIO(r.content))

JSON содержимое ответа

С библиотекой Requests также поставляется встроенный JSON декодер на случай, если вы имеете дело с данными в формате JSON:

>>> import requests 
>>> r = requests.get('https://api.github.com/events') 
>>> r.json() 
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...

В случае, если декодирование JSON не удается, r.json выбрасывает исключение. Например, если приходит ответ с кодом статуса 401 (неавторизованный), попытка обращения к r.json выбрасывает исключение ValueError: No JSON object could be decoded.

Необработанное содержимое ответа

В редких случаях, когда вы хотите получить доступ к сырому ответу сервера на уровне сокета, вы можете обратиться к r.raw. Если вы хотите сделать это, убедитесь, что вы установили stream=True в вашем первом запросе. После этого вы уже можете проделать следующее:

>>> r = requests.get('https://api.github.com/events', stream=True) 
>>> r.raw 
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810> 
>>> r.raw.read(10) 
'x1fx8bx08x00x00x00x00x00x00x03'

Теперь вы можете использовать подобный код как шаблон, чтобы сохранить получаемый поток в файл:

>>> with open(filename, 'wb') as fd:
>>>     for chunk in r.iter_content(chunk_size):
>>>         fd.write(chunk)

Использование Response.iter_content будет обрабатывать многое из того, с чем бы вам пришлось иметь дело при использовании Response.raw напрямую. Описанное выше является предпочтительным и рекомендуемым способом извлечения содержимого при потоковой загрузке.

Пользовательские HTTP заголовки

Если вы хотите добавить HTTP-заголовки в запрос, просто передайте соответствующий словарь в параметре headers. Например, мы не указали заголовок content-type в предыдущем примере. Давайте сделаем это сейчас:

>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'} 
>>> headers = {'content-type': 'application/json'}
>>> r = requests.post(url, data=json.dumps(payload), headers=headers)

Более сложные POST запросы

Зачастую вы хотите послать некоторые form-encoded данные также как это делается в HTML форме. Чтобы сделать это, просто передайте соответствующий словарь в аргументе data. Ваш словарь данных в таком случае будет автоматически закодирован как HTML форма, когда будет сделан запрос:

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

Но есть много случаев, когда вы можете захотеть отправить данные, которые не закодированы методом form-encoded. Если вы передадите в запрос строку вместо словаря, то данные будут отправлены в неизменном виде. Например, API v3 GitHub принимает JSON-закодированные POST/PATCH данные:

>>> import json url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(payload))

Как послать Multipart-Encoded файл

Requests позволяет легко послать на сервер Multipart-Encoded файлы:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "<censored...binary...data>" }, ... }

Вы можете установить имя файла, content-type и заголовки в явном виде:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
>>> r = requests.post(url, files=files)
>>> r.text
{ ... "files": { "file": "<censored...binary...data>" }, ... }

При желании, вы можете отправить строки, которые будут приняты в виде файлов:

>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,sendnanother,row,to,sendn')}
>>> r = requests.post(url, files=files)
>>> r.text
{ ... "files": { "file": "some,data,to,sendnanother,row,to,sendn" }, ... }

В случае, если вы отправляете очень большой файл как multipart/form-data, вы можете захотеть отправить запрос потоком. По умолчанию requests не поддерживает этого, но есть отдельный пакет, который это делает — requests-toolbelt.

Для отправки нескольких файлов в одном запросе, обратитесь к дополнительной документации.

Коды состояния ответа

Мы можем проверить код состояния ответа:

>>> r = requests.get('http://httpbin.org/get')
>>> r.status_code
200

Для удобства Requests также поставляется со встроенным объектом подстановок кодов состояния:

>>> r.status_code == requests.codes.ok
True

Если мы сделали плохой запрос (ошибка 4XX клиента или ошибка 5XX ответа сервера), то мы можем возбудить исключение с помощью Response.raise_for_status():

>>> bad_r = requests.get('http://httpbin.org/status/404') 
>>> bad_r.status_code 
404
>>> bad_r.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
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error

Но если status_code для r оказался 200, то когда мы вызываем raise_for_status() мы получаем:

>>> r.raise_for_status()
None

Это значит, что все в порядке.

Заголовки ответов

Мы можем просматривать заголовки ответа сервера, используя словарь Python:

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

Однако это словарь особого рода: он сделан специально для HTTP заголовков. Согласно RFC 7230, имена заголовков HTTP нечувствительны к регистру.

Таким образом, мы можем получить доступ к заголовкам с любым написанием:

>>> r.headers['[red]Content-Type[/red]'] 
'application/json'
>>> r.headers.get('[red]content-type[/red]')
'application/json'

Cookies

Если ответ содержит cookie, вы можете быстро получить к ним доступ:

>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']
'example_cookie_value'

Для отправки собственных cookie на сервер, вы можете использовать параметр cookies:

>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'

Редиректы и история

По умолчанию Requests будет выполнять редиректы для всех HTTP методов, кроме HEAD. Мы можем использовать свойство history объекта Response, чтобы отслеживать редиректы. Список Response.history содержит объекты Response, которые были созданы во время выполнения запроса. Список сортируется от более ранних к более поздним ответам.

Например, GitHub перенаправляет все HTTP запросы на HTTPS:

>>> r = requests.get('http://github.com') 
>>> r.url 'https://github.com/' 
>>> r.status_code 
200 
>>> r.history 
[<Response [301]>]

Если вы используете GET, OPTIONS, POST, PUT, PATCH или DELETE, вы можете отключить обработку редиректов с помощью параметра allow_redirects:

>>> r = requests.get('http://github.com', allow_redirects=False) 
>>> r.status_code 
301 
>>> r.history 
[]

Если вы используете HEAD, вы можете включить обработку редиректов:

>>> r = requests.head('http://github.com', allow_redirects=True) 
>>> r.url 'https://github.com/' 
>>> r.history 
[<Response [301]>]

Тайм-ауты

Вы можете сказать Requests прекратить ожидание ответа после определенного количества секунд с помощью параметра timeout:

>>> requests.get('http://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)
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 секунд (точнее, если ни одного байта не было получено от основного сокета за timeout секунд).

Ошибки и исключения

  • В случае неполадок в сети (например, отказа DNS, отказался соединения и т.д.), Requests возбудит исключение ConnectionError.
  • В более редком случае неверного HTTP ответа, Requests возбудит исключение HTTPError.
  • Если превышено время ожидания ответа, возбуждается исключение Timeout.
  • Если запрос превышает заданное значение максимального количества редиректов, то возбуждается исключение TooManyRedirects.

Все исключения, которые возбуждает непосредственно Requests, унаследованы от requests.exceptions.RequestException.

24 Дек. 2015, Python, 344766 просмотров,

Стандартная библиотека Python имеет ряд готовых модулей по работе с HTTP.

  • urllib
  • httplib

Если уж совсем хочется хардкора, то можно и сразу с socket поработать. Но у всех этих модулей есть один большой недостатокнеудобство работы.

Во-первых, большое обилие классов и функций. Во-вторых, код получается вовсе не pythonic. Многие программисты любят Python за его элегантность и простоту, поэтому и был создан модуль, призванный решать проблему существующих и имя ему requests или HTTP For Humans. На момент написания данной заметки, последняя версия библиотеки — 2.9.1. С момента выхода Python версии 3.5 я дал себе негласное обещание писать новый код только на Py >= 3.5. Пора бы уже полностью перебираться на 3-ю ветку змеюки, поэтому в моих примерах print отныне является функцией, а не оператором :-)

Что же умеет requests?

Для начала хочется показать как выглядит код работы с http, используя модули из стандартной библиотеки Python и код при работе с requests. В качестве мишени для стрельбы http запросами будет использоваться очень удобный сервис httpbin.org


>>> import urllib.request
>>> response = urllib.request.urlopen('https://httpbin.org/get')
>>> print(response.read())
b'{n  "args": {}, n  "headers": {n    "Accept-Encoding": "identity", n    "Host": "httpbin.org", n    "User-Agent": "Python-urllib/3.5"n  }, n  "origin": "95.56.82.136", n  "url": "https://httpbin.org/get"n}n'
>>> print(response.getheader('Server'))
nginx
>>> print(response.getcode())
200
>>> 

Кстати, urllib.request это надстройка над «низкоуровневой» библиотекой httplib о которой я писал выше.

>>> import requests
>>> response = requests.get('https://httpbin.org/get')
>>> print(response.content)
b'{n  "args": {}, n  "headers": {n    "Accept": "*/*", n    "Accept-Encoding": "gzip, deflate", n    "Host": "httpbin.org", n    "User-Agent": "python-requests/2.9.1"n  }, n  "origin": "95.56.82.136", n  "url": "https://httpbin.org/get"n}n'
>>> response.json()
{'headers': {'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'python-requests/2.9.1', 'Host': 'httpbin.org', 'Accept': '*/*'}, 'args': {}, 'origin': '95.56.82.136', 'url': 'https://httpbin.org/get'}
>>> response.headers
{'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Server': 'nginx', 'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Length': '237', 'Date': 'Wed, 23 Dec 2015 17:56:46 GMT'}
>>> response.headers.get('Server')
'nginx'

В простых методах запросов значительных отличий у них не имеется. Но давайте взглянем на работы с Basic Auth:


>>> import urllib.request
>>> password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
>>> top_level_url = 'https://httpbin.org/basic-auth/user/passwd'
>>> password_mgr.add_password(None, top_level_url, 'user', 'passwd')
>>> handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
>>> opener = urllib.request.build_opener(handler)
>>> response = opener.open(top_level_url)
>>> response.getcode()
200
>>> response.read()
b'{n  "authenticated": true, n  "user": "user"n}n'

>>> import requests
>>> response = requests.get('https://httpbin.org/basic-auth/user/passwd', auth=('user', 'passwd'))
>>> print(response.content)
b'{n  "authenticated": true, n  "user": "user"n}n'
>>> print(response.json())
{'user': 'user', 'authenticated': True}

А теперь чувствуется разница между pythonic и non-pythonic? Я думаю разница на лицо. И несмотря на тот факт, что requests ничто иное как обёртка над urllib3, а последняя является надстройкой над стандартными средствами Python, удобство написания кода в большинстве случаев является приоритетом номер один.

В requests имеется:

  • Множество методов http аутентификации
  • Сессии с куками
  • Полноценная поддержка SSL
  • Различные методы-плюшки вроде .json(), которые вернут данные в нужном формате
  • Проксирование
  • Грамотная и логичная работа с исключениями

О последнем пункте мне бы хотелось поговорить чуточку подробнее.

Обработка исключений в requests

При работе с внешними сервисами никогда не стоит полагаться на их отказоустойчивость. Всё упадёт рано или поздно, поэтому нам, программистам, необходимо быть всегда к этому готовыми, желательно заранее и в спокойной обстановке.

Итак, как у requests дела обстоят с различными факапами в момент сетевых соединений? Для начала определим ряд проблем, которые могут возникнуть:

  • Хост недоступен. Обычно такого рода ошибка происходит из-за проблем конфигурирования DNS. (DNS lookup failure)
  • «Вылет» соединения по таймауту
  • Ошибки HTTP. Подробнее о HTTP кодах можно посмотреть здесь.
  • Ошибки SSL соединений (обычно при наличии проблем с SSL сертификатом: просрочен, не является доверенным и т.д.)

Базовым классом-исключением в requests является RequestException. От него наследуются все остальные

  • HTTPError
  • ConnectionError
  • Timeout
  • SSLError
  • ProxyError

И так далее. Полный список всех исключений можно посмотреть в requests.exceptions.

Timeout

В requests имеется 2 вида таймаут-исключений:

  • ConnectTimeout — таймаут на соединения
  • ReadTimeout — таймаут на чтение
>>> import requests
>>> try:
...     response = requests.get('https://httpbin.org/user-agent', timeout=(0.00001, 10))
... except requests.exceptions.ConnectTimeout:
...     print('Oops. Connection timeout occured!')
...     
Oops. Connection timeout occured!
>>> try:
...     response = requests.get('https://httpbin.org/user-agent', timeout=(10, 0.0001))
... except requests.exceptions.ReadTimeout:
...     print('Oops. Read timeout occured')
... except requests.exceptions.ConnectTimeout:
...     print('Oops. Connection timeout occured!')
...     
Oops. Read timeout occured

ConnectionError


>>> import requests
>>> try:
...     response = requests.get('http://urldoesnotexistforsure.bom')
... except requests.exceptions.ConnectionError:
...     print('Seems like dns lookup failed..')
...     
Seems like dns lookup failed..

HTTPError


>>> import requests
>>> try:
...     response = requests.get('https://httpbin.org/status/500')
...     response.raise_for_status()
... except requests.exceptions.HTTPError as err:
...     print('Oops. HTTP Error occured')
...     print('Response is: {content}'.format(content=err.response.content))
...     
Oops. HTTP Error occured
Response is: b''

Я перечислил основные виды исключений, которые покрывают, пожалуй, 90% всех проблем, возникающих при работе с http. Главное помнить, что если мы действительно намерены отловить что-то и обработать, то это необходимо явно запрограммировать, если же нам неважен тип конкретного исключения, то можно отлавливать общий базовый класс RequestException и действовать уже от конкретного случая, например, залоггировать исключение и выкинуть его дальше наверх. Кстати, о логгировании я напишу отдельный подробный пост.

У блога появился свой Telegram канал, где я стараюсь делиться интересными находками из сети на тему разработки программного обеспечения. Велком, как говорится :)

Полезные «плюшки»

  • httpbin.org очень полезный сервис для тестирования http клиентов, в частности удобен для тестирования нестандартного поведения сервиса
  • httpie консольный http клиент (замена curl) написанный на Python
  • responses mock библиотека для работы с requests
  • HTTPretty mock библиотека для работы с http модулями

💌 Присоединяйтесь к рассылке

Понравился контент? Пожалуйста, подпишись на рассылку.

Like this post? Please share to your friends:
  • Ошибка respcode 198
  • Ошибка resourcerequest timed out
  • Ошибка resource not owned
  • Ошибка resource materials
  • Ошибка resizebuffers failed device removed device hung