random 模块
随机模块,在某个范围内取到每一个值的概率是相同的
import random # 随机小数 print(random.random()) # 0-1之内的随机小数 print(random.uniform(1, 5)) # 任意范围之内的随机小数 # 随机整数 print(random.randint(1, 2)) # [1,2] 包含2在内的范围内随机取整数 print(random.randrange(1, 2)) # [1,2) 不包含2在内的范围内随机取整数 print(random.randrange(1, 10, 2)) # [1,10) 不包含10在内的范围内随机取奇数 # 随机抽取 lst = [1, 2, 3, 'abc', ('wahaha', 'qqxing')] ret = random.choice(lst) # 随机抽取一个值 print(ret) ret1 = random.sample(lst, 2) # 随机抽取两个值 print(ret1) # 打乱顺序 在原列表的基础上做乱序 lst = [1, 2, 3, 'abc', ('wahaha', 'qqxing')] random.shuffle(lst) print(lst)
随机生成验证码
# (1)4位数字的验证码 # 基础版本 lis = '' for i in range(4): num = random.randint(0, 9) lis += str(num) print(lis) # 函数版本 def rand_code(n=4): lis = '' for i in range(n): num = random.randint(0, 9) lis += str(num) return lis print(rand_code(6)) # (2)6位 数字+字母 def rand_code(n): code = '' for i in range(n): rand_num = str(random.randint(0, 9)) rand_alph = chr(random.randint(97, 122)) rand_alph_upper = chr(random.randint(65, 90)) rand_num = random.choice([rand_num, rand_alph, rand_alph_upper]) code += rand_num return code ret = rand_code(6) print(ret) # (3)可控制验证码 数字 / 数字+字母 def rand_code(num, DefaultAlph=True): # 当DefaultAlph=True时生成字母+数字的验证码, 为False时生成纯数字验证码 code = '' for i in range(num): rand_num = str(random.randint(0, 9)) if DefaultAlph: rand_alph = chr(random.randint(97, 122)) rand_alph_upper = chr(random.randint(65, 90)) rand_num = random.choice([rand_num, rand_alph, rand_alph_upper]) code += rand_num return code ret = rand_code(4, DefaultAlph=False) print(ret)
time 模块
#常用方法 import time time.sleep(secs) #(线程)推迟指定的时间运行。单位为秒 time.time() #获取当前时间戳
表示时间的三种方式
在python中,通常用这三种方式表示时间:时间戳、格式化的时间字符串、元组(struct_time)
# 时间模块 import time # 时间戳 print(time.time()) # 返回当前时间的时间戳 # 结果>>> 1543743462.3950245 # 时间字符串 print(time.strftime('%Y-%m-%d %X')) # 结果>>> 2018-12-02 17:39:58 print(time.strftime('%Y-%m-%d %H-%M-%S')) # 结果>>> 2018-12-02 17-39-58 # 时间元组:localtime将一个时间戳转换为当前时区的struct_time print(time.localtime()) # 结果>>> time.struct_time(tm_year=2018, tm_mon=12, tm_mday=2, tm_hour=17, tm_min=43, tm_sec=44, tm_wday=6, tm_yday=336, tm_isdst=0)
time模块相关方法:
time.localtime([secs]):将一个时间戳转换为当前时区的struct_time;secs参数未提供,则以当前时间为准。 time.gmtime([secs]):和 localtime()类似;gmtime()方法是将一个时间戳转换为UTC时区(0 时区)的struct_time。 time.time():返回当前时间戳 time.mktime(t):将一个time.struct_time转为时间戳 time.sleep(secs):线程推迟指定的时间运行,单位为秒 time.asctime([t]):把一个表示时间的元组或者struct_time表示为这种形式:'Sun Dec 2 17:52:36 2018'。如果没有参数,默认将time.localtime()作为参数传入 time.ctime([t]):把一个时间戳(按秒计算的浮点数)转为time.asctime()的形式。如果参数未给或者为None的时候,默认将time.time()作为参数,相当于time.asctime(time.localtime(secs)) time.strftime(format[, t]):把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转为格式化的时间字符串,如果t未指定,默认传入time.localtime() time.strptime(string[, format]):把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作
时间格式之间的转换
# 时间戳——>结构化时间 # time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致 # time.localtime(时间戳) #当地时间。例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间 print(time.gmtime(1510000000)) #结果>>> time.struct_time(tm_year=2017, tm_mon=11, tm_mday=6, tm_hour=20, tm_min=26, tm_sec=40, tm_wday=0, tm_yday=310, tm_isdst=0) print(time.localtime()) #结果>>> time.struct_time(tm_year=2018, tm_mon=12, tm_mday=2, tm_hour=18, tm_min=4, tm_sec=23, tm_wday=6, tm_yday=336, tm_isdst=0) # 结构化时间——>时间戳 # time.mktime(结构化时间) time_tuple = time.localtime(1510000000) print(time.mktime(time_tuple)) #结果>>> 1510000000.0 # 结构化时间——>字符串时间 # time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则显示当前时间 print(time.strftime("%Y-%m-%d %X")) #结果>>> 2018-12-02 18:07:47 print(time.strftime("%Y-%m-%d", time.localtime(1510000000))) #结果>>> 2017-11-07 # 字符串时间——>结构化时间 # time.strptime(时间字符串,字符串对应格式) print(time.strptime("2018-02-22", "%Y-%m-%d")) #结果>>> time.struct_time(tm_year=2018, tm_mon=2, tm_mday=22, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=53, tm_isdst=-1) print(time.strptime("2018/03/01", "%Y/%m/%d")) #结果>>> time.struct_time(tm_year=2018, tm_mon=3, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=60, tm_isdst=-1)
# 结构化时间 ——> %a %b %d %H:%M:%S %Y串 # time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串 print(time.asctime(time.localtime(1510000000))) #结果>>> Tue Nov 7 04:26:40 2017 print(time.asctime()) #结果>>> Sun Dec 2 18:12:19 2018 # 时间戳 ——> %a %b %d %H:%M:%S %Y串 # time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串 print(time.ctime()) #结果>>> Sun Dec 2 18:13:14 2018 print(time.ctime(1510000000)) #结果>>> Tue Nov 7 04:26:40 2017
获取当月一号的时间戳格式
# 结构化时间 struct_time = time.localtime() struct_time = time.strptime('%s-%s-1'%(struct_time.tm_year,struct_time.tm_mon),'%Y-%m-%d') print(time.mktime(struct_time)) # 格式化时间 ret = time.strftime('%Y-%m-1') struct_time = time.strptime(ret,'%Y-%m-%d') print(time.mktime(struct_time))
datetime 模块
相比于time模块,datetime模块的接口则更直观,更容易调用
- datetime模块定义了下面这几个类:
- datetime.date:表示日期的类;常用的属性有year, month, day;
- datetime.time:表示时间的类;常用的属性有hour, minute, second, microsecond;
- datetime.datetime:表示日期时间。
- datetime.timedelta:表示时间间隔,即两个时间点之间的长度。
- datetime.tzinfo:与时区有关的相关信息。
import datetime print(datetime.datetime.now()) # 现在的时间 # 只能调整的字段:weeks days hours minutes seconds print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三周后 print(datetime.datetime.now() + datetime.timedelta(weeks=-3)) # 三周前 print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前 print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后 print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小时后 print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前 print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分钟前 print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后 print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前 print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后 current_time = datetime.datetime.now() # 可直接调整到指定的 年 月 日 时 分 秒 等 print(current_time.replace(year=1977)) # 直接调整到1977年 print(current_time.replace(month=1)) # 直接调整到1月份 print(current_time.replace(year=1989,month=4,day=25)) # 1989-04-25 18:49:05.898601 # 将时间戳转化成时间 print(datetime.date.fromtimestamp(1232132131)) # 2009-01-17
sys 模块
常用方法:
sys.argv 命令行参数List,第一个元素是程序本身路径,(类似shell中调用脚本后面传入的$1,$2,$3) sys.exit(n) 退出程序,正常退出时exit(0),错误退出sys.exit(1) sys.version 获取Python解释程序的版本信息 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 sys.platform 返回操作系统平台名称
sys.argv用法
name = sys.argv[1] pwd = sys.argv[2] if name == 'xiaobai' and pwd == 'a123456': print('执行以下代码') else: exit()
os 模块
#当前执行这个python文件的工作目录相关的工作路径 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd os.curdir 返回当前目录: ('.') os.pardir 获取当前目录的父目录字符串名:('..') #和文件夹相关 os.makedirs('dirname1/dirname2') 可生成多层递归目录 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 # 和文件相关 os.remove() 删除一个文件 os.rename("oldname","newname") 重命名文件/目录 os.stat('path/filename') 获取文件/目录信息 # 和操作系统差异相关 os.sep 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/" os.linesep 输出当前平台使用的行终止符,win下为" ",Linux下为" " os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为: os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' # 和执行系统命令相关 os.system("bash command") 运行shell命令,直接显示 os.popen("bash command).read() 运行shell命令,获取执行结果 os.environ 获取系统环境变量 #path系列,和路径相关 os.path.abspath(path) 返回path规范化的绝对路径 os.path.split(path) 将path分割成目录和文件名二元组返回 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 os.path.basename(path)返回path最后的文件名。如何path以/或结尾,那么就会返回空值,即os.path.split(path)的第二个元素。 os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False os.path.isabs(path) 如果path是绝对路径,返回True os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 os.path.getatime(path) 返回path所指向的文件或者目录的最后访问时间 os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间 os.path.getsize(path) 返回path的大小
注意:os.stat('path/filename') 获取文件/目录信息 的结构说明
stat结构:
st_mode: inode 保护模式 st_ino: inode 节点号。 st_dev: inode 驻留的设备。 st_nlink: inode 的链接数。 st_uid: 所有者的用户ID。 st_gid: 所有者的组ID。 st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。 st_atime: 上次访问的时间。 st_mtime: 最后一次修改的时间。 st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。
统计目录大小:
import os, sys SIZE = 0 def countDirSize(path): global SIZE pathDirList = os.listdir(path) for fileName in pathDirList: newAbsPath = os.path.join(path, fileName) if os.path.isdir(newAbsPath): SIZE += os.path.getsize(newAbsPath) countDirSize(newAbsPath) else: SIZE += os.path.getsize(newAbsPath) def win(): path = input('请输入需要统计的目录>>> ') if os.path.exists(path): countDirSize(path) else: print("请输入正确的路径...") exit() return SIZE def linux(): path = sys.argv[1] if os.path.exists(path): countDirSize(path) else: print("请输入正确的路径...") exit() return SIZE if __name__ == '__main__': if os.name == "nt": print(win()) elif os.name == "posix": print(linux())
统计当前文件大小:
import os import sys import datetime import time now_date = datetime.datetime.now().strftime('%Y%m%d') def get_size(file_path): size = os.path.getsize(file_path) mod_date = time.strftime('%Y%m%d',time.localtime(os.path.getatime(file_path))) if mod_date == now_date: fnl_size=round(round(size,4)/1024,2) return (fnl_size) else: return 1 if __name__ == "__main__": file_path=sys.argv[1] size=get_size(file_path) print(size)
查找空目录:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- #*******查询指定目录中的空目录********* #执行方法:python3 04-1.py + 目录名称 import sys import os, os.path def find(p): fs = os.listdir(p) if len(fs) == 0: print(os.path.abspath(p)) return for f in fs: pf = os.path.join(p, f) if not os.path.isdir(pf): continue find(pf) if __name__ == '__main__': find(sys.argv[1])
删除空目录:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys def del_emp_dir(path): for (root, dirs, files) in os.walk(path): # print('path-->',path) # print('root-->',root) # print('dirs-->',dirs) for item in dirs: dir = os.path.join(root, item) try: os.removedirs(dir) #os.rmdir() 方法用于删除指定路径的目录。仅当这文件夹是空的才可以, 否则, 抛出OSError。 print(dir) except Exception as e: pass # print('Exception',e) if __name__ == '__main__': del_emp_dir(sys.argv[1])
查看目录文件/文件夹是否更新
#!usr/bin/python # -*- encoding: utf-8 -*- # ============================================================================= # Author: wu import os import datetime import time now_date = datetime.datetime.now().strftime('%Y%m%d%H%M') # 获取文件夹日期 files_date = datetime.datetime.now().strftime('%Y-%m-%d') # print(files_date,type(files_date)) def files_report(report): try: start_time = time.time() new_report = os.path.join(report,files_date) # print(new_report) # 列出目录的下所有文件和文件夹保存到lists lists = os.listdir(new_report) # 按照时间进行排序 lists.sort(key=lambda fn: os.path.getmtime(new_report + "/" + fn)) # 获取最新的文件保存到file_new中 file_new = os.path.join(new_report, lists[-1]) mod_date = time.strftime('%Y%m%d%H%M', time.localtime(os.path.getatime(file_new))) s = '文件时间:【%s】'% mod_date print('最新文件:【%s】 %s'%(file_new,s)) end_time =time.time() # print(mod_date,type(mod_date)) old_time = end_time - start_time if mod_date == now_date: print('查询耗时:【%s】'% old_time) return '状态:【((o(^_ ^)o))文件正常】' else: print('查询耗时:【%s】' %old_time) return '状态:【ლ(ٱ٥ٱლ)没有最新文件夹,请检查!】' except Exception as e: return '【ლ(ٱ٥ٱლ)单日文件夹不存在,请检查!】' if __name__ == '__main__': report = r"path" # 目录地址 res = files_report(report) print(res)
序列化模块(json模块、pickle)
序列化:
将一个对象从内存中转换为可存储(字符串类型)或者可传输(bytes)类型的过程,就叫做序列化。在python中叫做pickling,通俗讲:序列化就是将其他数据类型转换为字符串/bytes类型的过程。
为什么要使用序列化:
(1)持久化数据类型
(2)跨平台进行交互。不同的编程语言都用协商好的序列化格式,那么便能打破平台/语言之间的限制,实现跨平台数据交互。
(3)使程序更具维护性
Json
json格式在各个语言之间都是通用的序列化格式。在json中,所有的字符串都必须是" "双引号。
json的优点:
所有的数据类型都是各个语言通用的。在各个编程语言中都支持。
json的缺点:
1、json只是支持非常少的数据类型
2、对数据类型的约束十分严格
(1)字典中的key必须是字符串。
(2)json只支持列表,字典,数值,字符串,布尔值。
json模块提供了四个功能:dumps、dump、loads、load
# dumps 与 loads import json # 序列化 dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} str_dic = json.dumps(dic) # 序列化:将一个字典转换为字符串 print(str_dic, type(str_dic)) #结果>>> {"name": "xiaobai", "age": 20, "sex": "nan", "2": 4} <class 'str'> # 注意:json转换完的字符串类型的字典中的字符串是有""表示; 如果数字为key那么dump之后会强行转换为字符串数据类型 # 反序列化 dic2 = json.loads(str_dic) # 将一个字符串格式的字典转换成一个字典 print(dic2, type(dic2)) #结果>>> {'name': 'xiaobai', 'age': 20, 'sex': 'nan', '2': 4} <class 'dict'> # 注意:要用json的loads功能处理的字符串类型的字典中的字符串必须有""表示 # json是否支持元组,对元组做value的字典会把元组强制转换为列表 dic = {'a': (1, 2, 3)} str_dic = json.dumps(dic) print(str_dic) # {"a": [1, 2, 3]} new_dic = json.loads(str_dic) print(new_dic) # {'a': [1, 2, 3]} # json是否支持元组做key? 不支持,会报错 # dic = {(1, 2, 3): "a"} # str_dic = json.dumps(dic) # TypeError: keys must be a string # 处理嵌套的数据类型 list_dic = [1, ['a', 'b', 'c'], 2, {'k1': 'k2', 'k3': 'k4'}] str_dic = json.dumps(list_dic) print(str_dic, type(str_dic)) #结果:[1, ["a", "b", "c"], 2, {"k1": "k2", "k3": "k4"}] <class 'str'> new_dic = json.loads(str_dic) print(new_dic, type(new_dic)) #结果:[1, ['a', 'b', 'c'], 2, {'k1': 'k2', 'k3': 'k4'}] <class 'list'>
如果想把数据类型直接序列化到一个文件中,那么就要使用到dump和load方法
# dump 与 load # 系列化进文件 import json dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} with open('dump_json', 'w') as f: json.dump(dic, f) # dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件 with open('dump_json') as f: ret = json.load(f) # load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回 print(type(ret), ret) # <class 'dict'> {'name': 'xiaobai', 'age': 20, 'sex': 'nan', '2': 4} # 能不能dump多个数据进入文件, dump可以多个数据进去,但是load不出来了,会报错 dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} dic2 = {'k1': 'v1', 'k2': 'v2'} with open('dump_json', 'w') as f: json.dump(dic, f) json.dump(dic2, f) # with open('dump_json') as f: # json.load(f) # json.decoder.JSONDecodeError # 如果非要使用json dump多个数据到文件里面,那么就要用到dumps dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} dic2 = {'k1': 'v1', 'k2': 'v2'} with open('dump_json', 'w') as f: str_dic1 = json.dumps(dic) str_dic2 = json.dumps(dic2) f.write(str_dic1 + ' ') f.write(str_dic2 + ' ') with open('dump_json') as f: for line in f: ret = json.loads(line) print(ret)
写入中文乱码,需要使用ensure_ascii关键字参数
# 中文格式的 import json dic = {'中国':'北京', '美国':'华盛顿'} new_dic = json.dumps(dic) print(new_dic) # {"u4e2du56fd": "u5317u4eac", "u7f8eu56fd": "u534eu76dbu987f"} new2_dic = json.dumps(dic, ensure_ascii=False) print(new2_dic) # {"中国": "北京", "美国": "华盛顿"} with open('dump_json', 'w') as f: json.dump(dic, f) # 写入文件的内容:{"u4e2du56fd": "u5317u4eac", "u7f8eu56fd": "u534eu76dbu987f"} with open('dump_json', 'w', encoding='utf-8') as f: json.dump(dic, f, ensure_ascii=False) # 写入文件的内容:{"中国": "北京", "美国": "华盛顿"}
json格式化输出
import json data = {'username':['李华','二愣子'],'sex':'male','age':16} json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False) print(json_dic2) # 结果: ''' { "age":16, "sex":"male", "username":[ "小明", "小李" ] } '''
pickle
由于json格式对python数据类型的支持不是那么完美,如果只是在python程序之间交互,使用pickle模块的支持性会更好。但是不足之处就是,pickle只是适用于python语言。
pickle的优点:
(1)pickle支持python中的几乎所有数据类型
(2)pickle会把数据类型序列化为bytes类型
pickle的缺点:
(1)pickle只适用于python
pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load (不仅可以序列化字典,列表...可以把python中任意的数据类型序列化)
import pickle # dumps 与 loads dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} b_dic = pickle.dumps(dic) print(type(b_dic)) # <class 'bytes'> d_dic = pickle.loads(b_dic) print(type(d_dic)) #<class 'dict'> # dump 与 load dic = {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} with open('pickle_dump', 'wb') as f: pickle.dump(dic, f) with open('pickle_dump', 'rb') as f: ret = pickle.load(f) print(ret) # {'name': 'xiaobai', 'age': 20, 'sex': 'nan', 2: 4} # 可以发现pickle和json用法其实是完全一样,只是dump写和读的时候注意,因为pickle转换为bytes类型,所以写读时候都要以wb 和rb的形式
hashlib模块
算法介绍:
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。
摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
hashlib介绍:
hashlib模块是一个内部有摘要算法的模块,而且内部可以给我们提供不止一种摘要算法。能够把 一个 字符串 数据类型的变量,转换成一个 定长的 密文的 字符串,字符串里的每一个字符都是一个十六进制数字
为什么需要hashlib?
对于同一个字符串,用相同的算法,相同的手段去进行摘要,获得的值总是相同的
hashlib模块的使用:
hashlib模块提供了多种摘要算法:
md5算法: 定长32位 16进制(应用最广发的摘要算法)
sha系统算法: 定长40位
sha算法要比MD5算法更加复杂,且sha N数值越大,算法越复杂,耗时越久,结果越长,但也更安全
import hashlib # hashlib模块md5摘要算法的使用 name = "xiaobai" password = 'xiaobai123' # 1.首先,需要先实例化一个md5的对象,一个对象只加密一个数据 md5_obj = hashlib.md5() # 2. update()方法,把需要进行md5的对象放入 md5_obj.update(password.encode('utf-8')) # 3. 通过hexdigest(),得到摘要算法之后的密文 md5_password = md5_obj.hexdigest() print(md5_password) # 21b3a6792936ba9c2ecbcbe0da8ba961 print(len(md5_password)) # md5算法,定长32位 # hashlib模块sha摘要算法的使用 # 操作和md5如出一辙,先创建对象,通过update加密,再通过hexdigest取值 name = "xiaobai" password = 'xiaobai123' sha_obj = hashlib.sha1() sha_obj.update(password.encode('utf-8')) sha_password = sha_obj.hexdigest() print(sha_password) # 6e96c5250d4d9c3b1ea9b5815d41aa0343a3c691 print(len(sha_password)) # sha1算法,定长40位
hashlib的应用
用户登录的验证
name | password -------+-------- xiaobai|xiaobai123
有一个用户小白,密码为xiaobai123,如果密码就这样明文存储,如果数据库被黑,那么密码就毫无保留的暴露给了黑客。所以这时候就需要用到摘要,在数据库中,存储密码的摘要信息,每次登陆的时候,再做摘要信息的对比
name | password -------+-------- xiaobai|21b3a6792936ba9c2ecbcbe0da8ba961
所以每次登陆的时候,便需要进行密码信息的摘要对比
import hashlib # 密码加密 def get_md5_pwd(s): md5_obj = hashlib.md5() md5_obj.update(s.encode('utf-8')) ret = md5_obj.hexdigest() return ret username = input("username>>>: ").strip() password = input("password>>>: ").strip() with open('userinfo', encoding='utf-8') as f: for line in f: user, pwd = line.strip().split('|') if username == user and get_md5_pwd(password) == pwd: print("登录成功") break else: print("登录失败")
通过摘要算法的手段,虽然密码是用密文的形式存储了,但是在现在的攻击手段中,有一种叫做"撞库"的手段,就是通过一个存储着大量密码与md5后的摘要对应的关系,再一一进行匹配,如果摘要信息一致,便能够反推出密码,因为同一种算法的同一个字符串,结果总是不变的。那么,有什么方法能够防止撞库?那就通过加盐值得手段(1.固定盐值 2.更好的方法:动态加盐)
何为盐值(salt),其实就是给原数据+一段指定的字符串,这样得到的MD5值就会发生变化。只要颜值不被黑客知道,那么就很难反向推出原数据。
# 加盐的md5算法,采用固定盐值(盐值:static) username = "xiaobai" password = "xiaobai123" md5_obj = hashlib.md5() md5_obj.update('static'.encode('utf-8')) # 加油 md5_obj.update(password.encode('utf-8')) ret = md5_obj.hexdigest() print(ret) # 动态加盐,通过把用户的唯一标识作为盐值,例如每个用户的用户名都是唯一 username = "xiaobai" password = "xiaobai123" md5_obj = hashlib.md5() md5_obj.update(username.encode('utf-8')) # 动态加盐 md5_obj.update(password.encode('utf-8')) ret = md5_obj.hexdigest() print(ret)
文件一致性的校验
给一个文件中的所有内容进行摘要算法,得到一个md5结果。此时,我们可以体验到md5摘要算法的神奇的地方,对于同一个字符串,不管把他拆开多少段,最终得到的md5值都是一样。
# 同一个字符串,不管拆开多少段,最终的md5都是一样的。 s = 'hello world' md5_obj = hashlib.md5() md5_obj.update(s.encode('utf-8')) ret = md5_obj.hexdigest() print(ret) # 5eb63bbbe01eeed093cb22bb8f5acdc3 md5_obj = hashlib.md5() md5_obj.update('hello '.encode('utf-8')) md5_obj.update('world'.encode('utf-8')) ret = md5_obj.hexdigest() print(ret) # 5eb63bbbe01eeed093cb22bb8f5acdc3
所以对文件进行一致性校验
def get_file_md5(file_path): file_md5_obj = hashlib.md5() with open(file_path, encoding='utf-8') as f: for line in f: file_md5_obj.update(line.encode('utf-8')) ret = file_md5_obj.hexdigest() return ret
总结:两个文件MD5对比
# 文件校验, 两个文件对比 import os, sys, hashlib def get_file_md5(file_path): file_md5_obj = hashlib.md5() with open(file_path, encoding='utf-8') as f: for line in f: file_md5_obj.update(line.encode('utf-8')) ret = file_md5_obj.hexdigest() return ret def file_Contrast(file_one_path, file_tow_path): file_one_md5 = get_file_md5(file_one_path) file_tow_md5 = get_file_md5(file_tow_path) if file_one_md5 == file_tow_md5: print("%s 与 %s 一致" % (file_one_path, file_tow_path)) else: print("两个文件不一致") if __name__ == '__main__': if os.name == 'posix': if len(sys.argv) < 3: print("