快速开始
发送请求
>>> import requests
>>> r = requests.get('https://api.github.com/events') # GET
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'}) # POST
>>> r = requests.put('https://httpbin.org/put', data={'key': 'value'}) # PUT
>>> r = requests.delete('https://httpbin.org/delete') # DELETE
>>> r = requests.head('https://httpbin.org/get') # HEAD
>>> r = requests.options('https://httpbin.org/get') # OPTIONS
URL传参
可以使用
params
字典参数为URL提供查询字符串参数,例如,访问
https://httpbin.org/get?key1=value1&key2=value2
,可使用以下代码:
params
https://httpbin.org/get?key1=value1&key2=value2
>>> import requests
>>> payload = {'key1': 'value1', 'key2': 'value2', 'key3':'', 'key4':None}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> r.url
https://httpbin.org/get?key2=value2&key1=value1&key3=
需要注意的是,如果字典参数中key值(即URL参数的值为
None
),则该参数不会添加到URL的查询字符串中。
None
如果URL查询字符串中,存在重复参数(参数名称相同,参数值不同),则需要将key值设置为由参数值组成的列表,如下:
>>> import requests
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> r.url
https://httpbin.org/get?key1=value1&key2=value2&key2=value3
响应内容
读取服务器响应内容
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
<class 'str'> [{"id":"27579847062","type":"PushEvent","actor":{"...
requests 将自动解码来自服务器的内容。大多数unicode字符集都是无缝解码的。
当你发出请求时,requests会根据HTTP头对响应的编码进行有依据的猜测。当你访问
r.text
时,将使用requests猜测的文本编码。可以使用
r.encoding
属性查找请求使用的编码,并对其进行更改:
r.text
r.encoding
>>> r.encoding # 输出:utf-8
r.encoding = 'ISO-8859-1'
如果更改编码,则每当调用
r.text
时,requests都将使用新的
r.encoding
的值。在任何情况下,你都可以应用特殊逻辑来确定内容的编码。例如,HTML和XML可以在其正文中指定其编码。在这种情况下,你应该使用
r.content
查找编码,然后设置
r.encoding
。这将允许你使用具有正确编码的
r.text
。
r.text
r.encoding
r.content
r.encoding
r.text
requests还将在需要时使用自定义编码。如果你已经创建了自己的编码并将其注册到
codecs
模块,则可以简单地使用codec名称作为
r.encoding
的值,而requests将为你处理解码。
codecs
r.encoding
二进制响应内容
对于非文本请求,还可以以字节的形式访问响应体(当然,文本请求也可以):
>>> r.content
b'[{"id":"27581220674","type":"IssueCommentEvent","actor":{"id":327807...
requests会自动解码
gzip
和
deflate
传输编码。
gzip
deflate
如果安装了类似 [brotli]或 [brotlicffi]的Brotil类库,Requets也会自动界面
br
传输编码
br
如果Brotli库(如[Brotli])为您自动解码
br
传输编码或[brotliffi]已安装。
br
例如,可以使用以下代码,从请求返回的二进制数据创建图像:
from PIL import Image
from io import BytesIO
img = Image.open(BytesIO(r.content))
JSON响应内容
可使用内置的JSON解码器,处理JSON数据:
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.json() # JSON
[{'id': '27609416600', 'type': 'PushEvent', ...
如果JSON解码失败,
r.json()
将抛出异常。例如,如果响应得到一个204(无内容),或者如果响应包含无效的JSON,则
r.json()
会抛出
requests.exceptions.JSONDecodeError
。此封装的异常可能会因为不同python版本和JSON序列化库可能引发的多个异常提供互操作性。
r.json()
r.json()
requests.exceptions.JSONDecodeError
需要注意的是,调用
r.json()
的成功调用并不表示响应的成功。一些服务器可能会在失败的响应中返回JSON对象(例如,HTTP 500的错误详细信息)。这样的JSON将被解码并返回。要检查请求是否成功,请使用
r.raise_for_status()
或检查
r.status_code
r.json()
r.raise_for_status()
r.status_code
原始响应内容
可以通过访问
r.raw
访问服务器返回的原始socket响应。如果希望这样做,确保在初始请求中设置
stream=True
:
r.raw
stream=True
>>> import requests
>>> r = requests.get('https://api.github.com/events', stream=True)
>>> r.raw
<urllib3.response.HTTPResponse object at 0x0000018DB1704D30>
>>> 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)
使用
Response.iter_content
将处理很多你在直接使用
Resort.raw
时需要处理的事情。当流式传输下载时,以上是检索内容的首选和推荐方法。请注意,
chunk_size
可以自由调整为更适合你使用场景的数字。
Response.iter_content
Resort.raw
chunk_size
注意
关于使用
Response.iter_content
与
Response.raw
的重要注意事项。
Response.iter_content
将自动解码
gzip
和
deflate
传输编码。
Response.raw
是一个原始字节流–它不会转换响应内容。如果确实需要访问返回的字节,请使用
Response.raw
。
Response.iter_content
Response.raw
Response.iter_content
gzip
deflate
Response.raw
Response.raw
自定义请求头
如果您想向请求中添加HTTP头,只需向
headers
参数传递一个
dict
即可,例如:
headers
dict
>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)
注意:自定义请求头的优先级低于更具体的信息源。例如:
* 如果在
.netrc
中指定了凭据,则使用
headers=
设置的
Authorization
请求头将被覆盖,而凭据又将被
auth=
参数覆盖。请求将在
~/.netrc
、
~/_netrc
或
NETRC
环境变量指定的路径处中搜索netrc文件。
.netrc
headers=
Authorization
auth=
~/.netrc
~/_netrc
NETRC
* 如果从主机重定向,将删除
Authorization
请求头。
Authorization
*
Proxy-Authorization
请求头将被URL中提供的代理凭据覆盖。
Proxy-Authorization
* 当我们可以确定内容的长度时,将覆盖
Content-Length
请求头。
Content-Length
此外,请求根本不会根据指定的自定义请求头更改其行为。请求头仅是简单的传递到最终请求中。
注意:所有请求头值必须是字符串、字节字符串或unicode。虽然允许,但建议避免传递unicode请求头值。
更复杂的POST请求More complicated POST requests
通常,如果发送一些表单编码(form-encoded)的数据–就像一个HTML表单。为此,只需将字典传递给
data
参数即可。发送请求时,将自动对字典数据进行表单编码:
data
>>> import requests
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> 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": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-6409fe3b-0cb4118319f09ab3187402bc"
},
"json": null,
"origin": "183.62.127.25",
"url": "https://httpbin.org/post"
}
data
参数中,为每个键可以具有多个值。这可以通过将
data
设置为元组列表或以列表为值的字典来实现。当表单中有多个元素使用相同的键时,这特别有用:
data
data
>>> import requests
>>> 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)
>>> r1.text
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": [
"value1",
"value2"
]
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-6409ff49-11b8232a7cc81fc0290ec4c4"
},
"json": null,
"origin": "183.62.127.25",
"url": "https://httpbin.org/post"
}
>>> re.text == r2.text
True
有时,你可能想发送未经表单编码的数据,则需要传入
string
类型的数据,而不是
dict
,
string
数据将被直接提交。
string
dict
string
例如,GitHub API v3接受JSON编码的POST/PATCH数据:
>>> import requests
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(payload))
请注意,上述代码不会添加
Content-Type
请求头(特别是不会将其设置为
application/json
)。如果需要设置那个请求头(
'Content-Type': 'application/json
,发送json请求体),并且不想自己对
dict
进行编码,你也可以直接使用
json
参数传递它,它将自动被编码:
Content-Type
application/json
'Content-Type': 'application/json
dict
json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, json=payload)
注意,如果提供了
data
,或者
file
参数,
json
参数将被自动忽略。
data
file
json
提交Multipart-Encoded文件
Request让上传Multipart编码文件变得简单:
>>> import requests
>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)
>>> r.text
{
"args": {},
"data": "",
"files": {
"file": "#!/usr/bin/env python\r\n# -*- coding:utf-8 -*-\r\n\r\n#!/usr/bin/env python\r\n# -*- coding:utf-8 -*-\r\n\r\nfrom multiprocessing import Pool\r\nfrom threading import Thread\r\nfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor..."
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "3035",
"Content-Type": "multipart/form-data; boundary=9ef4437cb1e14427fcba1c42943509cb",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-640a03df-1a0a5ce972ce410378cda7a2"
},
"json": null,
"origin": "183.62.127.25",
"url": "https://httpbin.org/post"
}
可以显示的设置文件名称,内容类型,请求头:
>>> 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
{
"args": {},
"data": "",
"files": {
"file": "data:application/vnd.ms-excel;base64,UEsDBBQAAAAAAHy8iFMAAAAAAA...=="
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "9667",
"Content-Type": "multipart/form-data; boundary=ff85e1018eb5232f7dcab2b2bc5ffa50",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-640def51-43cc213e33437a0e60255add"
},
"json": null,
"origin": "183.62.127.25",
"url": "https://httpbin.org/post"
}
如果想发送一些字符串,以文件的方式被接收:
>>> url = 'https://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
>>> r = requests.post(url, files=files)
>>> r.text
{
"args": {},
"data": "",
"files": {
"file": "some,data,to,send\nanother,row,to,send\n"
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "184",
"Content-Type": "multipart/form-data; boundary=2bfe430e025860528e29c893a09f1198",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-640df132-247947ca699e9da35c588f2d"
},
"json": null,
"origin": "183.62.127.25",
"url": "https://httpbin.org/post"
}
如果你将一个非常大的文件作为
multipart/form-data
请求提交,你可能需要流式传输该请求。默认情况下,
requests
不支持此功能,但有一个单独的包支持此功能——
requests toolbelt
。
multipart/form-data
requests
requests toolbelt
要在一个请求中发送多个文件,请参阅[高级]章节。
警告
强烈建议以[二进制模式]打开文件。这是因为requests可能会尝试为你提供
Content-Length
请求头,如果这样做,该请求头值将被设置为文件中的_字节数_。如果以_文本模式_打开文件,可能会发生错误。
Content-Length
响应状态码
>>> import requests
>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code
200
以便于参考,
requests
还附带一个内置的状态代码查找对象:
requests
>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code == requests.codes.ok
True
如果请求出错4XX客户端错误或5XX服务器错误响应),我们可以使用[
response.raise_for_status()
]抛出错误:
response.raise_for_status()
>>> import requests
>>> 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 "D:/codePojects/test.py", line 12, in <module>
bad_r.raise_for_status()
File "D:\Program Files (x86)\python36\lib\site-packages\requests\models.py", line 960, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404
但是,如果
r.status_code
为
200
,
raise_for_status()
将返回
None
r.status_code
200
raise_for_status()
None
>>> r.raise_for_status()
None
响应头
>>> r.headers
{
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json'
}
根据[RFC 7230] HTTP请求头大小写不敏感,所以,我们可以使用任何大写。因此,我们可以使用任意大小写来访问请求头:
>>> r.headers['Content-Type']
'application/json'
>>> r.headers.get('content-type')
'application/json'
Cookies
如果响应包含Cookie,可以快速访问它们:
>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name'] # 如果存在名为 example_cookie_name的cookie的话
'example_cookie_value'
可以使用
cookies
参数将cookie发送给服务器:
cookies
>>> url = 'https://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{\n "cookies": {\n "cookies_are": "working"\n }\n}\n'
Cookies are returned in a [
RequestsCookieJar
], which acts like a
dict
but also offers a more complete interface, suitable for use over multiple domains or paths. Cookie jars can also be passed in to requests:
RequestsCookieJar
dict
返回的Cookie存储在[
RequestsCookieJar
]中,其作用类似于
dict
,同时提供了一个更完整的接口,适合在多个域或路径上使用。Cookie jar也可以传递给请求:
RequestsCookieJar
dict
>>> jar = requests.cookies.RequestsCookieJar()
>>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
Cookie(version=0, name='tasty_cookie', value='yum', port=None, port_specified=False, domain='httpbin.org', domain_specified=True, domain_initial_dot=False, path='/cookies', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)
>>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
Cookie(version=0, name='gross_cookie', value='blech', port=None, port_specified=False, domain='httpbin.org', domain_specified=True, domain_initial_dot=False, path='/elsewhere', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)
>>> url = 'https://httpbin.org/cookies'
>>> r = requests.get(url, cookies=jar)
>>> r.text
'{"cookies": {"tasty_cookie": "yum"}}'
重定向与history
默认情况下,
requests
将对除
HEAD
之外的所有请求执行位置重定向(如果需要重定向的话)。
requests
HEAD
我们可以使用Response对象的
history
属性来跟踪重定向。
history
[
Response.history
]列表包含为完成请求而创建的[
Response
]对象。列表按响应的先后顺序排序。
Response.history
Response
例如,Gitee将所有HTTP请求重定向到HTTPS:
>>> r = requests.get('http://gitee.com/')
>>> r.url
'https://gitee.com/'
>>> r.status_code
200
>>> r.history
[<Response [302]>]
如果使用HEAD,GET,
OPTIONS
,
POST
,
PUT
,
PATCH
或者
DELETE
,可以使用
allow_redirects
参数禁止重定向:
OPTIONS
POST
PUT
PATCH
DELETE
allow_redirects
>>> r = requests.get('http://gitee.com/', allow_redirects=False)
>>> r.status_code
302
>>> r.history
[]
>>> r = requests.head('http://gitee.com/', allow_redirects=False)
>>> r.url
'http://gitee.com/'
>>> r.status_code
302
>>> r.history
[]
>>> r = requests.head('http://gitee.com/', allow_redirects=True)
>>> r.status_code
200
>>> r.url
'https://gitee.com/'
>>> r.history
[<Response [302]>]
请求超时
可以使用
timeout
参数告诉requests在给定的秒数后停止等待响应。几乎所有的生产代码都应该在几乎所有的请求中使用此参数。否则会导致程序无限期挂起:
timeout
>>> requests.get('https://gitee.com/', timeout=0.1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
...
urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='gitee.com', port=443): Read timed out. (read timeout=0.1)
注意:
timeout
不是整个响应的下载时间限制;相反,如果服务器在
timeout
秒内没有发出响应(更准确地说,如果在
timeout
秒内底层socket没有接收到任何字节数据),则会引发异常。如果未明确指定
timeout
,则请求不会超时。
timeout
timeout
timeout
timeout
错误和异常
如果出现网络问题(例如DNS故障、拒绝连接等),requests将抛出[
ConnectionError
]异常。
ConnectionError
如果HTTP请求返回了失败的状态代码,[
Response.raise_for_statu()
]将抛出[
HTTPError
]
Response.raise_for_statu()
HTTPError
如果请求超时,则会抛出[
Timeout
]异常。
Timeout
如果请求超过了配置的最大重定向次数,则会抛出[
TooManyRedirects
]异常。
TooManyRedirects
requests显式抛出的所有异常都继承自[
requests.exceptions.RequestException
]
requests.exceptions.RequestException
高级用法
Session对象
Session对象允许你跨请求保持某些参数,以及Session实例发出的所有请求的cookie,并将使用
urllib3
的[连接池]([HTTP持久连接]
urllib3
Session对象具有主要 requests API的所有方法。
让我们在请求之间保持一些cookie:
>>> s = requests.Session()
>>> s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
<Response [200]>
>>> r = s.get('https://httpbin.org/cookies')
>>> r.text
'{\n "cookies": {\n "sessioncookie": "123456789"\n }\n}\n'
>>>
Seesion对象还可以用于向请求方法提供默认数据。这是通过向Session对象属性提供数据来实现的:
>>> s = requests.Session()
>>> s.auth = ('user', 'pass')
>>> s.headers.update({'x-test': 'true'})
# 'x-test'和'x-test2'请求头随请求发送了
>>> s.headers.update({'x-test': 'true'})
>>> s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})
<Response [200]>
传递给请求方法的任何字典都将与会话级别设置的值合并。方法级别的参数会覆盖会话级别的参数。
然而,请注意,即使使用会话,方法级参数也不能跨请求保持。本示例将只在发送第一个请求发送cookie,而不发送第二个请求
>>> s = requests.Session()
>>> r = s.get('https://httpbin.org/cookies', cookies={'from-my': 'browser'})
>>> r.text
'{\n "cookies": {\n "from-my": "browser"\n }\n}\n'
>>> r = s.get('https://httpbin.org/cookies')
>>> r.text
'{\n "cookies": {}\n}\n'
[Cookie utility functions]to manipulate [
Session.cookies
]
Session.cookies
如果想手动向Session添加Cookie,那么使用 [Cookie utility functions]来操作[
Session.cookies
]
Session.cookies
Session对象也可以用作上下文管理器
>>> with requests.Session() as s:
... s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
...
<Response [200]>
>>>
这将确保在退出
with
块后立即关闭会话,即使发生未处理的异常。
with
Remove a Value From a Dict Parameter
Sometimes you’ll want to omit session-level keys from a dict parameter. To do this, you simply set that key’s value to `None` in the method-level parameter. It will automatically be omitted.
从字典参数中删除值
有时,你需要从dict参数中忽略会话级别的键。为此,只需在方法级参数中将该键的值设置为“None”即可。它将被自动忽略。
请求和响应对象
示例:获取响应头和请求头
>>> r = s.get('https://httpbin.org')
>>> r.headers # 获取响应头
{'Date': 'Mon, 13 Mar 2023 15:43:41 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '9593', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}
>>> r.request.headers
{'User-Agent': 'python-requests/2.27.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'sessioncookie=123456789'}
>>>
Prepared requests
每当收到来自某个API调用或者Session调用的[
Response
]对象,
request
属性实际上是所使用的
PreparedRequest
。在某些情况下,你可能希望在发送请求之前对请求体或请求头(或其他任何内容)做一些额外的工作。简单的做法如下:
Response
request
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)
However, the above code will lose some of the advantages of having a requests [
Session
] object. In particular, [
Session
]-level state such as cookies will not get applied to your request. To get a [
PreparedRequest
]with that state applied, replace the call to [
Request.prepare()
]with a call to [
Session.prepare_request()
], like this:
Session
Session
PreparedRequest
Request.prepare()
Session.prepare_request()
由于你没有对
Request
对象执行任何特殊操作,因此您可以立即prepare它并修改
PreparedRequest
对象。然后将其与发送给
requests.*
或
Session.*
的其它参数一起发送。
Request
PreparedRequest
requests.*
Session.*
然而,上述代码将失去使用requests [
Session
]对象的一些优点。特别是[
Session
]级别的状态,比如cookie将不会应用于你的请求。如果需要获取应用了那些状态的 [
PreparedRequest
],替换 [
Request.prepare()
]调用为[
Session.prepare_request()
],像这样:
Session
Session
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)
When you are using the prepared request flow, keep in mind that it does not take into account the environment. This can cause problems if you are using environment variables to change the behaviour of requests. For example: Self-signed SSL certificates specified in
REQUESTS_CA_BUNDLE
will not be taken into account. As a result an
SSL: CERTIFICATE_VERIFY_FAILED
is thrown. You can get around this behaviour by explicitly merging the environment settings into your session:
REQUESTS_CA_BUNDLE
SSL: CERTIFICATE_VERIFY_FAILED
当你使用prepared request请求时,请记住它没有考虑环境。如果你正使用环境变量来更改请求的行为,这可能会导致问题。例如:在
REQUESTS_CA_BUNDLE
中指定的自签名SSL证书将不起作用,结果引发了
SSL:CERTIFICATE_VERIFY_FAILED
。你可以通过将环境设置显式合并到Session中来避免这种行为:
REQUESTS_CA_BUNDLE
SSL:CERTIFICATE_VERIFY_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)
HTTP Basic 验证
>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('your_username', 'your_password')
>>> r = requests.post(url='you_target_url', data=body, auth=auth)
SSL证书验证
requests验证HTTPS请求的SSL证书,就像web浏览器一样。默认情况下,SSL验证已启用,如果无法验证证书,请求将抛出SSLError:
>>> requests.get('https://requestb.in')
requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
你可以使用
verify
参数传递拥有受信任CA的证书的CA_BUNDLE文件的路径或者目录:
verify
>>> requests.get('https://github.com', verify='/path/to/certfile')
或者
s = requests.Session()
s.verify = '/path/to/certfile'
注意:
如果
verify
设置为目录的路径,则必须使用OpenSSL提供的
c_rehash
实用程序处理该目录。
verify
c_rehash
还可以通过
REQUESTS_CA_BUNDLE
环境变量指定此受信任CA列表。如果未设置
REQUESTS_CA_BUNDLE
,将使用
CURL_CA_BUNDLE
。
REQUESTS_CA_BUNDLE
REQUESTS_CA_BUNDLE
CURL_CA_BUNDLE
如果将
verify
设置为
False
,则requests也可以忽略SSL证书验证:
verify
False
>>> requests.get('https://kennethreitz.org', verify=False)
<Response [200]>
请注意,当
verify
设置为
False
时,Requests将接受服务器提供的任何TLS证书,并将忽略主机名不匹配,或过期的证书,这将使你的应用程序容易受到中间人(MitM)攻击。在本地开发或测试期间,将
verify
设置为
False
可能很有用。
verify
False
verify
False
默认情况下,
verify
设置为
True
。选项
verify
仅适用于主机证书。
verify
True
verify
客户端证书
你还可以将本地证书指定为客户端证书、单个文件(包含私钥和证书)或两个文件路径的元组
>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>
或者:
s = requests.Session()
s.cert = '/path/client.cert'
警告
本地证书的私钥必须为未加密的。当前,Requests不支持加密的私钥
CA证书
Reuests使用来自[certific]包中的证书. 这允许用户在不更改Requests版本的情况下更新其受信任的证书。
在2.16版本之前,Requests捆绑了一组其信任的根CA证书,证书来源于[Mzillatruststore]。每个Request版本只更新一次证书。当未安装
certific
时,当使用较旧版本的requests时,这会导致证书包非常过时。
certific
为了安全起见,我们建议经常升级
certific
!
certific
Body内容工作流
默认情况下,当你发出一个请求时,将立即下载响应的正文。你可以使用
stream
参数覆盖此行为并延迟下载响应主体直到访问[
response.content
]属性
stream
response.content
tarball_url = 'https://github.com/psf/requests/tarball/main'
r = requests.get(tarball_url, stream=True)
此时,仅仅响应头被下载,且连接保持打开状态,因此,允许我们有条件的检索内容:
if int(r.headers.get('content-length')) < TOO_LONG:
content = r.content
...
您可以使用 [
Response.iter_content()
] 和[
Response.iter_lines()
] 方法进一步控制工作流。或者,可以从位于[
Response.raw
]的底层的[
urllib3.HTTPResponse
]中读取未编码的主体.
Response.iter_content()
Response.iter_lines()
Response.raw
urllib3.HTTPResponse
如果在发出请求时将
stream
设置为
True
,则requests无法释放连接回连接池,除非读取完所有数据或调用[
Response.close
](。这可能导致连接效率低下。如果你发现自己在使用
stream=True
时部分读取请求体(或根本没有读取它们),则应在
with
语句中发出请求,以确保连接最终处于关闭状态:
stream
True
Response.close
stream=True
with
with requests.get('https://httpbin.org/get', stream=True) as r:
# Do things with the response here.
Keep-Alive
多亏了
urllib3
,
keep-alive
在Session中是100%自动的!你在Session发出的任何请求都将自动重用合适的连接!
urllib3
keep-alive
注意,只有在读取了所有响应体数据后,才会将连接释放回连接池以供重用;请确保将
stream
设置为
False
或读取
Response
对象的
content
属性。
stream
False
Response
content
流式上传
requests支持流式上传,允许发送大型流或文件,而无需将其读入内存。要流式传输和上传,只需为请求体提供一个类似文件的对象:
with open('massive-body', 'rb') as f:
requests.post('http://some.url/streamed', data=f)
警告
强烈建议以[二进制模式]打开文件。这是因为requests可能会尝试为你提供
Content-Length
请求头,如果这样做,该请求头值将被设置为文件中的_字节数_。如果以_文本模式_打开文件,可能会发生错误。
Content-Length
分块编码(Chunk-Encoded)请求
requests 还支持传出和传入请求的分块传输编码。要发送块编码请求,只需简单的为请求体提供一个生成器(或任何没有长度的迭代器)
def gen():
yield 'hi'
yield 'there'
requests.post('http://some.url/chunked', data=gen())
对于分块编码请求的响应,最好使用[
Response.iter_content()
]对数据进行迭代。在理想情况下,将在请求上设置
stream=True
,在这种情况下,可以通过使用值为
None
的
chunk_size
参数调用
iter_content
来逐块迭代。如果要设置块的最大大小,可以将
chunk_size
参数设置为任意目标大小整数。
Response.iter_content()
stream=True
None
chunk_size
iter_content
chunk_size
POST 多个分部编码(Multipart-Encoded)文件
你可以在一个请求中发送多个文件。例如,假设你要将图像文件上载到具有多个文件字段“images”的HTML表单:
<input type="file" name="images" multiple="true" required="true"/>
为此,只需将
files
设置为
(form_field_name,file_info)
的元组列表:
files
(form_field_name,file_info)
>>> 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
>>> r.text
'{\n "args": {}, \n "data": "", \n "files": {\n "images": "data:image/png;base64,iVBORw0KGgoAAAAN...=="\n }, \n "form": {}, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Content-Length": "1800", \n "Content-Type": "multipart/form-data; boundary=771ef90459071106c5f47075cbca2659", \n "Host": "httpbin.org", \n "User-Agent": "python-requests/2.27.1", \n "X-Amzn-Trace-Id": "Root=1-641122ea-10a6271f0fdf488c70cf90e9"\n }, \n "json": null, \n "origin": "183.62.127.25", \n "url": "https://httpbin.org/post"\n}\n'
事件钩子(Event Hooks)
requests拥有一个hook系统,可用于控制请求过程的部分,或者信号事件处理。
可用的hooks:
-
response
:
请求生成的响应
通过将
{hook_name:callback_function}
字典传递给
hooks
请求参数,可以按每个请求分配一个钩子函数:
{hook_name:callback_function}
hooks
hooks={'response': print_url}
callback_function
将接收一数据块(a chunk of data)作为其第一个参数。
callback_function
def print_url(r, *args, **kwargs):
print(r.url)
回调函数必须处理其自己的异常。任何为处理的异常,都不会以静默方式传递,因此应该由代码调用请求来处理。
如果回调函数返回某个值,则假定它将替换传入的数据。如果函数不返回任何内容,则不产生任何影响
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
实例添加钩子,这样添加的任何钩子都将在向会话发出的每个请求中被调用。例如:
Session
>>> s = requests.Session()
>>> s.hooks['response'].append(print_url)
>>> s.get('https://httpbin.org/')
https://httpbin.org/
<Response [200]>
如果
Session
实例可个钩子函数,那么将按钩子的添加顺序调用这些钩子。
Session
自定义身份验证
requests 请求支持自定义身份验证机制。
作为
auth
参数传递给请求方法的任何可调用对象都有机会在发送请求之前修改请求。
auth
身份验证实现为[
AuthBase
]的子类,并且易于定义。requests在
requests.auth
中提供了两种常见的身份验证方案实现:[
HTTPBasicAuth
]和[
HTTPDigestAuth
].
AuthBase
requests.auth
HTTPBasicAuth
HTTPDigestAuth
假设我们有一个web服务,它只有在
X-Pizza
请求头设置为密码值时才会响应。这不太可能,暂且还是顺着它:
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
然后,发送请求
>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>
流式请求
使用[
Response.iter_lines()
],可以很轻易的迭代流式API,比如 [Twitter Streaming API]。简单的设置
stream
为
True
并且使用[
iter_lines
]对响应进行迭代:
Response.iter_lines()
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))
将
decode_unicode=True
与 [
Response.iter_lines()
]、 或者[
Response.iter_content()
]配合使用时,如果服务器未提供编码,则需要提供编码:
decode_unicode=True
Response.iter_lines()
Response.iter_content()
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))
警告
[
iter_lines
]不是可重入安全的。多次调用此方法会导致一些接收到的数据丢失。如果需要从多个地方调用它,请使用生成的迭代器对象:
iter_lines
lines = r.iter_lines()
# Save the first line for later or just skip it
first_line = next(lines)
for line in lines:
print(line)
代理
如果你需要使用代理,可在任何请求方法的
proxys
参数中为单个请求配置代理
proxys
import requests
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)
可选的,可以一次性为整个Session配置代理。
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')
警告
为
session.proxies
提供的值可能被环境代理(由[urllib.request.getproxys]返回的值)覆盖,所以为了确保在环境代理存在的情况下,也使用给定代理,显示为所有单个请求指定
proxies
参数,如上述一开始所述。
session.proxies
proxies
如果没有为请求设置
proxies
请求参数的情况下,requests会尝试读取由标准环境变量
http_proxy
,
https_proxy
,
no_proxy
和
all_proxy
定义的代理配置。这些变量名称可大写。所以,可以通过这些变量配置为请求设置代理(请根据实际需要配置):
proxies
http_proxy
https_proxy
no_proxy
all_proxy
linux:
$ 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')
win:
set HTTP_PROXY=http://10.10.1.10:3128
>>> import requests
>>> requests.get('http://example.org')
要对代理使用HTTP基本身份验证,请在上述任意代理配置入口中使用语法:
$ export HTTPS_PROXY="http://user:pass@10.10.1.10:1080"
$ python
>>> proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}
警告
将敏感的用户名和密码信息存储在环境变量或版本控制的文件中会带来安全风险,强烈建议不要这样做。
如果要为特定shema和主机提供代理,请使用
scheme://hostname
作
proxies
字典参数的键来设置代理。这将匹配给定scheme和确切主机名的任何请求。
scheme://hostname
proxies
proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}
注意,代理URL必须包含schema。
最后需要注意的,为
https
连接设置代理,通常需要所在本机机器信任代理根证书。默认的,可以通过以下代码查找requests信任的证书列表:
https
from requests.utils import DEFAULT_CA_BUNDLE_PATH
print(DEFAULT_CA_BUNDLE_PATH)
通过将
REQUESTS_CA_BUNDLE
(or
CURL_CA_BUNDLE
) 环境变量设置为另一个文件路径,可以覆盖此证书路径:
REQUESTS_CA_BUNDLE
CURL_CA_BUNDLE
$ export REQUESTS_CA_BUNDLE="/usr/local/myproxy_info/cacert.pem"
$ export https_proxy="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get('https://example.org')
SOCKS
版本2.10.0中新增
除了基本的HTTP代理之外,requests还支持使用SOCKS协议的代理。这是一项可选功能,要求在使用前安装其他第三方库
可通过
pip
获取该功能需要的依赖:
pip
$ python -m pip install requests[socks]
安装依赖后,使用SOCKS代理就同使用HTTP代理一样简单:
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
使用
socks5
会导致DNS解析发生在客户端上,而不是代理服务器上。这与
curl
保持一致,curl使用scheme来决定是在客户端还是代理服务器上进行DNS解析。如果要解析代理服务器上的域,请使用
socks5h
作为scheme
socks5
curl
socks5h
编码
当收到响应时,并访问
Response.text
属性时,requests会猜测用于解码响应体的编码。requests将首先检查HTTP请求头中的编码,如果不存在,则使用[charset_normalizer]尝试猜测编码。
Response.text
如果安装了
chardet
,
requests
将使用它,但对于python3来说,
chardet
不再是强制依赖项。
chardet
requests
chardet
当安装
requests
时,没有指定
[use_chardet_on_py3]
,并且
chardet
尚未安装时,requests将使用
charset normalizer
来猜测编码。
requests
[use_chardet_on_py3]
chardet
charset normalizer
requests不会猜测编码的唯一情况是HTTP请求头中不存在显示字符集且
Content-Type
请求头包含
text
。在这种情况下,[RFC 2616]指定默认字符集必须是
ISO-8859-1
。requests遵循该规范。如果需要不同的编码,您可以手动设置[
Response.conding
])属性,或使用原始[
Response.content
]
Content-Type
text
ISO-8859-1
Response.conding
Response.content
Link请求头
许多HTTP API具有link请求头。它们使API更加自我描述和可发现。
>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
>>> r = requests.head(url=url)
>>> r.headers['link']
'<https://api.github.com/user/119893/repos?page=2&per_page=10>; rel="next", <https://api.github.com/user/119893/repos?page=5&per_page=10>; rel="last"'
requests 将自动解析这link请求头并且让它们更容易被使用:
>>> r.links["next"]
{'url': 'https://api.github.com/user/119893/repos?page=2&per_page=10', 'rel': 'next'}
>>> r.links["last"]
{'url': 'https://api.github.com/user/119893/repos?page=5&per_page=10', 'rel': 'last'}
传输适配器(Transport Adapters)
从v1.0.0开始,requests 已模块化内部设计。这样做的部分原因是为了实现传输适配器,最初[在此处描述]. 传输适配器提供了一种机制来定义某个HTTP服务的交互方法。特别是,它们允许你应用每个服务的配置。
requests附带单个传输适配器[
HTTPAdapter
]. 此适配器使用功能强大的
urllib3
提供与HTTP和HTTPS的默认请求交互。当初始化 requests [
Session
]时,其中一个附加到[
Session
]对象表示HTTP,一个表示HTTPS。
HTTPAdapter
urllib3
Session
Session
户能够创建和使用自己的具备特定功能的传输适配器。一旦创建,传输适配器就可以加载到会话对象,并指示它应该应用于哪些web服务。
>>> s = requests.Session()
>>> s.mount('https://github.com/', MyAdapter())
上述
mount
调用将传输适配器的指定实例注册到URL前缀中。一旦挂载,使用该session发起的,URL以给定前缀开头的任何HTTP请求都将使用给定的传输适配器。
mount
实现传输适配器的许多细节超出了本文档的范围,但是可以看下一个简单SSL使用示例。除此之外,您还可以考虑继承[
BaseAdapter
]\实现子类适配器。
BaseAdapter
示例: 指定SSL版本
The requests team has made a specific choice to use whatever SSL version is default in the underlying library ([urllib3]). Normally this is fine, but from time to time, you might find yourself needing to connect to a service-endpoint that uses a version that isn’t compatible with the default.
You can use Transport Adapters for this by taking most of the existing implementation of HTTPAdapter, and adding a parameter
ssl_version
that gets passed-through to urllib3. We’ll make a Transport Adapter that instructs the library to use SSLv3:
默认情况下,requests选择使用底层
urllib3
库中默认的SSL版本。 通常情况下,这是可以的,但有时,您可能会发现自己需要连接到使用与默认版本不兼容的SSL版本的服务端。
urllib3
为此,可以通过继承
HTTPAdapter
实现自定义传输适配器,
HTTPAdapter
示例:编写一个适配器,指示库使用SSLv3:
import ssl
from urllib3.poolmanager import PoolManager
from requests.adapters import HTTPAdapter
class Ssl3HttpAdapter(HTTPAdapter):
""""Transport adapter" that allows us to use SSLv3."""
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(
num_pools=connections, maxsize=maxsize,
block=block, ssl_version=ssl.PROTOCOL_SSLv3)
阻塞或非阻塞
有了默认的传输适配器,requests就不会提供任何类型的非阻塞IO。[
Response.content
]属性将阻塞,直到下载完整个响应为止。如果你需要更大的粒度,则库的流式传输功能(请参阅[流式传输请求])允许单次接收较小数量的响应那日。然而,这些调用仍然是阻塞。
Response.content
如果您关心阻塞IO的使用,那么有很多项目将请求与Python的异步框架结合在一起。一些很好的例子是 [requests-threads], [grequests], [requests-futures], 和[httpx].
超时
大多数对外部服务器的请求都应该附加超时,以防服务器没有及时响应。默认情况下,除非显式设置了超时时间,否则requests不会超时。如果没有超时,你的代码可能会挂起几分钟或更长时间。
连接超时是requests等待客户端建立与远程计算机的socke连接的秒数。将连接超时设置为略大于3的倍数是一种很好的做法,因为3秒是默认的[TCP数据包重传窗口].
一旦客户端连接到服务器并发送HTTP请求后,
读取
超时是客户端等待服务器返回响应的秒数(具体来说,这是客户端等待服务器返回字节数据的秒数。在99.9%的情况下,这是服务器返回第一个字节之前的等待时间)。
如果需要为请求设置一个超时时间,可以为
timeout
参数指定一个具体的时间值:
timeout
r = requests.get('https://github.com', timeout=5)
该超时时间将同时应用于连接超时和读取超时。如果想为连接超时和读取超时分别设置不同的等待时间,可以指定一个元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
如果服务很慢,想让requests一直等待响应直到获取响应,可以指定
timeout
参数值为
None
timeout
None
r = requests.get('https://github.com', timeout=None)
最后,编程资料、学习路线图、源代码、软件安装包等!都可以【
点击这里
】领取!码文不易,求各位看官动动小手给个关注吧~
版权声明:本文为2301_78094860原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。