1. Requests库 开发哲学
Requests 是以 PEP 20 的箴言为中心开发的
- Beautiful is better than ugly.(美丽优于丑陋)
- Explicit is better than implicit.(直白优于含蓄)
- Simple is better than complex.(简单优于复杂)
- Complex is better than complicated.(复杂优于繁琐)
- Readability counts.(可读性很重要)
注意Requests是以Apache2协议发布的。
2. 快速上手
2.1 发送请求
import requests
r = requests.get('https://api.github.com/events')
r是一个Response对象,其内部有header, text, status_code等参数。
类似的,对于其它类型的请求,我们有
r = requests.put('http://httpbin.org/put', data = {'key':'value'})
r = requests.delete('http://httpbin.org/delete')
r = requests.head('http://httpbin.org/get')
r = requests.options('http://httpbin.org/get')
2.2 传递URL参数
使用formData类型传递参数。
formData = {
'key1': 'value1', 'key2': 'value2'
}
r = requests.get("http://httpbin.org/get", params=formData)
print(r.url)
我们输出被正确编码的网址。
http://httpbin.org/get?key1=value1&key2=value2
2.3 响应内容
2.3.1 普通响应
import requests
r = requests.get('https://api.github.com/events')
print(r.text)
request会自动推测编码的类型,当然,你也可以手动设置编码方式。
print(r.encoding)
# out : 'utf-8'
r.encoding = 'ISO-8859-1'
2.3.2 二进制响应
对于非文本请求,可以使用r.content的值来获得响应字节。
from PIL import Image
from io import BytesIO
# 读取r.content图片
i = Image.open(BytesIO(r.content))
2.3.3 JSON响应
import requests
r = requests.get('https://api.github.com/events')
r.json()
需要注意,成功调用r.json()并不意味着响应一定是成功的。有的服务器会在失败的响应中包含一个JSON对象(比如HTTP500的错误细节)。要使用 **r.raise_for_status()**或者检查 r.status_code来确认请求的成功与否。
2.3.4 原始响应内容
直接获取来自服务器的socket响应。
在请求时,打开开关stream=True,并且使用r.raw变量。
r = requests.get('https://api.github.com/events', stream=True)
r.raw
print(r.raw.read(10))
通常情况下,我们将响应的文本流保存至文件。
with open(filename, 'wb') as fd:
for chunk in r.iter_content(chunk_size):
fd.write(chunk)
这种模式我们通常用于下载文件。
2.4 定制请求头
url = 'https://api.github.com/some/endpoint'
headers = {
'user-agent': 'my-app/0.0.1'
}
r = requests.get(url, headers=headers)
header有时会被高优先级的信息源覆盖。
- 如果在
.netrc
中设置了用户认证信息,使用 headers= 设置的授权就不会生效。而如果设置了auth=
参数,.netrc
的设置就无效了。 - 如果被重定向到别的主机,授权 header 就会被删除。
- 代理授权 header 会被 URL 中提供的代理身份覆盖掉。
- 在我们能判断内容长度的情况下,header 的 Content-Length 会被改写。
更进一步讲,Requests 不会基于定制 header 的具体情况改变自己的行为。只不过在最后的请求中,所有的 header 信息都会被传递进去。
2.5 构造复杂的POST请求
2.5.1 使用Dict模拟formData
payload = {
'key1': 'value1', 'key2': 'value2'
}
r = requests.post("http://httpbin.org/post", data=payload)
print(r.text)
2.5.2 使用Tuple模拟formData
payload = (('key1', 'value1'), ('key1', 'value2'))
r = requests.post('http://httpbin.org/post', data=payload)
print(r.text)
使用这种构造formData,可以将多个键值设为相同。此时,相同键值的value会合并为一个列表。
{
...
"form": {
"key1": [
"value1",
"value2"
]
},
...
}
2.5.3 构造一个字符串形式的请求
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
也可以使用json参数直接传递。
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, json=payload)
2.5.4 传递一个多部分编码的文件(Multipart Encoded File)
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)
print(r.text)
你可以显式地设置文件名,文件类型和请求头:
url = 'http://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
# output:
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
还有一个挺无聊的功能,可以把字符串作为文件来发送。
url = 'http://httpbin.org/post'
files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
r = requests.post(url, files=files)
print(r.text)
# output:
{
...
"files": {
"file": "some,data,to,send\\nanother,row,to,send\\n"
},
...
}
如果你发送一个非常大的文件作为 multipart/form-data
请求,你可能希望将请求做成数据流。默认下 requests
不支持, 但有个第三方包 requests-toolbelt
是支持的。你可以阅读 toolbelt 文档 来了解使用方法。
我们推荐使用二进制的形式打开文件,以防止被错误地默认操作,因为requests库太聪明了,聪明反被聪明误。
2.6 响应状态码
r = request.get('http://httpbin.org/get')
print(r.status_code) # output: 200
库中有常量可供引用。
r.status_code == requests.codes.ok
# output : True
当响应异常时,我们使用Response.raise_for_status()主动抛出异常(因为库函数默认不会抛出异常)。该函数在响应正常时不会抛出异常,无返回值(即返回值为None)。
bad_r = requests.get('http://httpbin.org/status/404')
print(bad_r.status_code)
# output: 404
bad_r.raise_for_status()
2.7 响应头
响应由响应头和响应体构成。r.header对象是响应头,它是一个字典。
print(r.headers)
# output:
{
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json'
}
2.8 Cookie
2.8.1 接收响应Cookie
url = 'http://example.com/some/cookie/setting/url'
r = requests.get(url)
print(r.cookies['example_cookie_name'])
# output:
'example_cookie_value'
2.8.2 发送Cookie
url = 'http://httpbin.org/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies)
print(r.text)
# output:
'{"cookies": {"cookies_are": "working"}}'
Cookie的返回对象是RequestsCookieJar,它的行为和字典类似,但接口更为完整,适合跨域名跨路径使用。你还可以把 Cookie Jar 传到 Requests 中:
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 = 'http://httpbin.org/cookies'
r = requests.get(url, cookies=jar)
# 只会自动使用指定domain和path下的cookie,因此适合跨域名跨路径使用
r.text
'{"cookies": {"tasty_cookie": "yum"}}'
2.9 重定向与历史请求
默认情况下,除了 HEAD, Requests 会自动处理所有重定向。
可以使用响应对象的 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]>]
2.10 超时,错误与异常
2.10.1 超时
生产代码中,我们需要使用超时限制,否则程序可能永远失去响应。
>>> requests.get('http://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)
2.10.2 错误与异常
遇到网络问题(如:DNS 查询失败、拒绝连接等)时,Requests 会抛出一个 ConnectionError
异常。
如果 HTTP 请求返回了不成功的状态码, Response.raise_for_status() 会抛出一个 HTTPError
异常。
若请求超时,则抛出一个 Timeout
异常。
若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects
异常。
所有Requests显式抛出的异常都继承自 requests.exceptions.RequestException
。