Requests

Перейдите к следующему слайду, нажав кнопку Вправо

Учимся работать с библиотекой requests в Python

Подготовлено онлайн-курсом

https://dvmn.org

import requests


url = 'https://yandex.ru'
requests.get(url)

requests.get

Шлёт запрос на Яндекс как будто мы просто зашли из браузера

import requests


url = 'https://yandex.ru'
response = requests.get(url)

requests.get

Не просто послали запрос, а поймали ответ в переменную

>>> response.status_code
200
>>> response.ok
True

HTTP статус ответа

Как узнать статус ответа

200 — код ответа. Есть много разных кодов ответа. Вы уже наверняка знаете про 404 — "страница не найдена", это неудавшийся запрос. Ответ 200 — успешный.

 

Успешен любой ответ, начинающийся с двойки — 2xx.

 

response.ok — проверка на успешность

 

Подробнее читайте по ссылке.

>>> response.raise_for_status()

requests.exceptions.HTTPError ....

Стандартная проверка статуса

Как проверить ответ сервера

Метод raise_for_status выкинет исключение (HTTPError), если запрос к серверу завершился неудачно. То есть если у запроса статус 4xx (ошибка клиента) или 5xx (ошибка сервера).

 

Эквивалентный код:

>>> if not response.ok:
...     raise requests.exceptions.HTTPError(response=response)

requests.exceptions.HTTPError ....
>>> response.encoding
'utf-8'

>>> response.url
'https://yandex.ru'

>>> response.is_redirect
False

response

В ответе хранится много всего полезного

payload = {"text": "python"}

response = requests.get('https://yandex.ru', params=payload)
response.raise_for_status()

>>> response.url
'https://yandex.ru/?text=python'

params

Формирование запросов с параметрами

делаем запрос с параметрами, получаем ответ

адрес, на который библиотека отправила запрос

payload = {"q": ""}

response = requests.get('https://yandex.ru', params=payload)
response.raise_for_status()

>>> response.url
'https://yandex.ru/?q='

params - ключ без значения

делаем запрос с параметром без значения, получаем ответ

этот адрес аналогичен такому: https://yandex.ru/?q

payload = {"text": "python django"}

response = requests.get('https://yandex.ru', params=payload)
response.raise_for_status()


>>> response.url
'https://yandex.ru/?text=python+django'

Экранирование ссылок

Пробелы в параметре преобразуются в плюсы

делаем запрос с параметрами, получаем ответ

адрес, на который библиотека отправила запрос

response = requests.get('https://www.ibm.com/developerworks/topics/django python')
response.raise_for_status()


>>> response.url
'https://www.ibm.com/developerworks/topics/django%20python'

Экранирование ссылок

Пробелы в ссылке преобразуются в %20

делаем запрос, получаем ответ

в адресе нет пробела, библиотека заменила его на %20

response = requests.get('https://ru.wikipedia.org/wiki/django rest framework')
response.raise_for_status()


>>> response.url
'https://ru.wikipedia.org/wiki/django-rest-framework'

Экранирование ссылок

Многие сайты самостоятельно преобразуют пробелы в дефисы или нижние подчёркивания

делаем запрос

сайт перенаправил запрос на другой адрес

payload = {"text": "курсы веб-разработки python"}

response = requests.get('https://yandex.ru', params=payload)
response.raise_for_status()


>>> response.url
'https://yandex.ru/?text=%D0%BA%D1%83%D1%80%D1%81
%D1%8B+%D0%B2%D0%B5%D0%B1-%D1%80%D0%B0%D0%B7%D1%80
%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8+python'

Экранирование ссылок

Кириллица может выглядеть странно, когда она закодирована для url запроса. Не пугайтесь, если встретитесь с таким.

делаем запрос с параметрами, получаем ответ

Адрес, на который библиотека отправила запрос

>>> response.text
'Weather report: Moscow, Russia
Overcast
    \  /       Переменная облачность
  _ /"".-.     -21--19 °C     
    \_(   ).   ↖ 4 km/h       
    /(___(__)  20 km          
               0.0 mm'

>>> response.raw
<urllib3.response.HTTPResponse object at 0x7f59da492a58>

>>> response.content
b'Weather report: Moscow, Russia
Overcast\n \x1b[38;5;240;1m     .--.
\x1b[0m \x1b[38;5;049m2\x1b[0m-\x1b[38;
\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94
\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80

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

Чаще всего вам нужен текст, но бывают и исключения

Текст в юникоде

Текст в byte формате

Ответ в "сыром" виде

>>> response.text
'{ "user_id" : "123132421" }'

>>> response.json()
{ "user_id" : "123132421" }

String vs JSON

Обратите внимание на возвращаемый тип

Строка с словарём

Словарь

>>> response.status_code
200
>>> response.text
"Ошибка 500"

Иногда status 200, но ошибка в ответе

Некоторые сайты всегда отправляют 200, но в тексте ответа присылают код ошибки.

import requests


url = 'https://yandex.ru'
response = requests.post(url)
response.raise_for_status()

requests.post

А вот так шлётся POST запрос

url = 'https://gist.github.com/статья'
payload = {
  "new-text": "Теперь в этой статье написан этот текст"
}

response_get = requests.get(url)
response_get.raise_for_status()


response_post = requests.post(url, data=payload)
response_post.raise_for_status()



# response_get.text - выдаст статью, которую вы хотели.

# response_post.text - выдаст ту же статью, но уже отредактированную.

POST data

Разница между GET запросом и POST запросом в том, что у POST есть тело запроса.

GET запрос

POST запрос

Тело POST запроса

Зачем нужен POST запрос

  • У url есть лимит длины и картинка не поместится в GET запрос
     
  • Поисковые роботы и программы ожидают, что GET-запрос ничего не изменит на сайте. Иначе Google и Яндекс повредят сайт при сканировании.
     
  • Для изменения данных есть POST-запрос. GET-запрос умеет только просить данные.

 

url = 'https://passport.yandex.ru/auth'
payload = {
  "login": "some-login",
  "password": "sample-text"
}

response = requests.post(url, json=payload)
response.raise_for_status()

print(response.json())

POST JSON data

Можно отправлять данные в формате JSON и смотреть ответы в формате JSON

{"account_status": "created"}
url = 'https://wikipedia.com'
headers = {
    'User-Agent': 'curl',
    'Accept-Language': 'ru-RU'
}

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

HTTP headers (Заголовки)

Так можно самостоятельно менять заголовки запроса

Заголовки запроса

url = 'https://wikipedia.com'
headers = {
    "Authorization": "Bearer cn389ncoiwuencr"
}

response = requests.get(url, headers=headers)
response.raise_for_status()
OAuth 2.0 bearer tokens (Заголовок авторизации)

Пример авторизации с токеном в заголовке запроса

import requests


filename = 'dvmn.svg'
url = "https://dvmn.org/filer/canonical/1542890876/16/"

response = requests.get(url)
response.raise_for_status()

with open(filename, 'wb') as file:
    file.write(response.content)
Как скачать картинку

Пример скачивания картинки

import requests

with open('image.jpg', 'rb') as file:
    url = '...'
    files = {
      'media': file,
    }
    response = requests.post(url, files=files)
    response.raise_for_status()
Как отправить картинку

Пример отправки картинки на сервер

Название media взято из документации к API

import requests

...


page = 0
pages_number = 1

while page < pages_number:
    page_response = requests.get(url, params={'page': page})
    page_response.raise_for_status()

    page_payload = page_response.json()
    pages_number = page_payload['pages_number']
    page += 1

    # TODO добавить данные из page_payload в итоговый список
    ...
    
Как скачать все страницы. Easy

Названия параметров и формат ответа сервера вымышлены

Нумерация страниц может начинаться с единицы, зависит от API

import requests
from itertools import count

...


for page in count(0):
    page_response = requests.get(url, params={'page': page})
    page_response.raise_for_status()

    page_payload = page_response.json()
    if page >= page_payload['pages_number']:
        break

    # TODO добавить данные из page_payload в итоговый список
    ...
    
Как скачать все страницы. Hard

Названия параметров и формат ответа сервера вымышлены

Нумерация страниц может начинаться с единицы, зависит от API

import requests
from itertools import count

...


def fetch_records():
    for page in count():
        page_response = requests.get(url, params={'page': page})
        page_response.raise_for_status()
        page_payload = page_response.json()
		
		yield from page_payload['page_records']
        
        if page >= page_payload['pages_number']:
            break
    
Как скачать все страницы. Hard + yield

Названия параметров и формат ответа сервера вымышлены

import requests

def fetch_specific_data():
    ...
    response = requests.get(url, params=params)

    # сообщаем внешнему коду о возможной ошибке
    response.raise_for_status()

    return response.json()['specific_key']


try:
    # плохое название, но без конкретики лучше не сделать
    some_data = fetch_specific_data()
except requests.exceptions.HTTPError as error:
    exit("Can't get data from server:\n{0}".format(error))
Сообщить об ошибке

Если статус ответа будет отличаться от 2xx, то функция выкинет исключение HTTPError. Внешний код узнает о проблеме и корректно завершит работу программы.

Документация

Полная документация библиотеки requests собрана здесь.

Создано для онлайн-курса https://dvmn.org