快速入门¶
急于开始?此页面将很好地介绍如何开始使用 Requests。
首先,确保
让我们从一些简单的示例开始。
发出请求¶
使用 Requests 发出请求非常简单。
首先导入 Requests 模块
>>> import requests
现在,让我们尝试获取一个网页。对于此示例,让我们获取 GitHub 的公开时间线
>>> r = requests.get('https://api.github.com/events')
现在,我们有一个名为 r
的 Response
对象。我们可以从此对象获取所需的所有信息。
Requests 的简单 API 意味着所有形式的 HTTP 请求都显而易见。例如,以下是如何发出 HTTP POST 请求
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'})
不错,对吧?其他 HTTP 请求类型(PUT、DELETE、HEAD 和 OPTIONS)呢?这些都一样简单
>>> 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')
这些都很好,但这只是 Requests 所能做的事情的开始。
在 URL 中传递参数¶
您通常希望在 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
请注意,任何字典键的值为 None
都不会添加到 URL 的查询字符串中。
您还可以将项目列表作为值传递
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> print(r.url)
https://httpbin.org/get?key1=value1&key2=value2&key2=value3
响应内容¶
我们可以读取服务器响应的内容。再次考虑 GitHub 时间线
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests 将自动解码来自服务器的内容。大多数 unicode 字符集都可以无缝解码。
当您发出请求时,Requests 会根据 HTTP 标头对响应的编码进行合理的猜测。当您访问 r.text
时,将使用 Requests 猜测的文本编码。您可以使用 r.encoding
属性找出 Requests 正在使用什么编码并对其进行更改
>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'
如果您更改编码,则每当您调用 r.text
时,Requests 都会使用 r.encoding
的新值。您可能希望在任何可以应用特殊逻辑来确定内容编码的情况下执行此操作。例如,HTML 和 XML 能够在其正文中指定其编码。在这种情况下,您应该使用 r.content
查找编码,然后设置 r.encoding
。这将允许您使用正确的编码使用 r.text
。
如果您需要,Requests 还会使用自定义编码。如果您创建了自己的编码并使用 codecs
模块对其进行了注册,则只需将编解码器名称用作 r.encoding
的值,Requests 就会为您处理解码。
二进制响应内容¶
对于非文本请求,您还可以将响应正文作为字节访问
>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...
gzip
和 deflate
传输编码会自动为您解码。
如果安装了 Brotli 库,如 brotli 或 brotlicffi,则 br
传输编码会自动为您解码。
例如,要从请求返回的二进制数据中创建图像,可以使用以下代码
>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))
JSON 响应内容¶
如果您处理的是 JSON 数据,还有一个内置的 JSON 解码器
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.json()
[{'repository': {'open_issues': 0, 'url': 'https://github.com/...
如果 JSON 解码失败,r.json()
会引发异常。例如,如果响应收到 204(无内容),或者如果响应包含无效的 JSON,则尝试 r.json()
会引发 requests.exceptions.JSONDecodeError
。此包装异常提供对不同 Python 版本和 JSON 序列化库可能抛出的多个异常的互操作性。
需要注意的是,对 r.json()
的调用的成功并不表示响应的成功。一些服务器可能会在失败的响应中返回 JSON 对象(例如,HTTP 500 的错误详细信息)。此类 JSON 将被解码并返回。要检查请求是否成功,请使用 r.raise_for_status()
或检查 r.status_code
是否符合您的预期。
原始响应内容¶
在极少数情况下,如果您想从服务器获取原始套接字响应,您可以访问 r.raw
。如果您想执行此操作,请确保在初始请求中设置 stream=True
。完成后,您可以执行以下操作
>>> r = requests.get('https://api.github.com/events', stream=True)
>>> r.raw
<urllib3.response.HTTPResponse object at 0x101194810>
>>> 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
将处理许多您在直接使用 Response.raw
时必须处理的内容。在流式传输下载时,上述方法是检索内容的首选且推荐的方法。请注意,chunk_size
可以自由调整为更适合您的用例的数字。
注意
关于使用 Response.iter_content
与 Response.raw
的重要说明。 Response.iter_content
会自动解码 gzip
和 deflate
传输编码。 Response.raw
是字节的原始流 - 它不会转换响应内容。如果您确实需要访问返回的字节,请使用 Response.raw
。
自定义标头¶
如果您想向请求添加 HTTP 标头,只需将 dict
传递给 headers
参数。
例如,我们在前面的示例中没有指定我们的用户代理
>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)
注意:与更具体的信息来源相比,自定义标头的优先级较低。例如
如果在
.netrc
中指定了凭据,则使用 headers= 设置的授权标头将被覆盖,而.netrc
又会被auth=
参数覆盖。Requests 将在 ~/.netrc、~/_netrc 或 NETRC 环境变量指定的路径中搜索 netrc 文件。如果您被重定向到主机外,授权标头将被删除。
URL 中提供的代理凭证将覆盖 Proxy-Authorization 标头。
当我们可以确定内容长度时,将覆盖 Content-Length 标头。
此外,Requests 不会根据指定哪些自定义标头而改变其行为。标头只是传递到最终请求中。
注意:所有标头值都必须是 string
、字节串或 unicode。虽然允许,但建议避免传递 unicode 标头值。
更复杂的 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"
},
...
}
对于每个键,data
参数也可以具有多个值。这可以通过将 data
设置为元组列表或将列表作为值传递的字典来完成。当表单具有使用相同键的多个元素时,这尤其有用
>>> 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
有时您可能希望发送未进行表单编码的数据。如果您传递 string
而不是 dict
,则该数据将直接发布。
例如,GitHub API v3 接受 JSON 编码的 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)
请注意,如果传递了 data
或 files
,则会忽略 json
参数。
发布多部分编码文件¶
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>"
},
...
}
如果您愿意,可以发送字符串以作为文件接收
>>> 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
{
...
"files": {
"file": "some,data,to,send\\nanother,row,to,send\\n"
},
...
}
如果您要将非常大的文件作为 multipart/form-data
请求发布,您可能希望对请求进行流式处理。默认情况下,requests
不支持此功能,但有一个单独的包支持此功能 - requests-toolbelt
。您应该阅读 toolbelt 的文档 以获取有关如何使用它的更多详细信息。
有关在一个请求中发送多个文件,请参阅 高级 部分。
警告
强烈建议您在二进制模式下打开文件。这是因为 Requests 可能会尝试为您提供Content-Length
头,如果它这样做,此值将设置为文件中的字节数。如果您在文本模式下打开文件,可能会发生错误。
响应状态代码¶
我们可以检查响应状态代码
>>> r = requests.get('https://httpbin.org/get')
>>> 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()
时,我们会得到
>>> r.raise_for_status()
None
一切正常。
响应头¶
我们可以使用 Python 字典查看服务器的响应头
>>> r.headers
{
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json'
}
但是,这个字典很特殊:它专门用于 HTTP 头。根据RFC 7230,HTTP 头名称不区分大小写。
因此,我们可以使用任何我们想要的大小写来访问头
>>> r.headers['Content-Type']
'application/json'
>>> r.headers.get('content-type')
'application/json'
它的另一个特殊之处在于,服务器可能会使用不同的值多次发送同一个头,但 Requests 会将它们组合起来,以便按照RFC 7230中的规定,在单个映射中以字典的形式表示它们
接收方可以将多个具有相同字段名称的头字段组合成一个“字段名:字段值”对,而不改变消息的语义,方法是按顺序将每个后续字段值附加到组合字段值中,并用逗号分隔。
重定向和历史记录¶
默认情况下,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
[]
如果您正在使用 HEAD,您也可以启用重定向
>>> 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)
注意
timeout
并不是对整个响应下载的时间限制;而是,如果服务器在 timeout
秒内未发出响应(更确切地说,如果在底层套接字上未收到任何字节 timeout
秒),则会引发异常。如果没有明确指定超时,请求不会超时。
错误和异常¶
在出现网络问题(例如 DNS 故障、拒绝连接等)时,Requests 将引发 ConnectionError
异常。
Response.raise_for_status()
如果 HTTP 请求返回不成功的状态代码,将引发 HTTPError
。
如果请求超时,将引发 Timeout
异常。
如果请求超过配置的最大重定向次数,将引发 TooManyRedirects
异常。
Requests 明确引发的所有异常都继承自 requests.exceptions.RequestException
。
准备了解更多?查看 高级 部分。