1. 程式人生 > >Python課程回顧(day20)

Python課程回顧(day20)

常用模組3

一、logging模組

日誌模組
import logging

logging.basicConfig(
    filename='a.log',   # 指定檔案列印路徑,不指定檔名會預設列印到控制檯
    filemode='a',       # 預設是a模式,可以不寫
    format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
    datefmt='%Y-%m-%d %X %p',  # 定製(asctime)的時間格式
    level=10            # 設定日誌的級別,代表當前級別及以上級別都可以列印

)
#  列印格式
# %(asctime)s   %(name)s   %(levelname)s       %(module)s:       %(message)s
#  格式化時間   日誌名稱     日誌的級別     當前使用日誌的模組名  日誌具體的資訊
logging.debug('除錯資訊')      # 10
logging.info('執行結果')       # 20
logging.warning('一級警告')    # 30       { 不同數字代表不同的級別
logging.error('二級警告')      # 40
logging.critical('三級警告')   # 50

# logging模組的四類物件
# logger  負責產生日誌
logger1 = logging.getLogger('')      # 使用getLogger產生一個產生日誌的物件


# filter  過濾日誌(不常用)


# handler 控制日誌列印到檔案或是終端
# 可能很多檔案都需要用到日誌,所以可以在多個檔案內列印
fh1 = logging.FileHandler(filename='a1.log', encoding='utf-8')  # 控制檔案列印路徑
fh2 = logging.FileHandler(filename='a2.log', encoding='utf-8')  # 控制檔案列印路徑
sh = logging.StreamHandler()  # 控制日誌列印到終端


# formatter 控制日誌的格式
fmt1 = logging.Formatter(
    fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
    datefmt='%Y-%m-%d %X %p'
)

fmt2 = logging.Formatter(
    fmt='%(asctime)s - %(message)s',
    datefmt='%Y-%m-%d %X %p'
)


# 讓logger1物件產生的日誌可以分別列印到不同的地方,要讓他們繫結關係
logger1.addHandler(fh1)
logger1.addHandler(fh2)     # handler物件
logger1.addHandler(sh)


# 讓logger1物件所繫結的handler物件再次繫結列印格式
fh1.setFormatter(fmt1)
fh2.setFormatter(fmt1)
sh.setFormatter(fmt2)


# 日誌級別
# 日誌級別要必須通過兩層關卡(logger1級別,檔案級別)才可以正常記錄
logger1.setLevel(10)  # 基礎級別,可以列印所有級別的資訊
fh1.setLevel(20)      # 只處理列印大於等於20級別的資訊
fh2.setLevel(30)      # 只處理列印大於等於30級別的資訊
sh.setLevel(40)       # 只處理列印大於等於40級別的資訊

logger1.warning('除錯資訊')    # 注意本層級別要 >= logger1的級別


瞭解:日誌繼承
import logging

logger1 = logging.getLogger('grandfather')
logger2 = logging.getLogger('grandfather.father')    # 繼承關係要在設定檔名時將子集放置父集後
logger3 = logging.getLogger('grandfather.father.son')

sh = logging.StreamHandler()
fmt3 = logging.Formatter(
    fmt='%(asctime)s - %(message)s',
    datefmt='%Y-%m-%d %X %p'
)
sh.setFormatter(fmt3)

logger1.addHandler(sh)
logger2.addHandler(sh)
logger3.addHandler(sh)

logger1.setLevel(10)
logger2.setLevel(10)
logger3.setLevel(10)
sh.setLevel(10)

logger3.debug('----------')   # logger3會將得到的所有資訊反繼承給logger3的所有父集

二、hashlib模組

import hashlib    # hash是一種演算法,該演算法接收傳入的內容經過運算後會得到一個hash值

# 特點1:只要傳入的值一樣,得到的hash值必然一樣
m = hashlib.md5() # 使用.md5拿到一個hash工廠
m.update('hello'.encode('utf-8')) # 使用update進行加工
m.update('world'.encode('utf-8'))
m.update('klf'.encode('utf-8'))
print(m.hexdigest()) # hexdigest拿到hash值1079e6f8e24cecd0c41c8841f727b31e


m.update('helloworldklf'.encode('utf-8'))
print(m.hexdigest()) # 拿到hash值1079e6f8e24cecd0c41c8841f727b31e


# 特點2:不能由hash值反解成原本內容
# 基於hash值的這種特性,我們可以在接收使用者輸入的時候將使用者輸入的密碼加工成hash值,
# 再由網路通訊發到我們的服務端進行資料儲存,以達到使用者密碼的安全性。
# 使用者再次校驗時只需要跟原本的hash值做對比即可

# 但考慮到使用者的個人習慣與撞庫(一些常用密碼格式的hash值),使用者的密碼還是不夠安全
# 這時候我們就需要幫助使用者來提高密碼安全性:即密碼加鹽
user_pwd = 'klf123'

m = hashlib.md5()
m.update('abcdefg'.encode('utf-8')) # 在使用者密碼之前加入其他字元的hash值
m.update(user_pwd.encode('utf-8')) # 中間是使用者密碼
m.update('hijklmn'.encode('utf-8')) # 在使用者密碼之後加入其他字元的hash值

print(m.hexdigest()) # 生成新的hash值

# 注意:加鹽的方式是隨機的,想要破解就必須知道使用的hash工廠,加鹽方式以及加鹽位置
# 服務端反解的時候按照當初加鹽的規則來反解即可


# 特點3:只要使用同一種hash演算法,得到的hash值的長度的固定的
# 這種特點大大降低了使用者所傳入資料的大小所帶來的空間不足
# 無論使用者傳入的資料有多大,得到的hash值的長度是固定的

三、re模組(重點******)

import re
# re模組之正則表示式re.findall的應用1:單次匹配

# print(re.findall('\w', 'abc 123 \[email protected]#$%\r\t\f'))  # '\w'匹配字串內所有的數字、字母、下劃線
# print(re.findall('\W', 'abc 123 \[email protected]#$%\r\t\f'))  # '\W'匹配的是字串所有的非數字字母下劃線,若字串內有\則會使用一個斜槓轉譯後面的斜槓

# print(re.findall('\s', 'abc 123 \
[email protected]
#$%\r\t\f')) # '\s'匹配的是字串內所有的空白字元,('空字元'\n\r\t\f) # print(re.findall('\S', 'abc 123 \[email protected]#$%\r\t\f')) # '\S'匹配的是字串內所有的非空白字元 # print(re.findall('\d', 'abc 123 \[email protected]#$%\r\t\f')) # '\d'匹配任意數字(0~9) # print(re.findall('\D', 'abc 123 \[email protected]#$%\r\t\f')) # '\D'匹配任意非數字 # print(re.findall('\alex', 'egon,alex_sb123yxx_sb,lxx_sb')) # 單純輸入的字串則會將字串作為整體全部匹配,若匹配不成功則返回空,有多少個匹配多少個 # print(re.findall('\Aalex', 'egon,alex_sb123yxx_sb,lxx_sb')) # '\A'表示只從頭開始找,若匹配不成功則返回空,若匹配成功只匹配一次,不會繼續向後匹配 # print(re.findall('^alex', 'egon,alex_sb123yxx_sb,lxx_sb')) # 作用等同於大A # print(re.findall('alex\Z', 'egon,alex_sb123yxx_sb,lxx_sb')) # '\Z'表示只從末尾開始找,若匹配不成功則返回空,若匹配成功只匹配一次,不會繼續向後匹配 # print(re.findall('alex$', 'egon,alex_sb123yxx_sb,lxx_sb')) # '$'作用等同於大Z # 組合應用之*,$ # print(re.findall('^abc$', 'abc1')) # 表示字串必須為^與$之間的字串,否則匹配不成功 # ============================================================================== # re模組之正則表示式的應用2:重複匹配 # print(re.findall('a.c', 'a123c a2c anc aaaaaaac')) # .表示匹配除換行符之外的任意'一個'字元,可加多個.,每個點都代表一個字元 # 結果:在a與c中間只有一個字元的情況下,才會匹配成功 # print(re.findall('a.c', 'a123c a2c anc aaaaaaac a\nc', re.DOTALL)) # re.DOTALL表示可以打破'.'不能匹配換行符的定義,當前是可以匹配任意字元的 # print(re.findall('ab?', 'a ab abb abbb abbbb abbbbb')) # ?表示?左邊的1個字元重複0次或1次(可以是0次,最多是1次) # 結果:只要a匹配成功,b可以匹配0次(若為0次則只打印a),最多隻匹配1次 # print(re.findall('ab*', 'a ab abb abbb abbbb abbbbb')) # *表示*左邊的1個字元重複0次或N次(可以是0次,沒有最大匹配次數,有多少要多少) # 結果:只要a匹配成功,b可以匹配0次(若為0次則只打印a),也可以匹配無限次 # print(re.findall('ab+', 'a ab abb abbb abbbb abbbbb')) # *表示*左邊的1個字元重複1次或N次(至少是1次,沒有最大匹配次數,有多少要多少) # 結果:只要a匹配成功,b可以匹配1次(若為0次則不列印),也可以匹配無限次 # print(re.findall('ab{0,1}', 'a ab abb abbb abbbb abbbbb')) # {}內可以傳入範圍,指定匹配的次數,若後面的引數不寫則代表N次(等同於*號的作用) # 結果:等同於?號的作用 # 組合應用之.*(貪婪匹配) # print(re.findall('a.*c', 'ac a123c a%$^$c aasdsdasdad')) # .*表示可以匹配任意長度任意字元(除\n),但*會盡可能的去匹配多個字元 # 結果:.*的組合會將距離開頭最近的a當做起始,將距離末尾最近的c當做結束,形成1個匹配然後結束,不建議使用 # 組合應用之.*?(非貪婪匹配) # print(re.findall('a.*?c', 'ac a123c a%$^$c aasdsdasdad')) # .*?中的問號的作用就是將貪婪匹配轉換成非貪婪匹配 # 結果:會將距離開頭最近的a當做起始,將距離a後面最近的c當做結束,形成一個匹配然後結束 # ============================================================================== # re模組之正則表示式的應用3:分組() # print(re.findall('(alex)_sb', 'alex_sb sdasfadsgasgalex_sb')) # ()的作用是不影響正則表示式基本的匹配方法的前提下,只打印括號內的內容 # 結果:若匹配成功後只打印alex # 組合應用之.*?()(爬取網址) # print(re.findall('href="(.*?)"', '<link href="/bundles/admin-new.css?v=Ye9IYl3rG1TPa1mMw-tr9jlbN_BMEt9-1G3QChTzFC01" rel="stylesheet"/>')) # 結果: ['/bundles/admin-new.css?v=Ye9IYl3rG1TPa1mMw-tr9jlbN_BMEt9-1G3QChTzFC01'] # ============================================================================== # re模組之正則表示式的應用4:[]指定範圍 # print(re.findall('a[0-9][0-9]c', 'a1c adc a4c aAc a11c aac')) # []內可以使用-號來定義一個範圍,英文是a-zA-Z,數字是0-9,在這個自定義範圍內的才算匹配成功 # 結果:會將a與c之間是0-9的字串匹配出來,若想匹配兩位數則需要再加中括號進行再次匹配(會以兩個字元去匹配),a1c a4c便不再成立 # 注意:若中括號內要匹配特殊符號例如+-*^之類的,'-'不可以放在兩個符號之間,要放左右兩邊。 # print(re.findall('^a[0-9]c', 'a1c adc a4c aAc a11c aac')) # []內前面加^號就代表取反的意思(原始預設為開頭)即不再這個範圍內的就匹配成功 # 結果:a與c之間不是0-9之間的就可以匹配 # 組合應用: # print(re.findall('[a-z]+_sb', 'alex_sb egon_sb 123wxxxxxxxxx_sb ryc_sb')) # [匹配條件]'+'匹配條件 '+'代表中括號內的條件可以匹配1次或N次,直到遇見不是括號範圍內的字元然後與'+'後面的字元做匹配 # 結果:會優先匹配中括號內的(一直匹配),直到字元不屬於中括號範圍內的,開始匹配_sb # print(re.findall('([a-z]+)_sb', 'alex_sb egon_sb 123wxxxxxxxxx_sb ryc_sb')) # 加入小括號來只取名稱 # ============================================================================== # re模組之正則表示式的應用5:| 或者 # print(re.findall('compan(ies|y)', 'too many companies have bank rupt , and the next one company')) # 在|左右兩邊的內容只要成立一個即匹配成功 # 結果:在compan後若為ies或為y的情況下,則匹配成功,因為有分組,所以只打印括號內的內容,(ies,y) # print(re.findall('compan(?:ies|y)', 'too many companies have bank rupt , and the next one company')) # 在分組內加入?:則代表列印分組外與分組內的所有內容,不單單隻要括號內的內容(companies,company) # ============================================================================== # re模組之正則表示式search與match的應用: # print(re.search('alex', '123 alex sb egon nb alex sb')) # print(re.search('^alex', '123 alex sb egon nb alex sb')) # search代表從起始到結束依次匹配,若匹配成功返回第一個匹配成功的物件,可以使用group列印結果 # 若匹配不成功則返回None,再使用group列印結果則會報錯 # 表示式之前加^代表只匹配起始位置,若不成功則返回None,再使用group列印結果則會報錯 # 一般使用search返回的物件(不加group)判斷是否存在你需要找的字元 # print(re.match('alex', '123 alex sb egon nb alex sb')) # match代表預設只從起始位置匹配,若匹配成功返回一個物件,使用group列印結果 # 若匹配不成功則返回None,再使用group列印結果則會報錯 # ============================================================================== # re模組之正則表示式split的應用 # info = 'a,b c:\*d/p' # print(re.split('[ ,:\\\/*]', info)) # split的原理基本等同於字串的split,不過re中的split可以使用多個符號來切分 # 將表示式內的各種符號對字元依次切分,只要字元內包含其中的某個切分符就進行切分 # 若兩個字元之間存在兩個或多個切分符則會切出相應的空字元 # ============================================================================== # re模組之正則表示式sup的應用 # 要求:將第二個egon替換成大寫 # print(re.sub('(.*?)(egon)(.*?)(egon)(.*?)', r'\1\2\3EGON\5', '123 egon is sb egon 123')) # sub的作用基本相同於replace,但sub功能可以在指定位置替換指定的字元 # 結果:事先將字元分組,然後使用\1\2\3\4\5可以將分組替換位置,也可以在分組內直接替換字元 # 要求:將is左右兩邊的單詞調換位置lqz123+ is sb09 # print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)', r'\5\2\3\4\1\6', r'lqz123+ is sb09')) # 首先明白單詞的定義:由連續的字母組成。 # 基於這個理念來將字元進行分組,然後將分組調換位置即可 # ============================================================================== # re模組之正則表示式compile的應用 # res = re.compile('alex') # 使用compile將經常用到的表示式定義成一個變數 # print(res.findall('alex_sb alex123 ale')) # 通過變數就可以直接去匹配相應的字元