上一个章节我们讲了如何快速使用Requests
发送网络请求、处理URL
参数和提取响应内容,这些是最基本的操作。
然而还有很多场景下,我们的网络请求更加复杂。比如我们必须要定制请求头来假装成浏览器,不然可能会被网站识别为机器并且被屏蔽;又比如我们需要在发送请求时以表单形式携带部分参数,以获得定制化的响应结果;甚至我们还要在请求时上传一个文件……
今天我们就来看一下如何在Requests
中优雅地实现这些功能。
一、定制请求头
这里的用法和urllib.request.Request
类里的请求头定制方法相似,都是将请求头的信息以字典的形式传给headers
参数,比如:
import requests
headers = {
'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61')
}
r = requests.get('https://www.baidu.com', headers=headers)
过于简单以至于没什么好说的。
二、在POST请求中携带参数
上一节课我们演示了在GET
请求中如何直接传递URL
参数。在POST
请求中一样简单,参数既能以字典形式传入,也能以元组形式传入。
import json
params1 = {
'name': '老Q在折腾',
'platform': ['公众号', '头条号', '知乎']
}
params2 = (('name', '老Q在折腾'), ('platform', '公众号'),
('platform', '头条号'), ('platform', '知乎'))
r1 = requests.post("http://httpbin.org/post", data=params1)
print(json.loads(r1.text))
r2 = requests.post("http://httpbin.org/post", data=params2)
print(json.loads(r2.text))
可以看到,不管是以字典形式还是以元组形式,两者在form
部分输出的结果是一样的,这意味着两种传参形式是等价的。
有些时候,我们可能传递的是一组字符串而非一个字典,那么我们就需要对数据进行编码。或者将字典数据传递给json
参数,如:
import json
url = 'https://api.github.com/some/endpoint'
params = {
'name': '老Q在折腾',
'platform': ['公众号', '头条号', '知乎']
}
r1 = requests.post(url, data=json.dumps(params))
r2 = requests.post(url, json=params)
三、POST上传多部分编码文件
多部分编码,顾名思义,是指一个消息头包含多个消息体的编码方式。这类编码主要用于发送非文本的各类媒体数据。
在Requests
中,传输一个多部分编码文件依然优雅。
url = 'http://httpbin.org/post'
files = {'file': open('text.xlsx', 'rb')}
r = requests.post(url, files=files)
我们还可以显示地声明文件名、文件类型和请求头,并且将字符串以文件形式传入。
url = 'http://httpbin.org/post'
files = {
'file': (
'test.csv',
'abasdf,asdfa,你好,测试,asdf',
'application/vnd.ms-excel',
{'Expires': '0'}
)}
r = requests.post(url, files=files)
json.loads(r.text)
我们将字符串'abasdf,asdfa,你好,测试,asdf'
以文件方式传递。在响应体中,可以看到它被识别到了files参数中。
{'args': {},
'data': '',
'files': {'file': 'abasdf,asdfa,你好,测试,asdf'},
'form': {},
'headers': {'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Length': '227',
'Content-Type': 'multipart/form-data; boundary=ee2379952200a33908d46cc0fc0d04ba',
'Host': 'httpbin.org',
'User-Agent': 'python-requests/2.28.2',
'X-Amzn-Trace-Id': 'Root=1-63e3a983-45f9921b6d14fa3368711d0c'},
'json': None,
'origin': 'xxx.xxx.xxx.xx',
'url': 'http://httpbin.org/post'}
四、使用Cookie
我们可以直接用字典对象来传递Cookie
,也可以使用requests.cookies.RequestsCookieJar
对象来实现,后者的接口更完整,适合跨域名和跨路径使用。
url = 'http://httpbin.org/cookies'
cookies = {
'KEY1': 'VALUE1'
}
# 以字典形式传入
r1 = requests.get(url, cookies=cookies)
print(r1.text)
# 以`RequestsCookieJar`传入
jar = requests.cookies.RequestsCookieJar()
jar.set('KEY2', 'VALUE2', domain='httpbin.org', path='/cookies')
r2 = requests.get(url, cookies=jar)
print(r2.text)
输出为:
{
"cookies": {
"KEY1": "VALUE1"
}
}
{
"cookies": {
"KEY2": "VALUE2"
}
}
可以看到,两者实现的效果是一样的。
五、其他基础功能
1. 响应状态码
我们可以用status_code
属性来查看响应状态码。
r = requests.get('https://www.baidu.com')
print(r.status_code)
2. 响应头
我们可以用headers
属性来查看响应头。
r = requests.get('https://www.baidu.com')
print(r.headers)
3. 重定向与请求历史
默认情况下,除了HEAD
类请求,Requests
会自动处理所有重定向。但是如果我们想要看到每次重定向的历史,我们可以在history
属性中查看。其中,所有重定向的请求会按照从最老到最新来排序。
r = requests.get('http://github.com')
print(r.url)
print(r.status_code)
print(r.history)
我们可以使用allow_redirects
参数来选择禁用重定向:
r = requests.get('http://github.com', allow_redirects=False)
print(r.url)
print(r.status_code)
print(r.history)
4. 超时
我们可以使用timeout
参数来控制超时时长。
r = requests.get('https://www.baidu.com', timeout=0.0000001)
5. 错误与异常
Requests
库中的异常处理能力比urllib
有所加强,下面是官方列出来的常见的异常。
- 遇到网络问题,如DNS查询失败、拒绝连接等,
Requests
会抛出ConnectionError
异常。 - 如果
HTTP
请求返回了不成功的状态码,Response.raise_for_status()
会抛出一个HTTPError
异常,当我们不调用此方法时,默认不会抛出异常。 - 若请求超时,
Requests
会抛出Timeout
异常。 - 若请求超过了设定的最大重定向次数,
Requests
会抛出TooManyRedirects
异常。 - 所有
Requests
显式抛出的异常都继承自requests.exceptions.RequestException
。
这两节的内容包含了Requests
中最常用的一些功能。再结合前边我们讲过的urllib
库,我们现在应该可以处理大部分普通网站的请求了。后边我会再花一些章节在实战中告诉大家应该如何灵活应用这些方法。
Requests
中的高级功能,我们在更往后的一些章节里慢慢讲。下面几章我会告诉大家如何解析网页内容,这样配合我们前边学过的内容,我们就可以完整地实现抓取网页的全流程了。