Для Python существует замечательная библиотека для работы со всеми типами HTTP запросов - Requests, но когда нужно сделать что-то без внешних зависимостей, то встает вопрос велосипедостроения. Проблема еще более усиливается когда необходима одновременная поддержка Python 2 и Python 3.
Стандартная библиотека urllib.urlopen
не поддерживает методы для отправки PUT и DELETE запросов, а кроме того в Python 3 перенесли большинство методов из urllib2
в urllib.request
, что добавляет некоторые костыли в код, для совместимости с Python 2. Таким образом составил себе список того что мне необходимо:
- Совместимость Python 2 и 3;
- Отправка GET, POST, PUT, DELETE запросов;
- Парсинг JSON ответа.
Посидев пару часов собрал свой велосипед совмещающий в себе все эти требования:
import sys
import json
try:
# python3
from urllib.request import build_opener, Request, HTTPHandler
from urllib.error import HTTPError
from urllib.parse import urlencode
except ImportError: # pragma: no cover
# python2
from urllib2 import build_opener, Request, HTTPHandler, HTTPError
from urllib import urlencode
def request(url, method='GET', data=None, headers={}):
if data is not None:
data = urlencode(data)
if method in ['GET', 'DELETE']:
url = url + '?' + data
data = None
else:
x_www = 'application/x-www-form-urlencoded; charset=utf-8'
headers.update({'Content-Type': x_www})
if sys.version_info > (3,): # python3
data = data.encode('utf-8')
try:
opener = build_opener(HTTPHandler)
req = Request(url, data=data, headers=headers)
req.get_method = lambda: method
response = opener.open(req).read()
data = json.loads(response.decode('utf-8'))
except HTTPError as e:
data = json.loads(e.read().decode('utf-8'))
except ValueError:
return False
return data
Можно легко проверить работу всех этих методов используя сервис httpbin.org
data = {'foo': 'bar'}
headers = {'x-header': 'x-value'}
resp = request('https://httpbin.org/get', data=data, headers=headers)
assert resp['headers']['X-Header'] == 'x-value'
assert resp['url'] == 'https://httpbin.org/get?foo=bar'
assert resp['args']['foo'] == 'bar'
resp = request('https://httpbin.org/post', 'POST', data=data, headers=headers)
assert resp['headers']['X-Header'] == 'x-value'
assert resp['url'] == 'https://httpbin.org/post'
assert resp['form']['foo'] == 'bar'
resp = request('https://httpbin.org/put', 'PUT', data=data, headers=headers)
assert resp['headers']['X-Header'] == 'x-value'
assert resp['url'] == 'https://httpbin.org/put'
assert resp['form']['foo'] == 'bar'
resp = request('https://httpbin.org/delete', 'DELETE', data=data, headers=headers)
assert resp['headers']['X-Header'] == 'x-value'
assert resp['url'] == 'https://httpbin.org/delete?foo=bar'
assert resp['args']['foo'] == 'bar'