python常用模塊整理(乾貨滿滿)

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")

參考:https://www.cnblogs.com/yanjieli/p/10179626.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章