urllib中的API大都与URL相关,所有可以得出这样一个结论,urllib主要侧重于URL的请求构造。而urllib2侧重于HTTP请求的处理,urllib3则是服务于升级的HTTP1.1标准,且拥有高效的HTTP连接池管理及HTTP代理服务的功能库。
urllib3的主要特性是
1.线程安全
2.连接池
3.客户端SSL/TLS验证
4.使用multipart编码上传文件
5.协助处理重复请求和HTTP重定位
6.支持压缩编码
7.支持HTTP和SCOKS代理
8.100%测试覆盖率
以下内容主要讲解
1.发送请求
2.设置HTTP请求头
3.获取HTTP响应头
4.上传文件
5.超时异常捕获
一.发送请求
1.1发送get请求
from urllib3 import *
# urlencode函数在urllib.parse模块中
from urllib.parse import urlencode
# 调用disable_warnings函数可以阻止显示警告消息
disable_warnings()
# 创建PoolManager类的实例
http = PoolManager()
'''
# 下面的代码通过组合URL的方式向百度发送请求
url = 'http://www.baidu.com/s?' + urlencode({'wd':'极客起源'})
print(url)
response = http.request('GET', url)
'''
url = 'http://www.baidu.com/s'
# 直接使用fields关键字参数指定GET请求字段
response = http.request('GET', url,fields={'wd':'极客起源'})
# 获取百度服务端的返回值(字节形式),并使用UTF-8格式对其进行解码
data = response.data.decode('UTF-8')
# 输出百度服务端返回的内容
print(data)
1.2发送HTTP POST请求
首先用flask模块编写个可以处理HTTP POST请求的服务端程序
# 支持HTTP POST请求的服务端程序
from flask import Flask, request
# 创建Flask对象,任何基于flask模块的服务端应用都必须创建Flask对象
app = Flask(__name__)
# 设置/register路由,该路由可以处理HTTP POST请求
@app.route('/register', methods=['POST'])
def register():
# 输出名为name的请求字字段的值
print(request.form.get('name'))
# 输出名为age的请求字段的值
print(request.form.get('age'))
# 向客户端返回“注册成功”消息
return '注册成功'
if __name__ == '__main__':
# 开始运行服务端程序,默认端口号是5000
app.run()
然后在使用urllib3中相应的API向这个服务端程序发送HTTP POST请求,然后输出服务端的返回结果
from urllib3 import *
disable_warnings()
http = PoolManager()
# 指定要提交HTTP POST请求的URL,/register是路由
url = 'http://localhost:5000/register'
# 向服务端发送HTTP POST请求,用fields关键字参数指定HTTP POST请求字段名和值
response = http.request('POST', url,fields={'name':'李宁','age':18})
# 获取服务端返回的数据
data = response.data.decode('UTF-8')
# 输出服务端返回的数据
print(data)
二.设置HTTP请求头
1.HTTP请求头
大多数服务端应用都会检测某些HTTP请求头,还有一些服务端应用要求只有处于登录状态才可以访问某些数据,所以需要检测HTTP请求头的Cookie字段,该字段会包含标识用户登录的信息。
通过PoolManager对象的request方法的headers关键字参数可以指定字典形式的HTTP请求头
response = http.request('GET', url,headers=headers)
详细代码如下,通过request方法访问了天猫商城的搜索功能
from urllib3 import *
import re
disable_warnings()
http = PoolManager()
# 定义天猫的搜索页面URL
#url = 'https://list.tmall.com/search_product.htm?spm=a220m.1000858.1000724.4.53ec3e72bTyQhM&q=%D0%D8%D5%D6&sort=d&style=g&from=mallfp..pc_1_searchbutton#J_Filter'
url = 'http://httpbin.org/get'
# 从headers.txt文件读取HTTP请求头,并将其转换为字典形式
def str2Headers(file):
headerDict = {}
f = open(file,'r')
# 读取headers.txt文件中的所有内容
headersText = f.read() # 文件读取出来看样子是一下子全部读取出来,才有之后的分隔操作,类型因该是字符串
print(type(headersText)) # <class 'str'>
print('read出来的样子是什么',headersText)
headers = re.split('
',headersText) # 以换行符进行分隔
for header in headers:
result = re.split(':',header,maxsplit = 1) # 以冒号进行分隔
headerDict[result[0]] = result[1] # 写入字典
f.close()
# print(headerDict)
return headerDict
headers = str2Headers('headers.txt')
# 请求天猫的搜索页面,并传递HTTP请求头
response = http.request('GET', url,headers=headers)
# 将服务端返回的数据按GB18030格式解码
data = response.data.decode('GB18030')
print(data)
三.获取HTTP响应头
1.使用HTTPresponse.info()方法可以很容易获取HTTP响应头的信息
from urllib3 import *
disable_warnings()
http = PoolManager()
url = 'https://www.baidu.com'
response = http.request('GET', url)
# # 输出HTTP响应头信息(以字典形式返回HTTP响应头信息)
for key in response.info().keys(): # keys()就是在外面嵌套了一层皮,
print(key,':', response.info()[key])
print('-'*10,'分割线','-'*10)
print(response.info().keys())
print(type(response.info().keys())) # <class 'collections.abc.KeysView'>差不多十个可迭代对象
print('-'*10,'分割线','-'*10)
print(response.info())
print(type(response.info())) # <class 'urllib3._collections.HTTPHeaderDict'>
四.上传文件
只需设置普通的HTTP请求头一样在request方法中使用fields关键字参数指定一个描述上传文件的HTTP请求头字段,然后再通过元组指定相关属性即可,例如:上传的文件名,文件类型
4.1编写一个可以将文件上传到服务端的Python程序,可以通过输入本地文件名来上传任何类型的文件
import os
from flask import Flask, request
# 定义服务端保存上传文件的位置
UPLOAD_FOLDER = 'uploads'
app = Flask(__name__)
# 用于接收上传文件的路由需要使用POST方法
@app.route('/', methods=['POST'])
def upload_file():
# 获取上传文件的内容
file = request.files['file'] # files字段
if file:
# 这里的save函数又是什么
# 将上传的文件保存到uploads子目录中;
# 这个是把后面的文件加到前面文件夹中么
file.save(os.path.join(UPLOAD_FOLDER, os.path.basename(file.filename)))
return "文件上传成功"
if __name__ == '__main__':
app.run()
4.2编写上传文件的客户端程序
上传文件的编写也有很多讲究
from urllib3 import *
disable_warnings()
http = PoolManager()
# 定义上传文件的服务端Url
url = 'http://localhost:5000'
while True: # 这里还是无限循环的上传
# 输入上传文件的名字
filename = input('请输入要上传的文件名字(必须在当前目录下):')
# 如果什么也未输入,退出循环
if not filename:
break
# 用二进制的方式打开要上传的文件名,然后读取文件的所有内容,使用with语句会自动关闭打开的文件
with open(filename,'rb') as fp:
fileData = fp.read()
# 上传文件
response = http.request('POST',url,fields={'file':(filename,fileData)})
# 输出服务端的返回结果,本例是“文件上传成功”
print(response.data.decode('utf-8'))
五.超时
由于HTTP底层是基于Scoket实现的,所以连接的过程也可能超时,Scoket超时分为连接超时和读超时
需要通过request方法的timeout关键字参数指定超时时间即可
通过PoolManager类的构造方法指定默认的连接超时和读超时
from urllib3 import *
disable_warnings()
# 通过PoolManager类的构造方法指定默认的连接超时和读超时
http = PoolManager(timeout=Timeout(connect=2.0,read=2.0))
url1 = 'https://www.baidu1122.com'
url2 = 'http://httpbin.org/delay/3'
try:
# 此处代码需要放在try…except中,否则一旦抛出异常,后面的代码将无法执行
# 下面的代码会抛出异常,因为域名www.baidu1122.com并不存在
# 由于连接超时设为2秒,
http.request('GET', url1,timeout=Timeout(connect=2.0,read=4.0))
except Exception as e:
print(e)
print('------------')
# 由于读超时为4秒,而url2指定的Url在3秒后就返回数据,所以不会抛出异常,
# 会正常输出服务器的返回结果
response = http.request('GET', url2,timeout=Timeout(connect=2.0,read=4.0))
print(response.info())
print('------------')
print(response.info()['Content-Length'])
# 由于读超时为2秒,所以会在2秒后抛出读超时异常
http.request('GET', url2,timeout=Timeout(connect=2.0,read=2.0))