Python課程回顧(day20)
阿新 • • 發佈:2018-10-31
常用模組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')) # 通過變數就可以直接去匹配相應的字元