安装
pip install requests
基本用法
使用requests写一个简单的http请求。
# 导入requests模块
import requests
r = requests.get('https://api.github.com/events')
r = requests.post('https://httpbin.org/post', data={'key': 'value'})
r = requests.put('https://httpbin.org/put', data={'key': 'value'})
r = requests.delete('https://httpbin.org/delete')
r = requests.head('https://httpbin.org/get')
r = requests.options('https://httpbin.org/get')
在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('https://httpbin.org/get', params=payload)
通过打印URL,可以看到正确的URL格式。
print(r.url)
# https://httpbin.org/get?key2=value2&key1=value1
响应内容
读取服务响应内容
import requests
r = requests.get('https://api.github.com/events')
print(r.text)
#[{"repository":{"open_issues":0,"url":"https://github.com/...
当请求发送时,Requests会根据HTTP标头对响应的编码进行有根据的解码。当访问r.text时,会使用Requests的文本编码。您可以使用r.encoding属性设置Requests正在使用的编码,并对其进行更改:
r.encoding
# 'utf-8'
r.encoding = 'ISO-8859-1'
二进制响应内容
对于非文本的响应内容,可以使用二进制响应查看。
r.content
# b'[{"repository":{"open_issues":0,"url":"https://github.com/...
gzip和deflate传输编码会自动解码。
如果安装了Brotli或brotlicffi等Brotli库,br传输编码将自动解码。
例如,要从请求返回的二进制数据创建图像,可以使用以下代码:
from PIL import Image
from io import BytesIO
i = Image.open(BytesIO(r.content))
JSON响应内容
如果JSON解码失败,r.json()会引发异常。例如,如果响应得到204(无内容),或者如果响应包含无效的JSON,使用r.json()会引发requests.exceptions。JSONDecodeError。此包装器异常为不同python版本和json序列化库可能引发的多个异常提供了互操作性。
import requests
r = requests.get('https://api.github.com/events')
r.json()
# [{'repository': {'open_issues': 0, 'url': 'https://github.com/...
原始响应内容
在极少数情况下,如果想从服务器获取原始套接字响应,可以访问r.raw。请确保在初始请求中设置stream=True。
r = requests.get('https://api.github.com/events', stream=True)
print(r.raw)
# <urllib3.response.HTTPResponse object at 0x101194810>
print(r.raw.read(10))
# b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
这种使用方法一般用来保存文件。
with open(filename, 'wb') as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
自定义请求头
如果想在请求中传入请求头,需要向headers参数传入一个dict。
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'}
r = requests.post('https://httpbin.org/post', data=payload)
print(r.text)
"""
{
...
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
"""
数据参数也可以为每个键设置多个值。这可以通过使数据成为元组列表或以列表为值的字典来实现。当表单中有多个元素使用相同的键时:
payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
payload_dict = {'key1': ['value1', 'value2']}
r2 = requests.post('https://httpbin.org/post', data=payload_dict)
print(r1.text)
"""
{
...
"form": {
"key1": [
"value1",
"value2"
]
},
...
}
"""
r1.text == r2.text
# True
有时您可能希望发送未经表单编码的数据。如果你传入一个字符串而不是字典,那么这些数据将被发送。
例如,GitHub API v3接受JSON-Encoded POST/PATCH数据:
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
请注意,上述代码不会添加Content-Type标头(因此特别不会将其设置为application/json)。
如果需要该标头集,并且不想自己对dict进行编码,也可以直接使用json参数(在2.4.2版本中添加):
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, json=payload)
传递文件
requests上传文件。
url = 'https://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 = 'https://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>"
},
...
}
"""
如果将一个非常大的文件作为ultipart/form-data请求发送,可能需要流式传输该请求。默认情况下,requests不支持此功能,但有一个单独的包可以支持此功能——requests-toolbelt。要在一个请求中发送多个文件,请参阅高级用法部分。
响应状态代码
查看响应状态代码
r = requests.get('https://httpbin.org/get')
print(r.status_code)
# 200
requests还附带了一个内置的状态代码查找对象:
r.status_code == requests.codes.ok
# True
如果发出了一个错误的请求(4XX客户端错误或5XX服务器错误响应),我们可以用response.raise_for_status()来捕获它:
bad_r = requests.get('https://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
"""
如果r的status_code为200,当调用raise_for_status()时,返回None:
r.raise_for_status()
None
响应头
响应头返回的是字典值(dict)
r.headers
"""
{
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json'
}
"""
Cookie
如果响应包含Cookie,可以快速访问
url = 'http://example.com/some/cookie/setting/url'
r = requests.get(url)
r.cookies['example_cookie_name']
# 'example_cookie_value'
要将您自己的Cookie发送到服务器,可以使用Cookie参数:
url = 'https://httpbin.org/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies)
r.text
# '{"cookies": {"cookies_are": "working"}}'
Cookie在RequestsCookieJar中返回,它的作用类似于字典,但也提供了一个更完整的接口,适合在多个域或路径上使用。Cookie jar也可以传递给请求:
jar = requests.cookies.RequestsCookieJar()
jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
url = 'https://httpbin.org/cookies'
r = requests.get(url, cookies=jar)
r.text
# '{"cookies": {"tasty_cookie": "yum"}}'
重定向和历史
默认情况下,Requests将对除HEAD之外的所有谓词执行位置重定向。
我们可以使用Response对象的history属性来跟踪重定向。
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
# []
r = requests.head('http://github.com/', allow_redirects=True)
r.url
# 'https://github.com/'
r.history
# [<Response [301]>]
超时
使用timeout参数告诉Requests在给定秒数后停止等待响应。几乎所有的生产代码都应该在几乎所有的请求中使用此参数。如果不这样做,可能会导致程序无限期挂起:
requests.get('https://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)
"""
高级用法
Session Object
Session对象允许您在请求之间持久化某些参数。它还将Cookie保存在Session实例发出的所有请求中,并将使用urllib3的连接池。因此,如果对同一主机发出多个请求,则底层TCP连接将被重用,这可能会导致性能显著提高。
Session Object具有Requests API的所有方法。
让我们在请求之间保留一些Cookie:
s = requests.Session()
s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get('https://httpbin.org/cookies')
print(r.text)
# '{"cookies": {"sessioncookie": "123456789"}}'
Session也可用于为请求方法提供默认数据。这是通过向Session对象的属性提供数据来实现的:
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
# both 'x-test' and 'x-test2' are sent
s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})
传递给请求方法的任何字典都将与设置的会话级值合并。方法级参数覆盖会话参数。
但是请注意,即使使用会话,方法级参数也不会在请求之间持久化。此示例将仅发送第一个请求的Cookie,而不发送第二个请求:
s = requests.Session()
r = s.get('https://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(r.text)
# '{"cookies": {"from-my": "browser"}}'
r = s.get('https://httpbin.org/cookies')
print(r.text)
# '{"cookies": {}}'
请求和响应对象
每当调用requests.get()和friends时,你都在做两件主要的事情。首先,你正在构建一个Request对象,该对象将被发送到服务器以请求或查询某些资源。其次,一旦Requests从服务器收到响应,就会生成一个Response对象。Response对象包含服务器返回的所有信息,还包含您最初创建的Request对象。以下是一个从维基百科服务器获取一些信息的简单请求:
r = requests.get('https://en.wikipedia.org/wiki/Monty_Python')
如果想访问服务器发送回我们的标头,可以这样做:
r.headers
"""
{'content-length': '56170', 'x-content-type-options': 'nosniff', 'x-cache':
'HIT from cp1006.eqiad.wmnet, MISS from cp1010.eqiad.wmnet', 'content-encoding':
'gzip', 'age': '3080', 'content-language': 'en', 'vary': 'Accept-Encoding,Cookie',
'server': 'Apache', 'last-modified': 'Wed, 13 Jun 2012 01:33:50 GMT',
'connection': 'close', 'cache-control': 'private, s-maxage=0, max-age=0,
must-revalidate', 'date': 'Thu, 14 Jun 2012 12:59:39 GMT', 'content-type':
'text/html; charset=UTF-8', 'x-cache-lookup': 'HIT from cp1006.eqiad.wmnet:3128,
MISS from cp1010.eqiad.wmnet:80'}
"""
如果想获取发送给服务器的标头,只需访问请求,然后访问请求的标头:
r.request.headers
"""
{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/1.2.0'}
"""
Prepared Requests
每当从API调用或会话调用接收到Response对象时,requests属性实际上就是所使用的PreparedRequest。在某些情况下,可能希望在发送请求之前对正文或标头(或其他任何内容)做一些额外的工作。
from requests import Request, Session
s = Session()
req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()
# do something with prepped.body
prepped.body = 'No, I want exactly this as the body.'
# do something with prepped.headers
del prepped.headers['Content-Type']
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
由于没有对Request对象进行任何特殊操作,因此可以立即准备它并修改PreparedRequest对象。然后,您将其与您本应发送给requests.*或Session.*的其他参数一起发送。
但是,上述代码将失去Requests Session对象的一些优点。特别是,会话级状态(如Cookie)不会应用于您的请求。要获得应用了该状态的PreparedRequest,请将对Request.prepare()的调用替换为对Session.prepare_Request()的呼叫,如下所示:
from requests import Request, Session
s = Session()
req = Request('GET', url, data=data, headers=headers)
prepped = s.prepare_request(req)
# do something with prepped.body
prepped.body = 'Seriously, send exactly these bytes.'
# do something with prepped.headers
prepped.headers['Keep-Dead'] = 'parrot'
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
当您使用准备好的请求流时,请记住它不考虑环境。如果您使用环境变量来更改请求的行为,这可能会导致问题。例如:REQUESTS_CA_BUNDLE中指定的自签名SSL证书将不予考虑。因此,会抛出SSL:CIFICATE_ERIFY_FAILED。您可以通过将环境设置显式合并到会话中来避免这种行为:
from requests import Request, Session
s = Session()
req = Request('GET', url)
prepped = s.prepare_request(req)
# Merge environment settings into session
settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
resp = s.send(prepped, **settings)
print(resp.status_code)
SSL证书验证
requests验证HTTPS请求的SSL证书,就像web浏览器一样。默认情况下,SSL验证已启用,如果Requests无法验证证书,它将抛出SSLError:
requests.get('https://requestb.in')
# requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
可以使用受信任CA的证书验证CA_BUNDLE文件或目录的路径:
requests.get('https://github.com', verify='/path/to/certfile')
or
s = requests.Session()
s.verify = '/path/to/certfile'
此受信任CA列表也可以通过REQUESTS_CA_BUNDLE环境变量指定。如果未设置REQUESTS_CA_BUNDLE,则CURL_CA_BUNDLE将用作回退。
如果将verify设置为False,则请求也可以忽略验证SSL证书:
requests.get('https://kennethreitz.org', verify=False)
# <Response [200]>
客户端证书
还可以指定一个本地证书作为客户端证书、单个文件(包含私钥和证书)或两个文件路径的元组:
requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
# <Response [200]>
or
s = requests.Session()
s.cert = '/path/client.cert'
如果指定了错误的路径或无效的证书,将收到SSLError:
requests.get('https://kennethreitz.org', cert='/wrong_path/client.pem')
# SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
正文内容工作流
默认情况下,当发出请求时,会立即下载响应正文。可以覆盖此行为,并推迟下载响应正文,直到您使用流参数访问response.content属性:
tarball_url = 'https://github.com/psf/requests/tarball/main'
r = requests.get(tarball_url, stream=True)
此时,只下载了响应标头,连接保持打开状态,因此允许我们对内容检索进行条件化:
if int(r.headers['content-length']) < TOO_LONG:
content = r.content
...
可以使用Response.iter_content()和Response.iter_lines()方法进一步控制工作流。或者可以从Response.raw的urllib3-urllib3.HTTPResponse底层读取未解码的正文。
如果在发出请求时将流设置为True,则除非您消耗所有数据或调用Response.close,否则Requests无法将连接释放回池。这可能会导致连接效率低下。如果你发现自己在使用stream=True时部分读取了请求体(或根本没有读取它们),你应该在with语句中发出请求,以确保它始终关闭:
with requests.get('https://httpbin.org/get', stream=True) as r:
# Do things with the response here.
流式上传
requests支持流式上传,这允许发送大型流或文件,而无需将其读入内存。要流式传输和上传,只需为您的身体提供一个类似文件的对象:
with open('massive-body', 'rb') as f:
requests.post('http://some.url/streamed', data=f)
POST Multiple Multipart-Encoded Files
可以在一个请求中发送多个文件。例如,假设您想将图像文件上传到具有多个文件字段“images”的HTML表单中:
url = 'https://httpbin.org/post'
multiple_files = [
('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
r = requests.post(url, files=multiple_files)
r.text
"""
{
...
'files': {'images': 'data:image/png;base64,iVBORw ....'}
'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
...
}
"""
事件钩子
requests有一个钩子系统,您可以使用它来操纵请求过程的某些部分,或信号事件处理。
可以通过向hooks请求参数传递{hook_name:callback_function}字典来为每个请求分配一个hook函数:
hooks={'response': print_url}
callback_function将接收一块数据作为其第一个参数。
def print_url(r, *args, **kwargs):
print(r.url)
回调函数必须处理自己的异常。任何未处理的异常都不会以静默方式传递,因此应由调用Requests的代码处理。
如果回调函数返回一个值,则假定它将替换传入的数据。如果函数没有返回任何值,则其他内容不受影响。
def record_hook(r, *args, **kwargs):
r.hook_called = True
return r
让我们在运行时打印一些请求方法参数:
requests.get('https://httpbin.org/', hooks={'response': print_url})
#https://httpbin.org/
#<Response [200]>
您可以向单个请求添加多个钩子。让我们同时调用两个钩子:
r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})
r.hook_called
# True
您还可以向Session实例添加钩子。然后,添加的任何钩子都将在向会话发出的每个请求上被调用。例如:
s = requests.Session()
s.hooks['response'].append(print_url)
s.get('https://httpbin.org/')
# https://httpbin.org/
# <Response [200]>
自定义身份验证
requests允许指定自己的身份验证机制。
任何作为auth参数传递给请求方法的可调用对象都有机会在发送请求之前对其进行修改。
身份验证实现是AuthBase的子类,易于定义。Requests在Requests.auth中提供了两种常见的身份验证方案实现:HTTPBasicAuth和HTTPDigestAuth。
让我们假设我们有一个web服务,只有在X-Pizza标头设置为密码值时才会响应。
from requests.auth import AuthBase
class PizzaAuth(AuthBase):
"""Attaches HTTP Pizza Authentication to the given Request object."""
def __init__(self, username):
# setup any auth-related data here
self.username = username
def __call__(self, r):
# modify and return the request
r.headers['X-Pizza'] = self.username
return r
然后,我们可以使用Pizza Auth发出请求:
requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
# <Response [200]>
流式请求
使用Response.iter_lines(),可以轻松地在流式API(如Twitter streaming API)上进行迭代。只需将stream设置为True,并使用iter_lines迭代响应:
import json
import requests
r = requests.get('https://httpbin.org/stream/20', stream=True)
for line in r.iter_lines():
# filter out keep-alive new lines
if line:
decoded_line = line.decode('utf-8')
print(json.loads(decoded_line))
当在Response.iter_lines()或Response.iter_content()中使用decode_unicode=True时,如果服务器没有提供回退编码,您需要提供回退编码:
r = requests.get('https://httpbin.org/stream/20', stream=True)
if r.encoding is None:
r.encoding = 'utf-8'
for line in r.iter_lines(decode_unicode=True):
if line:
print(json.loads(line))
代理
如果需要使用代理,可以使用任何请求方法的代理参数配置单个请求:
import requests
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)
或者,可以为整个会话配置一次:
import requests
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
session = requests.Session()
session.proxies.update(proxies)
session.get('http://example.org')
当代理配置没有如上所示按请求覆盖时,Requests依赖于由标准环境变量http_proxy、https_proxy、no-proxy和all_proxy定义的代理配置。还支持这些变量的大写变体。因此,您可以将它们设置为配置请求(仅设置与您的需求相关的请求):
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ export ALL_PROXY="socks5://10.10.1.10:3434"
$ python
>>> import requests
>>> requests.get('http://example.org')
SOCKS
2.10.0版本中的新功能。
除了基本的HTTP代理之外,Requests还支持使用SOCKS协议的代理。这是一个可选功能,要求在使用前安装其他第三方库。
可以从pip获取此功能的依赖关系:
pip install requests[socks]
安装了这些依赖项后,使用SOCKS代理和使用HTTP代理一样简单:
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}