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下爲"\t\n",Linux下爲"\n" 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】 \r\n%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 + '\n') f.write(str_dic2 + '\n') 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) # {"\u4e2d\u56fd": "\u5317\u4eac", "\u7f8e\u56fd": "\u534e\u76db\u987f"} new2_dic = json.dumps(dic, ensure_ascii=False) print(new2_dic) # {"中國": "北京", "美國": "華盛頓"} with open('dump_json', 'w') as f: json.dump(dic, f) # 寫入文件的內容:{"\u4e2d\u56fd": "\u5317\u4eac", "\u7f8e\u56fd": "\u534e\u76db\u987f"} 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("\033[1;36;40mUSAGE: python %s <file1> <file2>\033[0m" % sys.argv[0]) exit(-1) file_one_path = sys.argv[1] file_tow_path = sys.argv[2] if os.path.exists(file_one_path) and os.path.exists(file_tow_path): file_Contrast(file_one_path, file_tow_path) else: print("請輸入正確的路徑") elif os.name == 'nt': file_one_path = input('輸入需要對比的第一個文件路徑>>>: ') file_tow_path = input('輸入需要對比的第一個文件路徑>>>: ') if os.path.exists(file_one_path) and os.path.exists(file_tow_path): file_Contrast(file_one_path, file_tow_path) else: print("請輸入正確的路徑")
對視頻文件進行一致性校驗
一般是視頻格式的文件/網絡傳輸的文件,都是二進制的bytes類型。此時沒有行的概念,該怎麼做?此時,可以設置一個buffer,每次都讀取相同長度的buffer.
#設置一個buffer,每次都通過f.read(buffer)讀取定長的數據。如果電腦配置比較高,調整相應的buffer即可 import os, sys, hashlib def get_vedio_md5(file_path, buffer=1024): file_size = os.path.getsize(file_path) md5_obj = hashlib.md5() with open(file_path, 'rb') as f: while file_size: content = f.read(buffer) md5_obj.update(content) file_size -= len(content) ret = md5_obj.hexdigest() return ret
logging模塊
logging模塊是用來操作日誌的。
logging模塊分爲兩種配置方式:(1)函數式簡單配置。(2)logger對象配置
函數式簡單配置
import logging # 日誌模塊
logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical')
默認情況下Python的logging模塊將日誌打印到了標準輸出中,且只顯示了大於等於WARNING級別的日誌,這說明默認的日誌級別設置爲WARNING(日誌級別等級CRITICAL > ERROR > WARNING > INFO > DEBUG),默認的日誌格式爲日誌級別:Logger名稱:用戶輸出消息。此時如果想改變顯示,就需要在logging.basicConfig()中,把level級別調低
import logging logging.basicConfig(level=logging.DEBUG) # 日誌級別調到debug logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical')
此時,如果不想日誌默認輸出到標準輸出,想重定向輸出到指定的日誌文件中,那麼也可以通過修改logging.basicconfig()中filename屬性,指定重定向的文件。
import logging
# 通過修改logging.basicConfig(filename=‘file_path’),進行輸出重定向 logging.basicConfig(level=logging.DEBUG, filename='loging.log') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical')
basicConfig常用的參數及輸出格式
logging.basicConfig()函數中可通過具體參數來更改logging模塊默認行爲,可用參數有: filename:用指定的文件名創建FiledHandler,這樣日誌會被存儲在指定的文件中。 filemode:文件打開方式,在指定了filename時使用這個參數,默認值爲“a”還可指定爲“w”。 format:指定handler使用的日誌顯示格式。 datefmt:指定日期時間格式。 level:設置rootlogger(後邊會講解具體概念)的日誌級別 stream:用指定的stream創建StreamHandler。可以指定輸出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默認爲sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。 format參數中可能用到的格式化串: %(name)s Logger的名字 %(levelno)s 數字形式的日誌級別 %(levelname)s 文本形式的日誌級別 %(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有 %(filename)s 調用日誌輸出函數的模塊的文件名 %(module)s 調用日誌輸出函數的模塊名 %(funcName)s 調用日誌輸出函數的函數名 %(lineno)d 調用日誌輸出函數的語句所在的代碼行 %(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示 %(relativeCreated)d 輸出日誌信息時的,自Logger創建以 來的毫秒數 %(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒 %(thread)d 線程ID。可能沒有 %(threadName)s 線程名。可能沒有 %(process)d 進程ID。可能沒有 %(message)s用戶輸出的消息 # 一般常用的配置 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='loging.log', filemode='w')
總結:
通過logging模塊的簡單配置項可以完成對日誌的基本操作,但是依然有幾點痛點:
(1)basicConifg不支持輸出中文
(2)日誌的輸出只能夠文件/屏幕二選一,不能做到同時。
logger 對象配置
既然logging模塊自帶的basicConfig配置不能夠解決中文問題,那麼只能通過自己創建對象的方式來更加靈活的操作,解決固有的痛點:1.不能支持中文 2.同時向文件和屏幕輸出內容
事前應該有幾個準備事項:
(1)先實例化一個日誌對象
(2)創建一個控制文件輸出的文件操作符
(3)創建一個控制屏幕輸出的屏幕操作符
(4)指定日誌輸出的格式(可以指定多個,文件輸出和屏幕輸出格式可以不同)
(5)文件操作符綁定一個日誌格式
(6)屏幕操作符綁定一個日誌格式
(7)日誌對象綁定文件操作符以及屏幕操作符
import logging # (1) 創建一個log對象 logger = logging.getLogger() # (2) 創建一個控制文件輸出的文件操作符,encoding='utf-8‘’,解決中文問題 file_handler = logging.FileHandler('test.log', encoding='utf-8') # (3) 創建一個控制屏幕輸出的屏幕操作符 screen_handler = logging.StreamHandler() # (4) 設置日誌輸出的格式 log_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # (5) 給文件操作符綁定一個日誌格式 file_handler.setFormatter(log_fmt) # (6) 給屏幕操作符綁定一個日誌格式 screen_handler.setFormatter(log_fmt) # (7) 日誌對象綁定文件操作符和屏幕操作符 logger.addHandler(file_handler) logger.addHandler(screen_handler) # (8) 設置日誌輸出的級別 logger.setLevel(logging.DEBUG) # (9) 打印日誌 logger.debug('這是debug的消息') logger.info('這是info的消息') logger.warning('這是warning的消息') logger.error('這是error的消息')
實際應用
import logging # (1) 創建一個log對象 logger = logging.getLogger() # (2) 創建一個控制文件輸出的文件操作符,encoding='utf-8‘’,解決中文問題 file_handler = logging.FileHandler('test.log', encoding='utf-8') # (3) 創建一個控制屏幕輸出的屏幕操作符 screen_handler = logging.StreamHandler() # (4) 設置日誌輸出的格式 log_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # (5) 給文件操作符綁定一個日誌格式 file_handler.setFormatter(log_fmt) # (6) 給屏幕操作符綁定一個日誌格式 screen_handler.setFormatter(log_fmt) # (7) 日誌對象綁定文件操作符和屏幕操作符 logger.addHandler(file_handler) logger.addHandler(screen_handler) # (8) 設置日誌輸出的級別 logger.setLevel(logging.DEBUG) # (9) 打印日誌 def login(): username = input("username>>>: ") password = input("password>>>: ") if username == "xiaobai" and password == "xiaobai123": logger.info("%s 登錄成功 " % username) print("登錄成功") else: logger.error("%s 登錄失敗,密碼:%s " % (username, password)) print("登錄失敗") login()
不同級別的logging 日誌信息寫入到不同文件
# -*- coding: utf-8 -*- import os import time import logging import inspect from logging.handlers import RotatingFileHandler dir = os.path.dirname(__file__) dir_time = time.strftime('%Y-%m-%d', time.localtime()) handlers = {logging.NOTSET: os.path.join(dir, 'notset_%s.log'%dir_time), logging.DEBUG: os.path.join(dir, 'debug_%s.log'%dir_time), logging.INFO: os.path.join(dir, 'info_%s.log'%dir_time), logging.WARNING: os.path.join(dir, 'warning_%s.log'%dir_time), logging.ERROR: os.path.join(dir, 'error_%s.log'%dir_time), logging.CRITICAL: os.path.join(dir, 'critical_%s.log'%dir_time), } def createHandlers(): logLevels = handlers.keys() for level in logLevels: path = os.path.abspath(handlers[level]) handlers[level] = RotatingFileHandler(path, maxBytes=10000, backupCount=2, encoding='utf-8') # 加載模塊時創建全局變量 createHandlers() class TNLog(object): def printfNow(self): return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) def __init__(self, level=logging.NOTSET): self.__loggers = {} logLevels = handlers.keys() for level in logLevels: logger = logging.getLogger(str(level)) # 如果不指定level,獲得的handler似乎是同一個handler? logger.addHandler(handlers[level]) logger.setLevel(level) self.__loggers.update({level: logger}) def getLogMessage(self, level, message): frame, filename, lineNo, functionName, code, unknowField = inspect.stack()[2] '''日誌格式:[時間] [類型] [記錄代碼] 信息''' return "[%s] [%s] [%s - %s - %s] %s" % (self.printfNow(), level, filename, lineNo, functionName, message) def info(self, message): message = self.getLogMessage("info", message) self.__loggers[logging.INFO].info(message) def error(self, message): message = self.getLogMessage("error", message) self.__loggers[logging.ERROR].error(message) def warning(self, message): message = self.getLogMessage("warning", message) self.__loggers[logging.WARNING].warning(message) def debug(self, message): message = self.getLogMessage("debug", message) self.__loggers[logging.DEBUG].debug(message) def critical(self, message): message = self.getLogMessage("critical", message) self.__loggers[logging.CRITICAL].critical(message) if __name__ == "__main__": logger = TNLog() logger.debug("debug") logger.info("info") logger.warning("warning") logger.error("error") logger.critical("critical")