Python: модуль requests

Автор: | 06/03/2015
 

Pythonrequests — HTTP библиотека Python. Ближайший аналог — urllib2.

Содержит в себе встроенную библиотеку urllib3.

Домашняя страница проекта — Requests: HTTP for Humans.

Установить requests можно  спомощью PIP:

$ pip install requests
...
Installing collected packages: requests
Successfully installed requests-2.7.0

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

Создание подключения:

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

Теперь r является объектом Response:

>>> type(r)
<class 'requests.models.Response'>
>>> print(r.__doc__)
The :class:`Response <Response>` object, which contains a
    server's response to an HTTP request.

Таким образом мы можем выполнять все стандартные запросы — PUT, DELETE, HEAD and 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")

Передача аргументов в запросе

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

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

Данные были корректно обработа и переданы, что можно увидеть вызвав атрибут url объекта r:

>>> r.url
u'http://httpbin.org/get?key2=value2&key1=value1'

Учтите, что любое значение в словаре, которое является Null не будет передано.

Что бы передать список объектов в виде значения — вы должны добавить квадратые скобки [] к ключу:

>>> payload = {'key1': 'value1', 'key2[]': ['value2', 'value3']}
>>> r = requests.get("http://httpbin.org/get", params=payload)
>>> r.url
u'http://httpbin.org/get?key1=value1&key2%5B%5D=value2&key2%5B%5D=value3'

Содержимое объекта response

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

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

Необработанное содержимое объекта response

В случае, если вам необходимо получить необработанные (raw) данные от сервера — вы можете воспользоваться представлением raw. При этом — убедитесь, что параметр stream для Requests установлен в True:

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

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

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

Изменение заголовков

Если вы хотите добавить какие-то заголовки в HTTP-запрос — просто доавбьте передачу словаря с ними в параметр headers:

>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)

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

Возможно, вам требуется отправлять более сложные запросы, например — данные форм, как формы в HTML. Что бы сделать это — просто передайте словарь с данными параметру data:

>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> headers = {'user-agent': 'rtfm/0.0.1'}
>>> r = requests.post("http://httpbin.org/post", data=payload, headers=headers)
>>> print(r.text)
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "key1": "value1",
    "key2": "value2"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "23",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "rtfm/0.0.1"
  },
  "json": null,
  "origin": "194.***.***.69",
  "url": "http://httpbin.org/post"
}

Иногда вам может потребоваться передать данные не в виде формы. Если вы передадите строку вместо словаря — данные будут добдавлены напрямую.

Например, GitHub API v3 принимает POST/PATCH в формате JSON:

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

POST для файлов составной кодировки

С помощью 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>"
  },
  ...
}

Вы так же можете указать имя файла, тип контента и заголовки явным образом:

>>> 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,send\nanother,row,to,send\n"
  },
  ...
}

Коды состояний в requests

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

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

Кроме того, в requests имеются специальный способ проверки кодов состояния:

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

Для работы с кодами ошибок — можно использовать метод 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 "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/site-packages/requests/models.py", line 851, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: NOT FOUND

И использовать его в конструкции try/except:

#!/usr/bin/env python

import requests

bad_r = requests.get('http://httpbin.org/status/404')

try:
    bad_r.raise_for_status()
except requests.exceptions.HTTPError as e:
    print('ERROR: %s' % e)
$ ./req.py
ERROR: 404 Client Error: NOT FOUND

Заголовки в requests

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

>>> r.headers
{'content-length': '284', 'server': 'nginx', 'connection': 'keep-alive', 'access-control-allow-credentials': 'true', 'date': 'Tue, 02 Jun 2015 10:11:57 GMT', 'access-control-allow-origin': '*', 'content-type': 'application/json'}

Или получить только отдельное поле заголовка:

>>> r.headers['server']
'nginx'

Таймауты

Мы можем указать requests использовать таймаут подключения с помощью параметра timeout:

>>> requests.get('http://github.com', timeout=0.001)
...
requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='github.com', port=80): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<requests.packages.urllib3.connection.HTTPConnection object at 0x6ffff676e50>, 'Connection to github.com timed out. (connect timeout=0.001)'))

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

В случае проблем с подключением (ошибка DNS, сброс соединения и так далее) — requests вызовет исключение ConnectionError.

В случае некорректных заголовков HTTP — будет вызвано исключение HTTPError.

В случае таймаута — исключение Timeout.

Оригинал статьи: http://docs.python-requests.org