文件上传
我们知道requests可以模拟提交一些数据。例如有的网站需要上传文件,我们也可以用它来实现,这非常简单,示例如下:
import requests
files = {'file': open('favicon.ico', 'rb')}
r = requests.post("http://httpbin.org/post", files=files)
print(r.text)
需要注意的是,favicon.ico需要和当前脚本在同一目录下。
{
"args": {},
"data": "",
"files": {
"file": "data:application/octet-stream;base64,AAAAAA...="
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "147",
"Content-Type": "multipart/form-data; boundary=cff98377c65b22dea6b6b8843808f361",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.19.1"
},
"json": null,
"origin": "114.245.27.179",
"url": "http://httpbin.org/post"
}
这个网站会返回响应,里面包含files这个字段,而form字段是空,这证明文件上传部分会单独有一个files字段来标识。
Cookies
requests获取和设置Coolies只需一步即可完成。
import requests
r = requests.get("https://www.baidu.com")
print(r.cookies)
for key, value in r.cookies.items():
print(key + '=' + value)
运行结果如下:
]>
BDORZ=27315
这里我们首先调用cookies属性即可成功得到Cookies,可以发现它是RequestCookieJar类型。然后用items()方法将其转化为元组组成的列表,遍历输出每一个Cookie的名称和值,实现Cookie的遍历解析。
import requests
cookies = 'tgw_l7_route=860ecf76daf7b83f5a2f2dc22dccf049; _xsrf=vUuULqZA3FFmjIlhgE5QhQ5wqevPchwo; _zap=4947505c-3f0a-461c-9919-fc6774bd8bf0; d_c0="AHBiiWOtow6PTpwNxwmtRvBUzk8GoRnpN9A=|1544282366"; capsion_ticket="2|1:0|10:1544282386|14:capsion_ticket|44:MzQ5ZGMzMmNkZGQ3NDRiNThhODU4MjhjZTk2NDgxZjk=|303e46b9907dc5d0ed9ed6b4758b0117d5461a9fac16b7acdab44493ee0b56c7"; z_c0="2|1:0|10:1544282414|4:z_c0|92:Mi4xQ3d3ekNBQUFBQUFBY0dLSlk2MmpEaVlBQUFCZ0FsVk5MaV81WEFBOGVhaVBOYjhGYlBqZGlLamFOQnRRQ2NabDNn|3b2f2d0cab10c69f39091a3ff3742ac64d873547469927661fc1e4fcb926faa1"; tst=r; q_c1=d0a3c796b1364af9adc18022877142eb|1544282423000|1544282423000'
jar = requests.cookies.RequestsCookieJar()
headers = {
'Host': 'www.zhihu.com',
'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'
}
for cookie in cookies.split(';'):
key, value = cookie.split('=', 1)
jar.set(key, value)
r = requests.get("http://www.zhihu.com", cookies=jar, headers=headers)
print(r.text)
这里我们首先新建了一个RequestCookieJar对象,然后将复制下来的cookies利用split()方法分割,接着利用set()方法设置好每个Cookie的key和value,然后通过调用requests的get()方法并传递给cookies参数即可。当然,由于知乎本身的限制,headers参数也不能少,只不过不需要在原来的headers参数里面设置cookie字段了。
测试后,发现同样可以正常登录知乎。
会话维持
在requests中,如果直接利用get()或post()等方法的确可以做到模拟网页的请求,但是这实际上相当于不同的会话,也就是说相当于你用了两个浏览器打开了不同的页面。
利用Session,我们可以方便地维护一个会话,而且不用担心cookies的问题,它会帮我们自动处理好。
import requests
requests.get('http://httpbin.org/cookies/set/number/123456789')
r = requests.get('http://httpbin.org/cookies')
print(r.text)
使用如上所示代码,并不能成功设置Cookies。
import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)
运行结果如下:
{
"cookies": {
"number": "123456789"
}
}
所以,利用Session,可以做到模拟同一个会话而不用担心Cookies的问题。它通常用于模拟登录成功之后再进行下一步的操作。
SSL证书验证
此外,requests还提供了证书验证的功能。当发送HTTP请求的时候,它会检查SSL证书,我们可以使用verify参数控制是否检查此证书。其实如果不加verify参数的话,默认是True,会自动验证。
import requests
response = requests.get('https://www.12306.cn')
print(response.status_code)
运行结果如下(这是之前的验证结果,现在12306的证书已经可以验证了,会返回200):
requests.exceptions.SSLError:("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)
这里提示一个错误SSLError,表示证书验证错误。所以,如果请求一个HTTPS站点,但是证书验证错误的页面时,就会报这样的错误,那么如何避免这个错误呢?只需要把verify参数设置为False即可。
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
这样就会打印出请求成功的状态码:
lib/python3.7/site-packages/urllib3/connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
InsecureRequestWarning)
lib/python3.7/site-packages/urllib3/connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
200
不过我们发现了一个警告,它建议我们给它制定证书。我们可以通过设置忽略警告的方式来屏蔽这个警告:
import requests
from requests.packages import urllib3
urllib3.disable_warnings()
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
或者通过捕获警告到日志的方式忽略警告:
import logging
import requests
logging.captureWarnings(True)
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
当然,我们也可以指定一个本地证书用作客户端证书,这可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:
import requests
response = requests.get('https//www.12306.cn', cert=('/path/server.crt', '/path/key'))
print(response.status_code)
当然,上面的代码是演示实例,我们需要有crt和key文件,并且指定它们的路径。注意,本地私有证书的key必须是解密状态,加密状态的key是不支持的。
超时设置
在本机网络状态不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才可能受到响应,甚至到最后收不到响应而报错。为了防止服务器不能及时响应,应该设置一个超时时间,即超过了这个时间还没有得到响应,那就报错。这需要用到timeout参数。这个时间的计算是发出请求到服务器返回响应的时间。示例如下:
import requests
r = requests.get("https://www.taobao.com", timeout=1)
print(r.status_code)
通过这样的方式,我们可以将超时时间设置为1秒,如果1秒内没有响应,那就抛出异常。
实际上,请求分为两个阶段,即连接和读取。
上面设置的timeout将用作连接和读取二者的timeout总和。
如果要分别指定,就可以传入一个元组:
r = requests.get('https://www.taobao.com', timeout=(5, 11, 30))
如果想永久等待,可以直接将timeout设置为None,或者不设置直接留空,因为默认是None。这样的话,如果服务器还在运行,但是响应特别慢,那就慢慢等吧,它永远不会返回超时错误的。
r = requests.get('https://www.taobao.com', timeout=None)