文章目錄
一、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中布爾值True、False,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 算法),該算法接受傳入的內容,經過運算得到一串hash值
2. 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>