На этом шаге мы рассмотрим способы решения этой задачи.
Вам нужно обращаться к различным веб-сервисам через HTTP в роли клиента. Например, скачивать данные или взаимодействовать с REST API.
Для простых задач обычно достаточно модуля urllib.request. Например, чтобы послать простой GET-запрос удаленному сервису, сделайте так:
from urllib import request, parse # Базовый URL, к которому обращаемся url = 'http://httpbin.org/get' # Словарь параметров запроса (если они есть) parms = { 'name1': 'value1', 'name2': 'value2' } # Кодируем строку запроса querystring = parse.urlencode(parms) # Делаем GET-запрос и читаем ответ u = request.urlopen(url + '?' + querystring) resp = u.read()
Если вам нужно послать параметры в теле запроса, используя метод POST, закодируйте их и предоставьте в качестве необязательных аргументов в urlopen():
from urllib import request, parse # Базовый URL, к которому обращаемся url = 'http://httpbin.org/get' # Словарь параметров запроса (если они есть) parms = { 'name1': 'value1', 'name2': 'value2' } # Кодируем строку запроса querystring = parse.urlencode(parms) # Делаем POST-запрос и читаем ответ u = request.urlopen(url, querystring.encode('ascii')) resp = u.read()
Если вам нужно предоставить какие-то кастомные HTTP-заголовки в исходящем запросе (например, измененное поле User-Agent), нужно создать содержащий их значения словарь, а также экземпляр Request - и передать его в urlopen():
from urllib import request, parse . . . # Дополнительные заголовки headers = { 'User-agent': 'none/ofyourbusiness', 'Spam': 'Eggs' } req = request.Request(url, querystring.encode('ascii'), headers=headers) # Делаем запрос и читаем ответ u = request.urlopen(req) resp = u.read()
Если ваше взаимодействие с сервисом сложнее, вам стоит обратить внимание на библиотеку requests. Например, вот эквивалентный предыдущим операциям код на requests:
import requests # Базовый URL, к которому обращаемся url = 'http://httpbin.org/post' # Словарь параметров запроса (если они есть) parms = { 'name1': 'value1', 'name2': 'value2' } # Дополнительные заголовки headers = { 'User-agent': 'none/ofyourbusiness', 'Spam': 'Eggs' } resp = requests.post(url, data=parms, headers=headers) # Раскодированный текст, возвращенный запросом text = resp.text
Стоит отметить, как requests возвращает полученное содержимое ответа на запрос. Как показано выше, атрибут resp.text предоставляет декодированный в Unicode текст запроса. Однако если вы обратитесь к resp.content, то получите не текст, а сырое бинарное содержимое. С другой стороны, если вы обратитесь к resp.json, то получите содержимое ответа в формате JSON.
Вот пример использования requests для создания запроса HEAD и извлечения нескольких полей данных заголовка из ответа:
import requests resp = requests.head('http://www.python.org/index.html') status = resp.status_code last_modified = resp.headers['last-modified'] content_type = resp.headers['content-type'] content_length = resp.headers['content-length']
Вот пример, который показывает, как можно залогиниться в Python Package Index, используя базовую аутентификацию:
import requests resp = requests.get('http://pypi.python.org/pypi?:action=login', auth=('user', 'password'))
Вот пример передачи HTTP-куки из одного запроса следующему:
import requests # Первый запрос resp1 = requests.get(url) . . . # Второй запрос с cookie, полученными при первом запросе resp2 = requests.get(url, cookies=resp1.cookies)
Последний пример - загрузка данных на сервер:
import requests url = 'http://httpbin.org/post' files = {'file': ('data.csv', open('data.csv', 'rb'))} r = requests.post(url, files=files)
Для простого клиентского HTTP-кода встроенного модуля urllib обычно хватает. Однако если вам нужно что-то помимо простых POST- и GET-запросов, вы не можете полагаться на его функциональность. В этом случае лучше подойдут сторонние модули, такие как requests.
Например, если вы решите полностью опираться только на стандартную библиотеку вместо библиотеки типа requests, вам придется реализовать ваш код с использованием низкоуровневого модуля http.client. Например, этот код выполняет HEAD-запрос:
from http.client import HTTPConnection from urllib import parse c = HTTPConnection('www.python.org', 80) c.request('HEAD', '/index.html') resp = c.getresponse() print('Status', resp.status) for name, value in resp.getheaders(): print(name, value)
А если вам нужно написать код, использующий прокси, аутентификацию, куки и другие подобные моменты, использовать urllib неудобно, а код получится многословным. Например, вот пример кода, который производит авторизацию в Python Package Index:
import urllib.request auth = urllib.request.HTTPBasicAuthHandler() auth.add_password('pypi', 'http://pypi.python.org', 'username', 'password') opener = urllib.request.build_opener(auth) r = urllib.request.Request('http://pypi.python.org/pypi?:action=login') u = opener.open(r) resp = u.read() # Далее вы можете обращаться к другим страницам, используя opener . . .
Если честно, все это проще сделать с помощью requests. Тестирование клиентского HTTP-кода в процессе разработки часто может быть непростым из-за большого количества неочевидных деталей, о которых нужно думать (например, куки, авторизация, заголовки, кодировки и т. п.). Если вам нужно тестировать код, посмотрите на сервис httpbin.
Этот сайт получает запросы и "эхом" отсылает информацию обратно в форме JSON-ответа. Вот интерактивный сеанс:
>>> import requests >>> r = requests.get('http://httpbin.org/get?name=Dave&n=37', headers = {'User-agent': 'goaway/1.0'}) >>> resp = r.json >>> resp['headers'] {'User-Agent': 'goaway/1.0', 'Content-Length': '', 'Content-Type': '', 'Accept-Encoding': 'gzip, deflate, compress', 'Connection': 'keep-alive', 'Host': 'httpbin.org', 'Accept': '*/*'} >>> resp['args'] {'name': 'Dave', 'n': '37'} >>>
Работать с сайтом типа httpbin часто предпочтительнее, чем экспериментировать с реальным сайтом, особенно в том случае, если существует риск отключения аккаунта после трех провалившихся попыток залогиниться (не пробуйте учиться писать клиента с HTTP-аутентификацией путем отправки запросов на сайт вашего банка).
Хотя мы здесь это и не обсудили, requests поддерживает намного более продвинутые клиентские протоколы HTTP, такие как OAuth. Рекомендуем вам обратиться к замечательной документации requests.
На следующем шаге мы рассмотрим создание TCP-сервера.