urllib3

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('\n',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))
posted @ 2021-09-04 19:58  索匣  阅读(352)  评论(0编辑  收藏  举报