python基礎(二十一):常用模塊詳解

一、time與datetime模塊

1、time模塊

(1)三種格式時間生成

注意:生成時間都是生成的當前時間
在Python中,通常有這幾種方式來表示時間:

  • 時間戳(timestamp):通常來說,時間戳表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。我們運行“type(time.time())”,返回的是float類型。
  • 格式化的時間字符串(Format String)
  • 結構化的時間(struct_time):struct_time元組共有9個元素共九個元素:(年,月,日,時,分,秒,一年中第幾周,一年中第幾天,夏令時)
import time
#--------------------------我們先以當前時間爲準,讓大家快速認識三種形式的時間
print(time.time()) # 時間戳:1487130156.419527
print(time.strftime("%Y-%m-%d %X")) #格式化的時間字符串:'2017-02-15 11:40:53'
 
print(time.localtime()) #本地時區的struct_time
print(time.gmtime())    #UTC時區的struct_time
三種時間格式的轉換

其中計算機認識的時間只能是’時間戳’格式,而程序員可處理的或者說人類能看懂的時間有: ‘格式化的時間字符串’,‘結構化的時間’ ,於是有了下圖的轉換關係:
在這裏插入圖片描述結構化時間和時間戳之間的轉換:

# 時間戳-->結構化時間
>>> time.localtime(time.time()) # 執行結果:time.struct_time(tm_year=2020, tm_mon=5, tm_mday=15, tm_hour=10, tm_min=20, tm_sec=15, tm_wday=4, tm_yday=136, tm_isdst=0)
>>> time.gmtime(time.time())

# 結構化時間-->時間 
>>> time.mktime(time.localtime()) # 執行結果:1589510093.0

結構化時間和格式化字符串時間的轉換:

>>> time.strftime("%Y-%m-%d %X", time.localtime()) # 執行結果:'2020-05-15 10:48:47'

>>> time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X') # 執行結果:time.struct_time(tm_year=2011, tm_mon=5, tm_mday=5, tm_hour=16, tm_min=37, tm_sec=6, tm_wday=3, tm_yday=125, tm_isdst=-1)

2、datetime模塊

用途:用於時間的加減


import datetime

# 明確datetime.datetime.now()返回的究竟是個什麼東西?
>>> datetime.datetime.now() # 我們暫時稱之爲datetime格式時間,這種格式並不是那三種格式之一,不要被print誤導!
datetime.datetime(2020, 5, 15, 11, 7, 14, 505326)

>>> print(datetime.datetime.now()) # 這是print做了優化。不要被誤導。
2020-05-15 11:08:11.641439

#只取datetime格式時間的年月日
>>> datetime.date.fromtimestamp(time.time())
datetime.date(2020, 5, 15)

>>> print(datetime.date.fromtimestamp(time.time())) # 執行結果依然被print優化了
2020-05-15


#時間加減
print(datetime.datetime.now() )
print(datetime.datetime.now() + datetime.timedelta(3)) #當前時間+3天
print(datetime.datetime.now() + datetime.timedelta(-3)) #當前時間-3天
print(datetime.datetime.now() + datetime.timedelta(hours=3)) #當前時間+3小時
print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #當前時間+30分

# 時間修改
>>> c_time  = datetime.datetime.now()
>>> c_time.replace(minute=3,hour=2) #分鐘改爲3分,小時改爲2點
datetime.datetime(2020, 5, 15, 2, 3, 32, 602746)

二、random模塊

1、基本用法

>>> import random
# 隨機生成小數
>>> random.random()  				# (0,1)----float    大於0且小於1之間的小數

>>> random.uniform(1,3) 			# 大於1小於3的小數,如1.927109612082716 

#隨機生成整數
>>> random.randint(1,3) 			# [1,3]    大於等於1且小於等於3之間的整數
   
>>> random.randrange(0,100,2)	 		# [0,100)    步長爲2,0,2,4...,在這個裏面產生隨機數,即100以內的隨機偶數

# 其他用法
>>> random.choice([1,'23',[4,5]]) 	# 1或者23或者[4,5]

>>> random.sample([1,'23',[4,5]],2) # 列表元素任意2個組合
  
>>> item=[1,3,5,7,9]
>>> random.shuffle(item) #打亂item的順序,相當於"洗牌"

2、生成隨機驗證碼

import random
def make_code(n):
    res = ''
    for i in range(n):
        num = str(random.randint(1,9)) # 生成隨機1-9,並強轉成字符串格式
        char = chr(random.randint(65,90)) # 生成隨機a-z字母
        get_str = random.choice([num,char]) # 從生成的數字和字母選擇一個進行字符串拼接
        res += get_str
    return res
verti_code = make_code(5)
print(verti_code)

3、打印進度條(random模擬文件下載網速波動)

(1)打印進度條預備知識:格式化字符串
#進度條長度控制爲50個%s
>>> print('[%+50s]' %'#') # '+'代表右對齊,可簡化省略'+'
[                                                 #]

>>> print('[%50s]' %'#') # 其中還是在傳%s,中間的50代表[]中間的字符串長度,且沒有傳值的用空格代替,且默認爲右對齊
[                                                 #]

>>> print('[%-50s]' %'#') # '-'代表左對齊,給%s傳一個值'#'
[#                                                 ]
# 把進度條長度寫活
>>> '[%%-%ds]' %(50) # 第二個%號代表取消第一個%的特殊意義,所以最後只有一個%,%%相當於轉義%,因此這裏的%s不會被傳值,因此被傳值的是%d
'[%-50s]'

>>> ('[%%-%ds]' %(50)) %('##') # 相當於'[%-50s]' %('##')
'[##                                                ]'
(2)打印進度條實現

注意:所有的動態的實現都是靜態的快速替換

import time
import random
# 定義打印進度條函數
def progress(percent,width=50):
    if percent > 1:
        percent = 1
    show_str = ('[%%-%ds]' %(width)) %(int(percent*width)*'#') # 進度條字符串
    
    # 打印進度條,且後面加上文件下載百分比
    print('\r%s %.1f%%' %(show_str,percent*100),end='') # end = '':不換行打印;\r:從行首開始打印;%.1f:傳進來的浮點數取一位小數

# 下載文件
recv_size = 0 # 代表下載接收到的數據量
total_size = 150000 # 代表文件總大小,單位爲字節
while recv_size < total_size:
    time.sleep(random.random()) # random模擬網速波動,也可以用其他uniform方法指定區間波動加快速度
    recv_size += 1024 # 0.2秒下載1kb
    percent = recv_size/total_size # 獲取當前下載文件的比例(小數)
    progress(percent,70)

部分代碼疑點剖析:

    if percent > 1:
        percent = 1
# 因爲用recv_size += 1024模擬下載文件大小,這樣子,只要下載的文件大小不是1024倍數,最後就會出現超過百分百的情況,因此這裏做個判斷percent大於1,如果大於1,那麼就percent = 1,直接下完。

超出百分百:
在這裏插入圖片描述有人會問,有%-50s控制字符的長度(即#的個數),爲什麼還會出現超過50個字符長度?

答:%-50s,並不是強制約束了傳入進來的字符串長度爲50(或者說'#'號個數),而是說傳進來的'######'這個字符串,左對齊,然後除這個長度以外的我都用'空格'佔位,傳進來的超過這個長度,只是不再有空格佔位罷了!

三、json&pickle模塊

1、什麼是序列化和反序列化?

# 內存中的數據類型 --> 序列化 --> 特定的格式(json或pickle格式)

# 特定的格式(json或pickle格式)--> 反序列化 --> 內存中的數據類型

2、爲何要用序列化?

序列化的結果–>特定的格式的內容有兩種用途

  • 可用於存儲 -->存入硬盤中去
  • 傳輸給其他平臺使用 --> 跨平臺數據交互

我們都應該都明白一個公司的一個軟件,一定是使用了較長時間的,很有可能其中使用了多種編程語言,編程語言有各自負責的一個模塊,各編程語言之間會有數據交互,怎麼解決?

python						    		java
列表		-->	    特定的格式   	 -->		數組

強調:

  • 針對存儲用途,應該是一種專有格式–>pickle格式只能python使用
  • 針對跨平臺數據交互用途,對格式的要求應該是一種通用的、能夠被所有語言識別的格式

疑問解答:json格式可以用於存儲嗎?

答:不能,json格式只是把所有語言共有的一些數據類型提取出來做了通用格式,python的集合就無法轉成json格式。因此需要用python專用的pickle格式。

3、json模塊實現序列化、反序列化

(1)序列化
>>> import json
>>> res = json.dumps([1,'吳晉丞',True])
>>> res
'[1, "\\u5434\\u664b\\u4e1e", true]'
>>> type(res)
<class 'str'>

# 注意:
	1. json格式最後得到的都是一個字符串,而且必須用''(單引號)引起來,且裏面內容的字符串類型只能用""(雙引號)表示字符串,例如:'["\\u5434\\u664b\\u4e1e"]'
	2. json格式裏面對中文做了處理,但這個"\\u5434\\u664b\\u4e1e"並不是任何一種編碼格式,只是中文的json格式,要想顯示中文,必須進行反序列化
	3. python中布爾值TrueFalse,json格式中都變成小寫
(2)反序列化
>>> str is bytes
True
>>> load = json.loads(res) # res是字符串類型,這裏傳入還可以是bytes類型
>>> load
[1, '吳晉丞', True]
>>> type(load)
<class 'list'>
(3)序列化、反序列化與文件處理結合
# 源代碼
import json
with open('db.txt','wb') as f:
    res = json.dumps([1,'吳晉丞',True])
    f.write(res.encode('utf-8')) # 不能以字符串形式write進文件,除非用t模式
with open('db.txt','rb') as f:
    json_res = f.read()
    print(json.loads(json_res))
    print(json_res,type(json_res))
    print('-------------------------------------------------------')
    print(json.loads(json_res.decode('utf-8')))
    print(json_res.decode('utf-8'),type(json_res.decode('utf-8')))

# 執行結果
[1, '吳晉丞', True]
b'[1, "\\u5434\\u664b\\u4e1e", true]' <class 'bytes'>
-------------------------------------------------------
[1, '吳晉丞', True]
[1, "\u5434\u664b\u4e1e", true] <class 'str'>

實驗總結:

  • b模式,寫入文件必須是字節,因此必須要encode
  • b模式從文件直接讀出的是字節,根據實驗可以看出來字節類型也可以作爲loads函數的參數,也可以進行反序列化。在python解釋器2.7與3.6之後都可以json.loads(bytes類型),但唯獨3.5不可以
  • 字節類型解碼後就得到字符串了
  • 編碼與解碼,序列化和反序列化,這兩個的過程,其實形式都差不多。
# 多行數據的文件的反序列化
import json
with open('db.txt','wb') as f: 
    res = json.dumps([1,'吳晉丞',True]) + '\n' # 不加\n不會換行,下面的for循環會出錯
    res1 = json.dumps([2, 'haha', False])
    f.write(res.encode('utf-8'))
    f.write(res1.encode('utf-8'))
with open('db.txt','rb') as f:
    for i in f:
        json_res = json.loads(i)
        print(json_res)

# 執行完程序db.txt的內容(文件是多行數據):
[1, "\u5434\u664b\u4e1e", true]
[2, "haha", false]

# 執行結果:
[1, '吳晉丞', True]
[2, 'haha', False]
(4)json模塊的dump和load函數實現代碼簡化

(3)中的序列化、反序列化和文件處理的源代碼可以簡化的。如下:

# b模式因爲要先生成json格式,再編碼,因此無法使用dump函數
import json
with open('db.txt','wb') as f:
    res = json.dumps([1,'吳晉丞',True])
    f.write(res.encode('utf-8'))
with open('db.txt','rb') as f:
    json_res = json.load(f) # 直接將整個文件反序列化,因此文件只能有一個數據;如果有多個數據,且一行爲一個數據,則還是需要用loads,(3)中有多行數據文件的源代碼。
    print(json_res)
# t模式,因爲dumps函數生成的就是字符串可以用t模式直接寫入文件,因此不需要編碼,因此可以使用dump函數
import json
with open('db.txt','w') as f:
    json.dump([1,'吳晉丞',True],f)
with open('db.txt','r') as f:
    print(json.load(f)) # 直接將整個文件反序列化,因此文件只能有一個數據

4、猴子補丁

猴子補丁,是針對模塊,進行打補丁的一種思想方法。如果模塊中有些方法你覺得寫的並不好,你要用自己寫的方法,替換此模塊中對應的方法,但不改變模塊源代碼。就要用到猴子補丁。

# 原monkey模塊中的方法
def other_func():
    print("from other_func")
def hello():
	print('hello')
def world():
	print('world')

# 你自己認爲更好的方法
# 自己寫的monkey_plus模塊裏的方法
def hello():
	print('hello everyone !')
def world():
	print('This world is beautiful !')

#以後寫python項目會有很多python程序文件,不可能一個個打補丁,因此,只需要在程序入口打個補丁,後面所有其他程序文件,用的依舊是補丁版的monkey。
# 下面是運行程序文件。需要調用monkey模塊,且還要給monkey模塊打補丁,注意打補丁一般在程序入口處打補丁
import monkey
import monkey_plus
def monkey_patch_monkey():
	monkey.hello = monkey_plus.hello
	monkey.world = monkey_plus.world
monkey_patch_monkey()
# 其實這種場景也比較多, 比如我們引用團隊通用庫裏的一個模塊, 又想豐富模塊的功能, 除了繼承之外也可以考慮用Monkey Patch(猴子補丁).
# 採用猴子補丁之後,如果發現優化後的模塊使用效果不符合預期,那也可以快速撤掉補丁(不調用打補丁的函數即可)。個人感覺Monkey Patch帶了便利的同時也有搞亂源代碼的風險!

5、pickle模塊實現序列化、反序列化

pickle模塊中的方法的使用和json模塊大致一樣,有一個不同之處:dumps函數的返回值的數據類型不一樣,請看下面:

# 序列化與反序列化:
import pickle
res = pickle.dumps({1,'吳晉丞'}) # 集合
print(pickle.loads(res),type(res)) # 運行結果:{1, '吳晉丞'} <class 'bytes'>
# json模塊的dumps的返回值是一個字符串,而pickle模塊的dumps的返回值是一個bytes類型。


# 文件處理與序列化與反序列化(b模式):
import pickle
with open('db.txt','wb') as f:
    pickle.dump([1],f) # 相當於f.write(pickle.dumps({1,'吳晉丞'}))
with open('db.txt','rb') as f:
    print(pickle.load(f)) # 相當於pickle.loads(f.read())
# 不能使用t模式,只能使用b模式,因爲dumps返回值是bytes類型,t模式無法接收bytes類型數據

6、python2與python3的pickle兼容性問題

# coding:utf-8
import pickle

with open('a.pkl',mode='wb') as f:
    # 一:在python3中執行的序列化操作如何兼容python2
    # python2不支持protocol>2,默認python3中protocol=4
    # 所以在python2中dump操作應該指定protocol=2
    pickle.dump('你好啊',f,protocol=2)

with open('a.pkl', mode='rb') as f:
    # 二:python3中反序列化才能正常使用
    res=pickle.load(f)
    print(res)

Pickle的問題和所有其他編程語言特有的序列化問題一樣,就是它只能用於Python,並且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的數據,不能成功地反序列化也沒關係。

四、configparser模塊

用途:configparser模塊專門用來獲取配置文件中的配置信息的。
configparser模塊支持的配置文件格式如下:

# 註釋1
; 註釋2

[section1]
k1 = v1
k2:v2 # :號相當於=號
user=egon
age=18
is_admin=true
salary=31

[section2]
k1 = v1

1、獲取配置文件信息(重點)

import configparser

config=configparser.ConfigParser()
config.read('a.cfg')

#查看所有的標題
res=config.sections() #['section1', 'section2']
print(res)

#查看標題section1下所有key=value的key
options=config.options('section1')
print(options) #['k1', 'k2', 'user', 'age', 'is_admin', 'salary']

#查看標題section1下所有key=value的(key,value)格式
item_list=config.items('section1')
print(item_list) #[('k1', 'v1'), ('k2', 'v2'), ('user', 'egon'), ('age', '18'), ('is_admin', 'true'), ('salary', '31')]

#查看標題section1下user的值=>字符串格式
val=config.get('section1','user')
print(val) #egon

#查看標題section1下age的值=>整數格式
val1=config.getint('section1','age')
print(val1) #18

#查看標題section1下is_admin的值=>布爾值格式
val2=config.getboolean('section1','is_admin')
print(val2) #True

#查看標題section1下salary的值=>浮點型格式
val3=config.getfloat('section1','salary')
print(val3) #31.0

2、修改配置文件信息


import configparser

config=configparser.ConfigParser()
config.read('a.cfg',encoding='utf-8')


#刪除整個標題section2
config.remove_section('section2')

#刪除標題section1下的某個k1和k2
config.remove_option('section1','k1')
config.remove_option('section1','k2')

#判斷是否存在某個標題
print(config.has_section('section1'))

#判斷標題section1下是否有user
print(config.has_option('section1',''))


#添加一個標題
config.add_section('egon')

#在標題egon下添加name=egon,age=18的配置
config.set('egon','name','egon')
config.set('egon','age',18) #報錯,必須是字符串


#最後將修改的內容寫入文件,完成最終的修改
config.write(open('a.cfg','w'))

五、hashlib模塊

1、hash介紹

客戶端,用戶會輸入賬號、密碼,實現登錄,賬號密碼的傳輸不可能採用明文傳輸吧,被別人抓包分析,賬號密碼直接就暴露了,因此需要加密,把賬號密碼一起hash得到一個hash值,傳給服務端,對比hash值是否一樣即可。

 1. 什麼叫hash?
	hash是一種算法(3.x裏代替了md5模塊和sha模塊,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法),該算法接受傳入的內容,經過運算得到一串hash2. hash值的特點是:
	* 只要傳入的內容一樣,得到的hash值必然一樣 =====> 要用明文傳輸密碼文件完整性校驗
	* 不能由hash值反解成內容 =======> 把密碼做成hash值,不應該在網絡傳輸明文密碼
	* 只要使用的hash算法不變,無論校驗的內容有多大,得到的hash值長度是固定的

也可以通過撞庫破解hash得到密碼,抓包得到一個賬號與密碼的hash值,然後預先有個數據庫,裏面存着常用的密碼,然後把數據庫裏的一個個密碼與賬號hash,把得到的hash值和抓包的hash值對比,直到得到一樣的,就破解成功了。如果你賬號都不曉得,還破解個屁!!!

既然這種簡單hash可以比較容易破解,那麼就只能採用密碼加鹽進行hash。這樣一般來說都非常不容易破解。

2、hash應用

(1)hash基本應用

hash算法就像一座工廠,工廠接收你送來的原材料(可以用m.update()爲工廠運送原材料),經過加工返回的產品就是hash值
在這裏插入圖片描述

# hashlib模塊的用法
  import hashlib
  m=hashlib.md5() # m=hashlib.sha256(),指定需要使用的hash算法
  m.update('hello'.encode('utf8'))
  m.update('world'.encode('utf8'))
  print(m.hexdigest())  #92a7e713c30abbb0319fa07da2a5c4af
# 可以多次輸送原材料,最後一起hash。相當於m.update('helloworld'.encode('utf8'))
(2)模擬撞庫

雖然現實是賬號密碼一起hash,這裏爲了驗證的簡單,我就只是密碼進行hash,然後撞庫。

import hashlib
passwds=[ # 某人賬號常用密碼
    'alex3714',
    'alex1313',
    'alex94139413',
    'alex123456',
    '123456alex',
    'a123lex',
    ]
def make_passwd_dic(passwds):
    dic={}
    for passwd in passwds:
        m=hashlib.md5()
        m.update(passwd.encode('utf-8'))
        dic[passwd]=m.hexdigest()
    return dic

def break_code(cryptograph,passwd_dic):
    for k,v in passwd_dic.items():
        if v == cryptograph:
            print('密碼是===>\033[46m%s\033[0m' %k)

cryptograph='aee949757a2e698417463d47acac93df' # 抓包獲取的hash值
break_code(cryptograph,make_passwd_dic(passwds))
(3)校驗文件完整性

針對小文件(整個文件hash):

import hashlib
with open('db.txt','rb') as f:
    m = hashlib.md5()
    m.update(f.read())
    print(m.hexdigest())

針對比較大的文件(整個文件hash):

# 文件比較大,不能全部讀入內存,不然可能會死機,或者卡!
import hashlib
with open('db.txt','rb') as f:
    while True:
        res = f.read(2000)
        if len(res) == 0: # 先要判斷是否爲空,再決定是否將內容進行hash
            break
        m = hashlib.md5()
        m.update(res) # 文件讀出來本來就是bytes類型,因此不再需要encode
    verti_code = m.hexdigest()
    print(verti_code)

針對非常大的文件(文件部分hash):

 # 我們不能再像中型文件那樣while循環整個文件hash校驗了,太慢了
 # 下面是文件大小,用下面這個文件模擬非常大的文件:
total_res = 0
with open('haha.jpeg','rb') as f:
    while True:
        res = f.read(2048)
        if len(res) == 0:
            break
        total_res += len(res)
print(total_res) # 運行結果:267751 byte

# 校驗文件完整性
import hashlib
total_res = 0
with open('haha.jpeg','rb') as f:
    m = hashlib.md5()
    # 文件前部分
    f.seek(100)
    res = f.read(20000)
    m.update(res)

    # 文件中間部分
    f.seek(70000)
    res1 = f.read(20000)
    m.update(res1)

    # 文件結尾部分
    f.seek(200000)
    res2 = f.read(20000)
    m.update(res2)

    print(m.hexdigest())
# 注意:
	1. 利用f.seek分別在文件中取部分內容,進行hash,可以多取幾部分,取小點。也可以利用while循環等距取樣。
	2. 服務端基於這種前中後取文件內容進行hash的規則,得到一個校驗碼。客戶端拿到文件,也要採用同樣的規則進行hash.
	3. 雖然這樣也會出現文件不一致的情況,但概率較小。

3、密碼加鹽

密碼加鹽就是用戶端在用戶的密碼上,在任意位置加上任意數量的數字或字母或中文等,再進行hash,得到值傳給服務端,服務端用同樣的加鹽規則對你的密碼進行加鹽再hash,判斷你的密碼是否正確!

這樣會極大提高撞庫的難度,而且我們假設抓到包的人撞庫成功,得到一串加鹽的密碼,但是他知道那個地方,哪些內容是鹽嗎?很難猜中!他還要對一長串加鹽密碼進行排列組合去試密碼。

六、suprocess模塊

用途:執行命令,獲取其命令的標準輸出或錯誤輸出。

import subprocess
obj = subprocess.Popen('ls /;ls /root',shell = True,
                       stdout=subprocess.PIPE, # 產生一個管道,把管道的內存地址給stdout,標準輸出只認stdout,於是就把標準輸出扔進這個管道里去了
                       stderr=subprocess.PIPE # 再產生一個管道,把管道的內存地址給stderr,錯誤輸出只認stderr,於是就把錯誤輸出扔進這個管道里去了
                       )
print(obj)
print(obj.stderr.read()) # 從錯誤輸出管道里讀取內容,read可以指定讀取內容的大小
print('---------------------------------')
print(obj.stdout.read().decode('utf-8')) # subprocess使用當前系統默認編碼進行encode把內容扔進管道,得到結果爲bytes類型,在windows下需要用gbk解碼

# 執行結果:
<subprocess.Popen object at 0x108038390>
b'ls: /root: No such file or directory\n'
---------------------------------
X11
X11R6
bin
lib
libexec
local
sbin
share
standalone

七、logging模塊

1、日誌級別與配置

import logging

# 一:日誌配置
logging.basicConfig(
    # 1、日誌輸出位置:1、終端 2、文件
    # filename='access.log', # 不指定,默認打印到終端

    # 2、日誌格式
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

    # 3、時間格式
    datefmt='%Y-%m-%d %H:%M:%S %p',

    # 4、日誌級別
    # critical => 50
    # error => 40
    # warning => 30
    # info => 20
    # debug => 10
    level=30,
)

# 二:輸出日誌
logging.debug('調試debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('錯誤error')
logging.critical('嚴重critical')

'''
# 注意下面的root是默認的日誌名字
WARNING:root:警告warn
ERROR:root:錯誤error
CRITICAL:root:嚴重critical
'''

2、日誌配置字典

logger:產生日誌的對象

Filter:過濾日誌的對象

Handler:接收日誌然後控制打印到不同的地方,FileHandler用來打印到文件中,StreamHandler用來打印到終端

Formatter對象:可以定製不同的日誌格式對象,然後綁定給不同的Handler對象使用,以此來控制不同的Handler的日誌格式
(1)日誌字典的定義
"""
settings.py
"""

import os

# 1、定義三種日誌輸出格式,日誌中可能用到的格式化串如下
# %(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用戶輸出的消息

# 2、強調:其中的%(name)s爲getlogger時指定的名字
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]''[%(levelname)s][%(message)s]'

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

test_format = '%(asctime)s] %(message)s'

# 3、日誌配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'test': {
            'format': test_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到終端的日誌
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日誌,收集info及以上的日誌
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,日誌輪轉
            'formatter': 'standard',
            # 可以定製日誌文件路徑
            # BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # log文件的目錄
            # LOG_PATH = os.path.join(BASE_DIR,'a1.log')
            'filename': 'a1.log',  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日誌文件的編碼,再也不用擔心中文log亂碼了
        },
        'other': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'test',
            'filename': 'a2.log',
            'encoding': 'utf-8',
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        '': { # 這裏'',沒有名字,除下面指定日誌名字'專門的採集',使用下面那個loggers,其他沒有的日誌名,都使用這個loggers,日誌名爲你傳進來的日誌名!
            'handlers': ['default', 'console'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG', # loggers(第一層日誌級別關限制)--->handlers(第二層日誌級別關卡限制),loggers這裏定義的是那種級別往上需要產生日誌,handlers是定義那種級別往上的日誌我需要輸出到文件或終端。
            'propagate': False,  # 默認爲True,向上(更高level的logger)傳遞,通常設置爲False即可,否則會一份日誌向上層層傳遞
        },
        '專門的採集': {
            'handlers': ['other',],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}
(2)日誌字典的使用
"""
run.py
"""
import settings

# !!!強調!!!
# 1、logging是一個包,需要使用其下的config、getLogger,可以如下導入
# from logging import config
# from logging import getLogger

# 2、也可以使用如下導入
import logging.config # 這樣連同logging.getLogger都一起導入了,然後使用前綴logging.config.

# 3、加載配置
logging.config.dictConfig(settings.LOGGING_DIC)

# 4、輸出日誌
logger1=logging.getLogger('用戶交易')
logger1.info('egon兒子alex轉賬3億冥幣')

# logger2=logging.getLogger('專門的採集') # 名字傳入的必須是'專門的採集',與LOGGING_DIC中的配置唯一對應
# logger2.debug('專門採集的日誌')

八、re模塊

1、什麼是正則?

正則就是用一些具有特殊含義的符號組合到一起(稱爲正則表達式)來描述字符或者字符串的方法。或者說:正則就是用來描述一類事物的規則。(在Python中)它內嵌在Python中,並通過 re 模塊實現。正則表達式模式被編譯成一系列的字節碼,然後由用 C 編寫的匹配引擎執行。

2、常用匹配模式(元字符)

在這裏插入圖片描述

3、正則匹配演練

import re
1. \w與\W
print(re.findall('\w','hello egon 123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']
print(re.findall('\W','hello egon 123')) #[' ', ' ']

2. \s與\S
print(re.findall('\s','hello  egon  123')) #[' ', ' ', ' ', ' ']
print(re.findall('\S','hello  egon  123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']

3. \n \t都是空,都可以被\s匹配
print(re.findall('\s','hello \n egon \t 123')) #[' ', '\n', ' ', ' ', '\t', ' ']

4. \n與\t
print(re.findall(r'\n','hello egon \n123')) #['\n']
print(re.findall(r'\t','hello egon\t123')) #['\n']

5. \d與\D
print(re.findall('\d','hello egon 123')) #['1', '2', '3']
print(re.findall('\D','hello egon 123')) #['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' ']

6. ^與$
print(re.findall('^h','hello egon 123')) #['h']
print(re.findall('3$','hello egon 123')) #['3']

------------------------------------------------------------------------
------------------------------------------------------------------------

# 重複匹配:| . | * | ? | .* | .*? | + | {n,m} |

1. .(小數點)
print(re.findall('a.b','a1b')) # ['a1b']
print(re.findall('a.b','a1b a*b a b aaab')) # ['a1b', 'a*b', 'a b', 'aab']
print(re.findall('a.b','a\nb')) # []
print(re.findall('a.b','a\nb',re.S)) # ['a\nb']
print(re.findall('a.b','a\nb',re.DOTALL)) # ['a\nb']同上一條意思一樣
# 如果不使用re.S參數,則只在每一行內進行匹配,如果一行沒有,就換下一行重新開始。
# 而使用re.S參數以後,正則表達式會將這個字符串作爲一個整體,在整體中進行匹配。

2. *
print(re.findall('ab*','bbbbbbb')) # []
print(re.findall('ab*','a')) # ['a']
print(re.findall('ab*','abbbb')) # ['abbbb']

3. ?
print(re.findall('ab?','a')) # ['a']
print(re.findall('ab?','abbb')) # ['ab']
# 匹配所有包含小數在內的數字
print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) # ['123', '1.13', '12', '1', '3']

4. .*默認爲貪婪匹配
print(re.findall('a.*b','a1b22222222b')) # ['a1b22222222b']

5. .*?爲非貪婪匹配:推薦使用
print(re.findall('a.*?b','a1b22222222b')) # ['a1b']

6. +
print(re.findall('ab+','a')) #[]
print(re.findall('ab+','abbb')) #['abbb']

7. {n,m}
print(re.findall('ab{2}','abbb')) # ['abb']
print(re.findall('ab{2,4}','abbbbb')) # ['abbbb']
print(re.findall('ab{1,}','abbb')) # 'ab{1,}' ===> 'ab+'
print(re.findall('ab{0,}','abbb')) # 'ab{0,}' ===> 'ab*'

8. []
print(re.findall('a[1*-]b','a1b a*b a-b')) # []內的都爲普通字符了,匹配1或*或-,都可以
print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) # []內的^代表的意思是取反,不匹配1、*、-
print(re.findall('a[0-9]b','a1b a*b a-b a=b')) # 匹配0到9的數字中的任何一個都可以
print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb')) # 匹配a到z中的字母中的任何一個
print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) # 匹配一個a到z中的任何一個字母,再匹配一個A到Z中的任何一個字母

9. \
# print(re.findall('a\\c','a\c')) # 對於正則來說a\\c確實可以匹配到a\c,但是在python解釋器讀取a\\c時,會發生轉義,然後交給re去執行,所以拋出異常
print(re.findall(r'a\\c','a\c')) # r代表告訴解釋器使用rawstring,即原生字符串,把我們正則內的所有符號都當普通字符處理,不要轉義
print(re.findall('a\\\\c','a\c')) # 同上面的意思一樣,和上面的結果一樣都是['a\\c']

10. ()
print(re.findall('ab+','ababab123')) # ['ab', 'ab', 'ab']
print(re.findall('(ab)+123','ababab123')) # ['ab'],匹配到末尾的ab123中的ab
print(re.findall('(?:ab)+123','ababab123')) # findall的結果不是匹配的全部內容,而是組內的內容,?:可以讓結果爲匹配的全部內容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">點擊</a>'))# ['http://www.baidu.com']
print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">點擊</a>'))# ['href="http://www.baidu.com"']

11. |
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))

----------------------------------瞭解線-----------------------------------

九、os模塊

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.environ  獲取系統環境變量
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.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

十、sys模塊

sys.path           返回模塊的搜索路徑,初始化時使用PYTHONPATH環境變量的值
sys.argv           命令行參數List,第一個元素是程序本身路徑
sys.exit(n)        退出程序,正常退出時exit(0)
sys.version        獲取Python解釋程序的版本信息
sys.maxint         最大的Int值
sys.platform       返回操作系統平臺名稱

十一、shutil模塊

十二、shelve模塊

shelve模塊比pickle模塊簡單,只有一個open函數,返回類似字典的對象,可讀可寫;key必須爲字符串,而值可以是python所支持的數據類型

import shelve

f=shelve.open(r'sheve.txt')
# f['stu1_info']={'name':'egon','age':18,'hobby':['piao','smoking','drinking']}
# f['stu2_info']={'name':'gangdan','age':53}
# f['school_info']={'website':'http://www.pypy.org','city':'beijing'}

print(f['stu1_info']['hobby'])
f.close()

十三、xml模塊

xml是實現不同語言或程序之間進行數據交換的協議,跟json差不多,但json使用起來更簡單,不過,古時候,在json還沒誕生的黑暗年代,大家只能選擇用xml呀,至今很多傳統公司如金融行業的很多系統的接口還主要是xml。
xml的格式如下,就是通過<>節點來區別數據結構的:

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章