@(python)
目錄
paramiko模塊
一脚粟、time模塊
二、random模塊
三膜楷、os 模塊
四俩由、sys模塊
五、shutil模塊
六幻梯、json模塊與pickle模塊
1. 什么是序列化兜畸?
2. 為什么要序列化努释?
3. 如何序列化之json和pickle
json
pickle
七、shelve模塊
八咬摇、xml模塊
1. 解析xml
2. 操作xml
3. 創(chuàng)建xml文檔
方式一
方式二
方式三
九伐蒂、configparser模塊
1.讀取
2. 改寫和判斷
3. 添加
4. 基于上述方法添加一個(gè)ini配置文件
十、hashlib模塊
十一逸邦、subprocess模塊
十二健田、logging模塊
1. 簡單將日志打印到屏幕
2. 通過logging.basicConfig函數(shù)對日志的輸出格式及輸出位置做相關(guān)配置
3. 如果想同時(shí)把log打印在屏幕和日志文件里好爬,就需要了解一些復(fù)雜的知識蜈漓。
四個(gè)主要類
logger
handler
Formatter
示例一
示例二
日志輸出到其它位置
通過logging.config模塊配置日志
paramiko模塊用法:https://www.cnblogs.com/rainowl-ymj/p/7247287.html
一、time模塊
在python中巍佑,有三種方式來表示時(shí)間:
- 時(shí)間戳(timestamp): 時(shí)間戳表示的是從1970年1月1日 00:00:00開始按秒計(jì)算的偏移量(1970年是unix元年)腻菇。
- 格式化的時(shí)間字符串(Format String)例如:'2017-02-15 11:40:53' 這樣的時(shí)間
- 結(jié)構(gòu)化的時(shí)間(struct_time): struct_time元組共有9個(gè)元素(年、月、日边苹、時(shí)、分阱表、秒烂叔、一年中的第幾周、一年中的第幾天忘朝、夏令時(shí))
時(shí)間形式轉(zhuǎn)換:
print(time.strftime('%Y-%m-%d %X',time.localtime()))
# 將結(jié)構(gòu)化時(shí)間(struct_time)轉(zhuǎn)換為格式化的時(shí)間字符串,
# time.localtime()不給時(shí)間戳默認(rèn)是用當(dāng)前時(shí)間戳轉(zhuǎn)換成結(jié)構(gòu)化時(shí)間寡痰;
# 所以要想將時(shí)間戳轉(zhuǎn)換成格式化時(shí)間字符串入热,必須先轉(zhuǎn)換成結(jié)構(gòu)化時(shí)間,再將結(jié)構(gòu)先時(shí)間轉(zhuǎn)換成格式化時(shí)間
print(time.strptime('2017-06-05 10:50:11','%Y-%m-%d %X'))
# 將格式化的時(shí)間字符串轉(zhuǎn)換為結(jié)構(gòu)化時(shí)間(struct_time)
print(time.mktime(time.localtime()))
# 將結(jié)構(gòu)化時(shí)間(struct_time)轉(zhuǎn)換為時(shí)間戳
print(time.localtime(1496632089.0))
# 將時(shí)間戳轉(zhuǎn)換成當(dāng)前時(shí)區(qū)的結(jié)構(gòu)化時(shí)間(struct_time)
# time.localtime()不給時(shí)間戳默認(rèn)是用當(dāng)前時(shí)間戳轉(zhuǎn)換成結(jié)構(gòu)化時(shí)間
print(time.gmtime(1496632089.0))
# 將時(shí)間戳轉(zhuǎn)換成UTC時(shí)區(qū)(0時(shí)區(qū))的結(jié)構(gòu)化時(shí)間(struct_time)
轉(zhuǎn)換為linux系統(tǒng)時(shí)間表示形式:
print(time.asctime(time.localtime()))
# 將結(jié)構(gòu)化時(shí)間(struct_time)轉(zhuǎn)換為“Mon Jun 5 13:47:49 2017”這種形式的時(shí)間
print(time.ctime(1234567890))
# 將時(shí)間戳轉(zhuǎn)換為“Sat Feb 14 07:31:30 2009” 這種形式的時(shí)間
其他用法:
time.sleep(1) # 休眠1秒
二、random模塊
import random
print(random.random())
# float型潮峦,大于0且小于1之間的小數(shù)
print(random.randint(1,5))
# 1~5之間的整數(shù)齿兔,包含1和5
print(random.randrange(1,5))
# 1~5之間的整數(shù)吻商,包含1,不包含5
print(random.choice([3,5,6,2,7,1]))
# 從序列中隨機(jī)取出一個(gè)元素(字符串求橄、列表、元組)
print(random.sample([6,5,7,2,8,3,1,5],3))
# 從序列中隨機(jī)取出任意3個(gè)元素麸恍,元素個(gè)數(shù)可自定義
print(random.uniform(1,5))
# 取出大于1,小于5之間的任一小數(shù)逝薪,如:4.396934907979433
item = [1,2,3,4,5]
random.shuffle(item) # 打亂序列的順序谴轮,相當(dāng)于“洗牌”
print(item)
生成隨機(jī)驗(yàn)證碼:
# print(chr(65))
# print(ord('A'))
# ASCII碼表 65~90 表示A-Z 97~122 表示a-z
def v_code(n):
'''生成隨機(jī)驗(yàn)證碼'''
com_char = ''
for i in range(n): # 循環(huán)n次
nums = str(random.randint(0,9))
S = chr(random.randint(65,90)) # 大寫字母
s = chr(random.randint(97,122)) # 小寫字母
res = random.choice([nums,s,S]) # 隨機(jī)選擇一個(gè)
com_char += res # 每次循環(huán)將隨機(jī)選擇的字符相加
return com_char
print(v_code(4))
三、os 模塊
import os
# print(os.getcwd())
# 獲取當(dāng)前工作目錄做个,相當(dāng)Linux命令pwd
# os.chdir('E:\python\s17\day05')
# 進(jìn)入指定目錄鸽心,目錄不存在報(bào)錯滚局,相當(dāng)于Linux下的cd命令
# print(os.curdir) # 返回當(dāng)前目錄(“.”)
# print(os.pardir) # 返回當(dāng)前目錄的父目錄("..")
# os.makedirs(r'E:\python\s17\day06\test\abc')
# 相當(dāng)于mkdir -p居暖,存在會報(bào)錯
# os.removedirs(r'E:\python\s17\day06\test\abc')
# 若目錄為空,則刪除藤肢;并遞歸到上一級目錄太闺,如果也為空,則刪除嘁圈,依此類推
# os.mkdir(r'.\test')
# 在當(dāng)前目錄下創(chuàng)建一個(gè)test目錄省骂,相當(dāng)于Linux命令mkdir
# os.rmdir(r'.\test')
# 刪除單級空目錄,若目錄不為空則無法刪除最住,報(bào)錯钞澳;相當(dāng)于Linux中rmdir
# os.listdir('dirname')
# 列出指定目錄下的所有文件和子目錄,包括隱藏文件涨缚,并以列表的形式打印
# os.remove('test') # 刪除文件
# os.rename(r'.\test.txt',r'.\TEXT.txt') # 重命名轧粟,前新后舊
# print(os.stat(r'.\TEXT.txt')) # 獲取文件信息
# os.linesep # 輸出換行符
# os.pathsep # 輸出分割文件路徑的字符,win下為“;” 兰吟,Linux下為“:”
# os.name # 輸出字符串指示當(dāng)前使用平臺通惫。win->'nt'; Linux->'posix'
# os.system("bash command") # 運(yùn)行shell命令,直接顯示
# os.environ # 獲取系統(tǒng)環(huán)境變量
# print(os.path.abspath(r'TEXT.txt')) # 獲取絕對路徑
# print(os.path.split(r'E:\python\s17\day06\os模塊\TEXT.txt'))
# 將所給路徑分成目錄和文件名(不管是否存在)混蔼,并通過元組的形式返回
# print(os.path.dirname(r'E:\python\s17\day06\os模塊\TEXT.txt'))
# 提取父目錄(不管是否存在)
# print(os.path.basename(r'E:\python\s17\day06\os模塊\TEXT.txt'))
# 提取最后一個(gè)文件名(不管是否存在)履腋,以'\'結(jié)尾會報(bào)錯
# print(os.path.join(r'D:',r'c:\\def',r'ab\\anc\\rf',r'ac\\cd'))
# 將多個(gè)路徑組合后返回,第一個(gè)絕對路徑之前的參數(shù)將被忽略
# ===== 判斷===========================
# print(os.path.exists(r'E:\python\s17\day06\os模塊\'))
# 判斷所給路徑是否存在惭嚣,返回True或False遵湖,以'\'結(jié)尾會報(bào)錯
# print(os.path.isabs(r'E:\python\s17\day06\os模塊'))
# 判斷是否是絕對路徑,返回True或False晚吞,以'\'結(jié)尾會報(bào)錯
# print(os.path.isfile(r'TEXT.txt'))
# 判斷是否是一個(gè)文件奄侠,返回True或False,以'\'結(jié)尾會報(bào)錯
# print(os.path.isdir(r'E:\python\s17\day06\os模塊'))
# 判斷是否是一個(gè)目錄载矿,返回True或False垄潮,以'\'結(jié)尾會報(bào)錯
# =======================================
# ======= 獲取文件信息 =====================
# print(os.path.getatime(r'E:\python\s17\day06\os模塊\TEXT.txt'))
# 獲取文件的最后訪問的時(shí)間戳
# print(os.path.getmtime(r'E:\python\s17\day06\os模塊\TEXT.txt'))
# 獲取文件的最后修改的時(shí)間戳
# print(os.path.getsize(r'E:\python\s17\day06\os模塊\TEXT.txt'))
# 獲取文件大小,單位是字節(jié)(bytes)
# ======================================
# print(os.path.normcase(r'E:/python/s17/day06/os模塊/TEXT.txt'))
# 該函數(shù)會原樣返回path闷盔,在windows平臺上會將路徑中所有字符轉(zhuǎn)換為小寫弯洗,并將所有斜杠轉(zhuǎn)換為飯斜杠。
# print(os.path.normpath(r'E:/python/s17/day06/../TEXT.txt'))
# 返回“E:\python\s17\TEXT.txt” 逢勾,“..”表示上級目錄
# === os 路徑處理 ==================
# 獲取當(dāng)前代碼文件的所在的上層目錄
# 方式一:推薦使用
import os,sys
print(__file__)
possible_topdir = os.path.normpath(
os.path.join(
os.path.abspath(__file__), # __file__表示當(dāng)前文件的絕對路徑
os.pardir, # 返回上一級
os.pardir # 再返回上一級
)
)
print(possible_topdir) # 會獲取到day06目錄的絕對路徑
# sys.path.insert(0,possible_topdir) # 放入sys.path牡整,便于調(diào)用
# 方式二:不推薦使用
print(os.path.dirname(os.path.dirname(__file__)))
四、sys模塊
用于提供對python解釋器相關(guān)的操作:
print(sys.argv) # 打印參數(shù)列表溺拱,第一個(gè)元素是文件本身
sys.argv[0] # 表示文件本身
sys.argv[1] # 表示命令行中命令后跟的第一個(gè)參數(shù)逃贝,以此類推。迫摔。沐扳。
sys.exit(n) 退出程序,正常退出時(shí)exit(0)
sys.version 獲取Python解釋程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模塊的搜索路徑句占,初始化時(shí)使用PYTHONPATH環(huán)境變量的值
sys.platform 返回操作系統(tǒng)平臺名稱
sys.stdin 輸入相關(guān)
sys.stdout 輸出相關(guān)
sys.stderror 錯誤相關(guān)
進(jìn)度百分比
# 進(jìn)度百分比
import sys
import time
def view_bar(num, total):
rate = float(num) / float(total)
rate_num = int(rate * 100)
r = '\r%d%%' % (rate_num, )
sys.stdout.write(r)
sys.stdout.flush()
if __name__ == '__main__':
for i in range(0, 100):
time.sleep(0.1)
view_bar(i, 100)
進(jìn)度條
# 進(jìn)度條
for i in range(50):
sys.stdout.write('%s\r'%("#"*i))
sys.stdout.flush()
time.sleep(0.3)
五沪摄、shutil模塊
高級的文件、文件夾纱烘、壓縮包處理模塊
import os,shutil
# with open('test.txt','w',encoding='utf-8') as f:
# f.write('你好杨拐,世界')
# if os.path.isfile('test.txt'):
# shutil.copyfile('test.txt','test1.txt')
# 所有都是前舊后新
# shutil.copyfileobj(open('test.txt','r',encoding='utf-8'),\
# open('test2.txt','w',encoding='utf-8'))
# # 將文件內(nèi)容拷貝到另一個(gè)文件中
# shutil.copyfile('test2.txt','test3.txt',follow_symlinks=True)
# 拷貝文件,目標(biāo)文件無需存在,follow_symlinks=True表示保留軟鏈接屬性
# shutil.copymode('test3.txt','test5.txt')
# 僅拷貝權(quán)限擂啥,內(nèi)容哄陶、組、用戶均不變哺壶;目標(biāo)文件必須存在
# shutil.copystat('test3.txt','test5.txt')
# 僅拷貝文件狀態(tài)信息屋吨,包括:mode bits atime mtime flgs
# shutil.copy('test5.txt','test6.txt')
# 拷貝文件和權(quán)限
# shutil.copy2('test3.txt','test6.txt')
# 拷貝文件和狀態(tài)信息
# import shutil
# shutil.ignore_patterns(*patterns)
# shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
#目標(biāo)目錄不能存在舱痘,注意對folder2目錄父級目錄要有可寫權(quán)限,ignore的意思是排除
# shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# 拷貝時(shí)保持軟鏈接离赫,如不指定symlinks=True,會把軟鏈接拷貝成硬鏈接
# shutil.rmtree('b')
# 遞歸刪除目錄(將目錄下的文件和子目錄全都刪除芭逝,相當(dāng)于rm -rf)
# shutil.move(r'.\a\b\b.txt',r'.\a')
# 遞歸移動文件,它類似mv命令渊胸,也可以對文件重命名
壓縮包處理
shutil.make_archive
創(chuàng)建歸檔文件旬盯,并返回文件路徑(例如zip或tar)
# 語法:
make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,dry_run=0, owner=None, group=None, logger=None)
'''
參數(shù):
base_name: 壓縮包的文件名,也可以是壓縮包的路徑翎猛。只是文件名時(shí)胖翰,則保存至當(dāng)前目錄,否則保存至指定路徑切厘,
如 data_bak =>保存至當(dāng)前路徑
如:/tmp/data_bak =>保存至/tmp/
format: 壓縮包種類萨咳,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要壓縮的文件夾路徑(默認(rèn)當(dāng)前目錄)
owner: 用戶疫稿,默認(rèn)當(dāng)前用戶
group: 組培他,默認(rèn)當(dāng)前組
logger: 用于記錄日志,通常是logging.Logger對象
'''
# 示例
# 將當(dāng)前目錄下的a文件夾打包至當(dāng)前目錄下
res = shutil.make_archive('a','gztar',root_dir='.')
# 會生成一個(gè)a.tar.gz的文件
print(res)
# 輸出:
E:\python\s17\day06\shutil模塊\a.tar.gz
'''
壓縮歸檔模式有:
gztar ---> *.tar.gz
xztar ---> *.tar.xz
bztar ---> *.tar.bz2
'''
shutil
對壓縮包的處理是調(diào)用ZipFile
和TarFile
兩個(gè)模塊來進(jìn)行的遗座,詳細(xì):
zipfile壓縮和解壓縮:
import zipfile
# 壓縮
z = zipfile.ZipFile('b.zip','w')
z.write(r'.\a\b')
z.close()
# 解壓
x = zipfile.ZipFile('b.zip','r')
x.extractall(path='.')
x.close()
tarfile壓縮解壓縮:
import tarfile
# 壓縮
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()
# 解壓
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()
六舀凛、json模塊與pickle模塊
之前嘗過內(nèi)置函數(shù)eval()
可以將一個(gè)字符串轉(zhuǎn)成python對象,不過途蒋,eval方法是有局限性的猛遍,對于普通的數(shù)據(jù)類型,json.loads()
和eval()
;但遇到特殊類型的時(shí)候号坡,eval就不管用了懊烤,所以eval的重點(diǎn)還是通常用來執(zhí)行一個(gè)字符串表達(dá)式,并返回表達(dá)式的值宽堆。
import json
x = '[null,true,false,1]'
# print(eval(x))
# 會報(bào)錯
print(json.loads(x))
# 會轉(zhuǎn)換成[None, True, False, 1]
1. 什么是序列化腌紧?
我們把對象(變量)從內(nèi)存中變成可存儲或可傳輸?shù)倪^程稱之為序列化,在Python中叫pickling日麸,在其他語言中被稱為serialization寄啼、marshalling、flattening等等代箭,都是一個(gè)意思。
2. 為什么要序列化涕刚?
持久保存狀態(tài)
程序的執(zhí)行就是在處理一系列狀態(tài)的變化嗡综,在編程語言中,狀態(tài)會以各種各樣有結(jié)構(gòu)的數(shù)據(jù)類型的形式被保存在內(nèi)存中杜漠。但是內(nèi)存是無法永久保存數(shù)據(jù)的极景,當(dāng)程序運(yùn)行一段時(shí)間察净,我們斷電或者重啟程序,內(nèi)存中關(guān)于這個(gè)程序的之前一段時(shí)間有結(jié)構(gòu)的數(shù)據(jù)都被清空了盼樟。在斷電或重啟程序之前將程序當(dāng)前內(nèi)存中所有的數(shù)據(jù)都保存下來(保存到文件中)氢卡,以便于下次程序執(zhí)行能夠從文件中載入之前的數(shù)據(jù),然后繼續(xù)執(zhí)行晨缴,這就是序列化译秦。
例如:你玩使命召喚闖到了第13關(guān),然后你保存游戲狀態(tài)击碗,關(guān)機(jī)走人筑悴,下次再玩,還能從上次的位置開始繼續(xù)闖關(guān)稍途;還有像虛擬機(jī)的掛起等阁吝。跨平臺數(shù)據(jù)交互
序列化之后,不僅可以把序列化后的內(nèi)容寫入磁盤械拍,還可以通過網(wǎng)絡(luò)傳輸?shù)絼e的機(jī)器上突勇,如果收發(fā)的雙方約定好使用一種序列化格式,那么便打破了平臺/語言差異化帶來的限制坷虑,實(shí)現(xiàn)了跨平臺數(shù)據(jù)交互与境。
3. 如何序列化之json和pickle
json
如果我們要在不同的編程語言之間傳遞對象,就必須把對象序列化為標(biāo)準(zhǔn)格式猖吴,比如xml摔刁,但更好的方法是序列化為json格式,因?yàn)閖son表示出來的就是一個(gè)字符串海蔽,可以被所有語言讀取共屈,也可以方便的存儲到磁盤或者通過網(wǎng)絡(luò)傳輸。json不僅是標(biāo)準(zhǔn)格式党窜,并且比xml更快拗引,而且可以直接在web頁面中讀取,非常方便幌衣。
json表示的對象就是標(biāo)準(zhǔn)的JavaScript語言的對象矾削,json和python內(nèi)置的數(shù)據(jù)類型對應(yīng)如下:
JSON類型 | Python類型 |
---|---|
{} | dict |
[] | list |
'string' | str |
123.23 | int或float |
true/false | True/False |
null | None |
序列化代碼:
import json
dic={'name':'alvin','age':23,'sex':'male'}
print(type(dic)) # <class 'dict'>
j = json.dumps(dic) # 轉(zhuǎn)換為字符串形式
print(type(j)) # <class 'str'>
f = open('test2.txt','w')
f.write(j)
# 等價(jià)于json.dump(dic,f),
# json.dump(dic,f)
# `json.dump()`可以直接將字典dic以字符串的形式寫入文件中
f.close()
反序列化
# 反序列化
f = open('test2.txt','r',encoding='utf-8')
data = json.loads(f.read())
# 等價(jià)于data = json.load(f)豁护,
# data = json.load(f)
#可以直接將文件內(nèi)容加載并賦值
f.close()
print(type(data)) # <class 'dict'>
注意:
import json
# dct = "{'name':'caigy'}"
# j = json.loads(dct) # 這里會報(bào)錯哼凯,因?yàn)閖son.loads()生成的數(shù)據(jù)還是{'name':'caigy'}
# 將變量dct改為下面這樣
dct='{"1":"111"}'
j = json.loads(dct)
print(type(dct)) # <class 'str'>
print(type(j)) # <class 'dict'>
print(j) # 輸出:{'1': '111'}
# 這樣就不會報(bào)錯,可以看出楚里,要想通過json反序列化數(shù)據(jù)断部,那么原來的字符串?dāng)?shù)據(jù)必須是單引號里用雙引號,否則就會報(bào)錯班缎。
# 無論數(shù)據(jù)是怎樣創(chuàng)建的蝴光,只要滿足json格式她渴,就可以json.loads出來,不一定非要dumps的數(shù)據(jù)才能loads
pickle
pickle的用法與json一樣,只是json序列化后的數(shù)據(jù)類型是str蔑祟,而pickle序列化后的數(shù)據(jù)類型是bytes趁耗。
import pickle
dic={'name':'alvin','age':23,'sex':'male'}
print(type(dic)) # <class 'dict'>
p = pickle.dumps(dic)
print(type(p)) # <class 'bytes'>
print(p) # 輸出b'\x80\x03}q...maleq\x05u.'
with open('data.pic','wb') as f: # 注意w是寫入str,wb是寫入bytes
# f.write(p) # 等價(jià)于pickle.dump(dic,f)
# 或者
pickle.dump(dic,f)
with open('data.pic','rb') as f1:
# data = pickle.loads(f1.read())
# 或者
data = pickle.load(f1)
print(data) # 輸出字典{'name': 'alvin', 'sex': 'male', 'age': 23}
注:
pickle的問題和所有其他編程語言特有的序列化問題一樣,就是它只能用于Python疆虚,并且可能不同版本的python彼此都不兼容苛败,因此,只能用pickle保存那些不重要的數(shù)據(jù)装蓬,不能成功地反序列化也沒關(guān)系著拭。
七、shelve模塊
shelve模塊比pickle模塊簡單牍帚,只有一個(gè)open函數(shù)儡遮,返回類似字典的對象,可讀可寫暗赶,key必須為字符串鄙币,而值可以是python支持的數(shù)據(jù)類型。
利用shelve模塊將數(shù)據(jù)存入文件蹂随,會產(chǎn)生三個(gè)分別以".bak"十嘿、".dat"、".dir" 結(jié)尾的文件
import shelve
# 存數(shù)據(jù),存入的數(shù)據(jù)可以是列表岳锁、字典等python所支持的數(shù)據(jù)類型
f = shelve.open(r'shelve.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'}
f.close()
# 取數(shù)據(jù)绩衷,取數(shù)據(jù)時(shí)知道數(shù)據(jù)的名稱,在.dir結(jié)尾的文件中可以看到激率,然后用列表的索引或字典的key去取值
data = shelve.open(r'shelve.txt')
print(data['stu2_info']['name'])
print(data['school_info']['city'])
data.close()
'''
# 輸出結(jié)果
gangdan
beijing
'''
八咳燕、xml模塊
xml是實(shí)現(xiàn)不同語言或程序之間進(jìn)行數(shù)據(jù)交換的協(xié)議,跟json差不多乒躺,但是json使用起來更簡單招盲,不過在json還沒誕生的時(shí)候,主要使用就是xml嘉冒,至今很多傳統(tǒng)公司曹货,如金融行業(yè)的很多系統(tǒng)的接口還主要是xml。
xml的格式如下讳推,就是通過<>
節(jié)點(diǎn)來區(qū)別數(shù)據(jù)結(jié)構(gòu)的:
示例文件:exa.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>
xml協(xié)議在各個(gè)語言里都是支持的顶籽,在python中可以用以下模塊操作xml;
1. 解析xml
- 利用ElementTree.XML將字符串解析成xml對象
# 利用ElementTree.XML將字符串解析成xml對象
from xml.etree import ElementTree as ET
# 打開文件娜遵,讀取XML內(nèi)容
str_xml = open('xo.xml', 'r').read()
# 將字符串解析成xml特殊對象蜕衡,root代指xml文件的根節(jié)點(diǎn)
root = ET.XML(str_xml)
- 利用ElementTree.parse將文件直接解析成xml對象
利用ElementTree.parse將文件直接解析成xml對象
from xml.etree import ElementTree as ET
# 直接解析xml文件
tree = ET.parse("xo.xml")
# 獲取xml文件的根節(jié)點(diǎn)
root = tree.getroot()
2. 操作xml
# 以下得到的是迭代器,需要通過for循環(huán)查看
# print(root.iter('year')) #全文搜索
# print(root.find('country')) #在root的子節(jié)點(diǎn)找设拟,只找一個(gè)
# print(root.findall('country')) #在root的子節(jié)點(diǎn)找慨仿,找所有
xml格式類型是節(jié)點(diǎn)嵌套節(jié)點(diǎn),對于每一個(gè)節(jié)點(diǎn)均有以下功能纳胧,以便對當(dāng)前節(jié)點(diǎn)進(jìn)行操作镰吆;
'''
tag #當(dāng)前節(jié)點(diǎn)的標(biāo)簽名
attrib #當(dāng)前節(jié)點(diǎn)的屬性
text #當(dāng)前節(jié)點(diǎn)的內(nèi)容
makeelement(self, tag, attrib) #創(chuàng)建一個(gè)新節(jié)點(diǎn)
append(self, subelement) #為當(dāng)前節(jié)點(diǎn)追加一個(gè)子節(jié)點(diǎn)
extend(self, elements) #為當(dāng)前節(jié)點(diǎn)擴(kuò)展n個(gè)子節(jié)點(diǎn)
insert(self, index, subelement) #為當(dāng)前節(jié)點(diǎn)創(chuàng)建子節(jié)點(diǎn),然后插入指定位置
remove(self, subelement) #在當(dāng)前節(jié)點(diǎn)中刪除某個(gè)子節(jié)點(diǎn)
find(self, path, namespaces=None) #獲取第一個(gè)尋找到的子節(jié)點(diǎn)
findtext(self, path, default=None, namespaces=None) #獲取第一個(gè)尋找到的子節(jié)點(diǎn)的內(nèi)容
findall(self, path, namespaces=None) #查找所有子節(jié)點(diǎn)
iterfind(self, path, namespaces=None) #獲取所有指定的節(jié)點(diǎn)跑慕,并創(chuàng)建一個(gè)迭代器(可以被for循環(huán))
iter(self, tag=None) #根據(jù)節(jié)點(diǎn)名稱尋找所有指定的節(jié)點(diǎn)万皿,并返回一個(gè)迭代器
itertext(self) #根據(jù)節(jié)點(diǎn)名稱尋找所有指定的節(jié)點(diǎn)的內(nèi)容,并返回一個(gè)迭代器
clear(self) #清空節(jié)點(diǎn)
get(self, key, default=None #獲取當(dāng)前節(jié)點(diǎn)的屬性值
set(self, key, value) #為當(dāng)前節(jié)點(diǎn)設(shè)置屬性值
keys(self) #獲取當(dāng)前節(jié)點(diǎn)的所有屬性的 key
items(self) #獲取當(dāng)前節(jié)點(diǎn)的所有屬性值核行,每個(gè)屬性都是一個(gè)鍵值對
'''
a. 遍歷xml文件中所有內(nèi)容
import xml.etree.ElementTree as ET
tree = ET.parse('exa.xml') # 直接解析xml文件
root = tree.getroot() # xml文件的根節(jié)點(diǎn)
print(root.tag) # 頂層標(biāo)簽
# 遍歷XML文檔的第二層
for child in root:
# 第二層節(jié)點(diǎn)的標(biāo)簽名稱和標(biāo)簽屬性
print(child.tag,child.attrib)
for i in child:
# 第二層節(jié)點(diǎn)的標(biāo)簽名稱和內(nèi)容
print(i.tag,i.text)
b. 遍歷xml中指定的節(jié)點(diǎn)
import xml.etree.ElementTree as ET
tree = ET.parse('exa.xml') # 直接解析xml文件
root = tree.getroot() # xml文件的根節(jié)點(diǎn)
print(root.tag) # 頂層標(biāo)簽
# 遍歷XML中所有的year節(jié)點(diǎn)
for node in root.iter('year'):
# 節(jié)點(diǎn)的標(biāo)簽名稱和內(nèi)容
print(node.tag,node.text)
c. 修改節(jié)點(diǎn)內(nèi)容
由于修改節(jié)點(diǎn)時(shí)牢硅,均是在內(nèi)存中進(jìn)行,其不會影響文件中的內(nèi)容芝雪,所以减余,如果想要修改,則需要重新將內(nèi)存中的內(nèi)容寫到文件中惩系。
# 循環(huán)所有的year節(jié)點(diǎn)
for node in root.iter('year'):
# 將year節(jié)點(diǎn)中的內(nèi)容自增一
node.text = str(int(node.text)+1)
# 將year節(jié)點(diǎn)名改為year_up
node.tag = node.tag+'_up'
# 設(shè)備屬性
node.set('updated','yes')
node.set('version','1.0')
# 刪除屬性
del node.attrib['updated']
# 保存至文件位岔,可以寫原文件名
tree.write('exa_3.xml',encoding='utf-8')
d. 增加子節(jié)點(diǎn)
# 增加節(jié)點(diǎn)
#在country下添加一個(gè)子節(jié)點(diǎn)year_new
for country in root:
#生成一個(gè)新的子節(jié)點(diǎn)
year_new = ET.Element('year_new')
#設(shè)置屬性
year_new.set('name','alex')
#添加內(nèi)容,內(nèi)容為原year內(nèi)容加1
year_new.text = str(int(country.find('year').text)+1)
#將新生成的子節(jié)點(diǎn)追加至每個(gè)country節(jié)點(diǎn)
country.append(year_new)
# 保存至文件中
tree.write('exa_5.xml',encoding='utf-8')
e. 刪除節(jié)點(diǎn)
import xml.etree.ElementTree as ET
tree = ET.parse('exa_5.xml') # 直接解析xml文件
root = tree.getroot() # xml文件的根節(jié)點(diǎn)
for country in root:
for item in country.findall('year_new'):
# 獲取country節(jié)點(diǎn)的year_new的內(nèi)容
year = int(item.text)
if year < 2010:
# 刪除指定year_new節(jié)點(diǎn)
print(item.tag,item.text)
country.remove(item)
# 寫入文件
tree.write('exa_5.xml',encoding='utf-8')
3. 創(chuàng)建xml文檔
方式一
#創(chuàng)建方式(一)
from xml.etree import ElementTree as ET
# 創(chuàng)建根節(jié)點(diǎn)
root = ET.Element("famliy")
# 創(chuàng)建節(jié)點(diǎn)大兒子
son1 = ET.Element('son', {'name': '兒1'})
# 創(chuàng)建小兒子
son2 = ET.Element('son', {"name": '兒2'})
# 在大兒子中創(chuàng)建兩個(gè)孫子
grandson1 = ET.Element('grandson', {'name': '兒11'})
grandson2 = ET.Element('grandson', {'name': '兒12'})
son1.append(grandson1)
son1.append(grandson2)
# 把兒子添加到根節(jié)點(diǎn)中
root.append(son1)
root.append(son1)
tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
方式二
#創(chuàng)建方式(二)
from xml.etree import ElementTree as ET
# 創(chuàng)建根節(jié)點(diǎn)
root = ET.Element("famliy")
# 創(chuàng)建大兒子
# son1 = ET.Element('son', {'name': '兒1'})
son1 = root.makeelement('son', {'name': '兒1'})
# 創(chuàng)建小兒子
# son2 = ET.Element('son', {"name": '兒2'})
son2 = root.makeelement('son', {"name": '兒2'})
# 在大兒子中創(chuàng)建兩個(gè)孫子
# grandson1 = ET.Element('grandson', {'name': '兒11'})
grandson1 = son1.makeelement('grandson', {'name': '兒11'})
# grandson2 = ET.Element('grandson', {'name': '兒12'})
grandson2 = son1.makeelement('grandson', {'name': '兒12'})
son1.append(grandson1)
son1.append(grandson2)
# 把兒子添加到根節(jié)點(diǎn)中
root.append(son1)
root.append(son1)
tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
方式三
#創(chuàng)建方式(三)
from xml.etree import ElementTree as ET
# 創(chuàng)建根節(jié)點(diǎn)
root = ET.Element("famliy")
# 創(chuàng)建節(jié)點(diǎn)大兒子
son1 = ET.SubElement(root, "son", attrib={'name': '兒1'})
# 創(chuàng)建小兒子
son2 = ET.SubElement(root, "son", attrib={"name": "兒2"})
# 在大兒子中創(chuàng)建一個(gè)孫子
grandson1 = ET.SubElement(son1, "age", attrib={'name': '兒11'})
grandson1.text = '孫子'
et = ET.ElementTree(root) #生成文檔對象
et.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False)
# xml_declaration=True 表示開啟xml文檔的聲明
# <?xml version="1.0"?> 這樣的聲明
由于原生保存的XML時(shí)默認(rèn)無縮進(jìn)堡牡,如果想要設(shè)置縮進(jìn)的話抒抬, 需要修改保存方式:
from xml.etree import ElementTree as ET
from xml.dom import minidom
def prettify(elem):
"""將節(jié)點(diǎn)轉(zhuǎn)換成字符串,并添加縮進(jìn)晤柄。
"""
rough_string = ET.tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent="\t")
# 創(chuàng)建根節(jié)點(diǎn)
root = ET.Element("famliy")
# 創(chuàng)建大兒子
# son1 = ET.Element('son', {'name': '兒1'})
son1 = root.makeelement('son', {'name': '兒1'})
# 創(chuàng)建小兒子
# son2 = ET.Element('son', {"name": '兒2'})
son2 = root.makeelement('son', {"name": '兒2'})
# 在大兒子中創(chuàng)建兩個(gè)孫子
# grandson1 = ET.Element('grandson', {'name': '兒11'})
grandson1 = son1.makeelement('grandson', {'name': '兒11'})
# grandson2 = ET.Element('grandson', {'name': '兒12'})
grandson2 = son1.makeelement('grandson', {'name': '兒12'})
son1.append(grandson1)
son1.append(grandson2)
# 把兒子添加到根節(jié)點(diǎn)中
root.append(son1)
root.append(son1)
raw_str = prettify(root)
f = open("xxxoo.xml",'w',encoding='utf-8')
f.write(raw_str)
f.close()
九擦剑、configparser模塊
配置文件a.cfg內(nèi)容如下:
# 注釋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')
#查看所有的標(biāo)題
title = config.sections()
print(title) #返回['section1', 'section2']
#查看標(biāo)題section1下所有key=value的key
options = config.options('section1')
print(options) #返回['k1', 'k2', 'user', 'age', 'is_admin', 'salary']
#查看標(biāo)題section2下所有key=value的(key,value)元組格式
tu_title = config.items('section2')
print(tu_title) #返回[('k1', 'v1')],列表中的元素是元組
#查看標(biāo)題section1下user的值=>字符串格式
val = config.get('section1','user')
print(val) #返回egon
#查看標(biāo)題section1下age的值=>整數(shù)格式
age = config.getint('section1','age')
print(age) #返回18
#查看標(biāo)題section1下is_admin的值=>布爾值格式
val_1 = config.getboolean('section1','is_admin')
print(val_1) #返回True
#查看標(biāo)題section1下salary的值=>浮點(diǎn)型格式
val_2 = config.getfloat('section1','salary')
print(val_2) #返回31.0
2. 改寫和判斷
#改寫
config = configparser.ConfigParser()
config.read('a.cfg')
#刪除整個(gè)標(biāo)題section2
config.remove_section('section2')
#刪除標(biāo)題section1下的k1
config.remove_option('section1','k1')
#判斷是否存在某個(gè)標(biāo)題
print(config.has_section('section1')) #存在返回True
#判斷標(biāo)題section1下是否有user
print(config.has_option('section1','user')) #存在返回True
#最后將修改的內(nèi)容寫入文件,完成最終的修改
config.write(open('a.cfg','w'))
3. 添加
#添加一個(gè)標(biāo)題
config.add_section('egon')
#在標(biāo)題egon下添加name=egon,age=18的配置
config.set('egon','name','egon')
config.set('egon','age','18') #所有內(nèi)容必須是字符串
# 添加新節(jié)點(diǎn)
config = configparser.ConfigParser()
config.add_section('caigy')
config['caigy']['username'] = 'caigy'
config['caigy']['age'] = '18'
config.write(open('test.ini','w',encoding='utf-8'))
'''
生成的內(nèi)容如下:
[caigy]
username = caigy
age = 18
'''
4. 基于上述方法添加一個(gè)ini配置文件
# 基于上述方法添加一個(gè)ini文檔
import configparser
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
'Compression': 'yes',
'CompressionLevel': '9'}
config['bitbucket.org'] = {}
config['bitbucket.org']['User'] = 'hg'
config['topsecret.server.com'] = {}
topsecret = config['topsecret.server.com']
topsecret['Host Port'] = '50022' # mutates the parser
topsecret['ForwardX11'] = 'no' # same here
config['DEFAULT']['ForwardX11'] = 'yes'
with open('example.ini', 'w') as configfile:
config.write(configfile)
以上內(nèi)容實(shí)現(xiàn)的ini配置文件內(nèi)容如下:
[DEFAULT]
serveraliveinterval = 45
compression = yes
compressionlevel = 9
forwardx11 = yes
[bitbucket.org]
user = hg
[topsecret.server.com]
host port = 50022
forwardx11 = no
十芥颈、hashlib模塊
hash:一種算法 ,3.x里代替了md5模塊和sha模塊惠勒,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法浇借。
三個(gè)特點(diǎn):
- 1.內(nèi)容相同則hash運(yùn)算結(jié)果相同捉撮,內(nèi)容稍微改變則hash值則變
- 2.不可逆推
- 3.相同算法:無論校驗(yàn)多長的數(shù)據(jù),得到的哈希值長度固定妇垢。
import hashlib
m = hashlib.md5() # m = hashlib.sha256()
m.update('hello'.encode('utf-8')) #哈希前必須先encode
print(m.hexdigest()) #輸出16進(jìn)制的md5校驗(yàn)碼
print(m.digest()) #2進(jìn)制格式hash
'''
注意:把一段很長的數(shù)據(jù)update多次巾遭,與一次update這段長數(shù)據(jù),得到的結(jié)果一樣
但是update多次為校驗(yàn)大文件提供了可能闯估。
'''
import hashlib
# ######## md5 ########
hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())
# ######## sha1 ########
hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())
# ######## sha256 ########
hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())
# ######## sha384 ########
hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())
# ######## sha512 ########
hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())
對文件進(jìn)行哈希校驗(yàn)
import hashlib
m = hashlib.md5()
# 由于哈希前必須encode灼舍,所以可以直接用rb的方式打開文件
# 這樣在哈希時(shí)就不用再encode了
with open(r'E:\python\s17\day01\login.py','rb') as f:
for line in f:
m.update(line) #每次update一行,最終得到的結(jié)果永遠(yuǎn)一樣
print(m.hexdigest())
以上加密算法雖然依然非常厲害涨薪,但時(shí)候存在缺陷骑素,即:通過撞庫可以反解。
模擬撞庫破解密碼:
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'
break_code(cryptograph,make_passwd_dic(passwds))
為了解決這個(gè)缺陷刚夺,python 還有一個(gè) hmac 模塊献丑,它內(nèi)部對我們創(chuàng)建 key 和 內(nèi)容 再進(jìn)行處理然后再加密
散列消息鑒別碼末捣,簡稱HMAC,是一種基于消息鑒別碼MAC(Message Authentication Code)的鑒別機(jī)制创橄。使用HMAC時(shí),消息通訊的雙方箩做,通過驗(yàn)證消息中加入的鑒別密鑰K來鑒別消息的真?zhèn)危?/p>
一般用于網(wǎng)絡(luò)通信中消息加密,前提是雙方先要約定好key,就像接頭暗號一樣妥畏,然后消息發(fā)送把用key把消息加密邦邦,接收方用key + 消息明文再加密,拿加密后的值 跟 發(fā)送者的相對比是否相等醉蚁,這樣就能驗(yàn)證消息的真實(shí)性燃辖,及發(fā)送者的合法性了。
import hmac
h = hmac.new(bytes('898oaFs09f',encoding="utf-8"))
h.update(bytes('admin',encoding="utf-8"))
print(h.hexdigest())
十一网棍、subprocess模塊
subprocess模塊允許您生成新進(jìn)程黔龟,連接到輸入/輸出/錯誤管道,并獲取其返回代碼确沸。 該模塊意圖替換幾個(gè)較舊的模塊和功能:
os.system
os.spawn*
常用subprocess方法示例
#執(zhí)行命令溜畅,返回命令執(zhí)行狀態(tài) 光坝, 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])
#執(zhí)行命令堡僻,如果命令結(jié)果為0浩螺,就正常返回,否則拋異常
>>> subprocess.check_call(["ls", "-l"])
0
#接收字符串格式命令桨菜,返回元組形式豁状,第1個(gè)元素是執(zhí)行狀態(tài),第2個(gè)是命令結(jié)果
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')
#接收字符串格式命令倒得,并返回結(jié)果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'
#執(zhí)行命令泻红,并返回結(jié)果,注意是返回結(jié)果霞掺,不是打印谊路,下例結(jié)果返回給res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'
上面那些方法,底層都是封裝的subprocess.Popen
poll() #檢查子進(jìn)程是否終止菩彬。 返回returncode
wait() #等待子進(jìn)程終止缠劝。 返回returncode屬性。
terminate() #殺掉所啟動進(jìn)程
communicate() #等待任務(wù)結(jié)束
stdin #標(biāo)準(zhǔn)輸入
stdout #標(biāo)準(zhǔn)輸出
stderr #標(biāo)準(zhǔn)錯誤
pid #子進(jìn)程的進(jìn)程ID骗灶。
#例子
>>> p = subprocess.Popen("df -h|grep disk",stdout=subprocess.PIPE,shell=True)
>>> p.stdout.read() #輸出的是bytes類型惨恭,如果輸出的有漢字則會出現(xiàn)亂碼,因此要進(jìn)行decode('utf-8')
b'/dev/disk1 465Gi 64Gi 400Gi 14% 16901472 104938142 14% /\n'
#stdout=subprocess.PIPE #表示將命令執(zhí)行的正確輸出耙旦,輸出到管道里脱羡;
#shell=True #這個(gè)參數(shù)會傳遞給Popen 代表的是執(zhí)行命令之前先開一個(gè)shell來執(zhí)行 ,默認(rèn)的shell為/bin/sh 锉罐,Popen(['ls', '-l'], shell=True) 等效于 Popen(['/bin/sh',' ls', '-l'])帆竹,而如果你不加shell=True 則會開啟一個(gè)子進(jìn)程 并且在子進(jìn)程直接執(zhí)行。
subprocess.run()
>>> subprocess.run(["ls", "-l"]) # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
-rw-r--r--. 1 root root 2283 May 22 10:12 SQL.txt
drwxr-xr-x. 2 root root 4096 May 13 17:31 Templates
drwxr-xr-x. 2 root root 4096 May 13 17:31 Videos
-rw-r--r--. 1 root root 10143 Mar 13 18:01 zabbix筆記.txt
CompletedProcess(args=['ls', '-l'], returncode=0)
>>> subprocess.run("exit 1",shell=True,check=False)
CompletedProcess(args='exit 1', returncode=1)
>>> subprocess.run("exit 1",shell=True,check=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/rh/rh-python35/root/usr/lib64/python3.5/subprocess.py", line 711, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
#check=True #表示開啟檢測命令執(zhí)行狀態(tài)氓鄙,如果返回狀態(tài)碼為非0測報(bào)錯馆揉。
模擬ls /root | grep "txt$"
的功能
res_1 = subprocess.Popen('ls /root',shell=True,
stdout=subprocess.PIPE, #標(biāo)準(zhǔn)正確輸出放入管道
stderr=subprocess.PIPE) #錯誤輸出放入另一管道
res_2 = subprocess.Popen('grep txt$',shell=True,
stdin=res_1.stdout, #標(biāo)準(zhǔn)輸入接收res_1的標(biāo)準(zhǔn)正確輸出
stdout=subprocess.PIPE) #標(biāo)準(zhǔn)輸出放入管道
print(res_2.stdout.read().decode('utf-8')) #結(jié)果是bytes類型业舍,需要decode
#輸出:
SQL.txt
zabbix筆記.txt
十二抖拦、logging模塊
很多程序都有記錄日志的需求,并且日志中包含的信息即有正常的程序訪問日志舷暮,還可能有錯誤态罪、警告等信息輸出,python的logging模塊提供了標(biāo)準(zhǔn)的日志接口下面,你可以通過它存儲各種格式的日志复颈,logging的日志可以分為 debug(), info(), warning(), error() and critical() 5個(gè)級別,下面我們看一下怎么用沥割。
1.簡單將日志打印到屏幕
import logging
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
'''
屏幕上打印:
WARNING:root:This is warning message
'''
默認(rèn)情況下耗啦,logging將日志打印到屏幕,日志級別為WARNING机杜;
日志級別大小關(guān)系為:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET帜讲,當(dāng)然也可以自己定義日志級別
日志級別
Level | Numeric | When it's used |
---|---|---|
DEBUG | 10 | 詳細(xì)信息,通常僅在調(diào)試程序時(shí)才用 |
INFO | 20 | 確認(rèn)程序按預(yù)期執(zhí)行 |
WARNING | 30 | 表明意外發(fā)生的事情椒拗,或在不久的將來表示某些問題(例如“磁盤空間低”)似将,該軟件仍然按預(yù)期工作。 |
ERROR | 40 | 由于更嚴(yán)重的問題蚀苛,軟件無法執(zhí)行某些功能在验。 |
CRITICAL | 50 | 一個(gè)嚴(yán)重的錯誤,表示程序本身可能無法繼續(xù)運(yùn)行堵未。 |
2. 通過logging.basicConfig函數(shù)對日志的輸出格式及輸出位置做相關(guān)配置
import logging
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='myapp.log',
filemode='w')
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
./myapp.log文件中內(nèi)容為:
Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message
format日志格式
format | 描述 |
---|---|
%(name)s | Logger的名字腋舌,并非用戶名 |
%(levelno)s | 數(shù)字形式的日志級別 |
%(levelname)s | 文本形式的日志級別 |
%(pathname)s | 調(diào)用日志輸出函數(shù)的模塊的完整路徑名,可能沒用 |
%(filename)s | 調(diào)用日志輸出函數(shù)的模塊的文件名 |
%(module)s | 調(diào)用日志輸出函數(shù)的模塊名 |
%(funcName)s | 調(diào)用日志輸出函數(shù)的函數(shù)名 |
%(lineno)d | 調(diào)用日志輸出函數(shù)的語句所在的代碼行 |
%(created)f | 當(dāng)前時(shí)間渗蟹,用UNIX標(biāo)準(zhǔn)的表示時(shí)間的浮 點(diǎn)數(shù)表示 |
%(relativeCreated)d | 輸出日志信息時(shí)的块饺,自Logger創(chuàng)建以 來的毫秒數(shù) |
%(asctime)s | 字符串形式的當(dāng)前時(shí)間。默認(rèn)格式是 “2003-07-08 16:49:45,896”拙徽。逗號后面的是毫秒 |
%(thread)d | 線程ID刨沦。可能沒有 |
%(threadName)s | 線程名膘怕∠胱纾可能沒有 |
%(process)d | 進(jìn)程ID。可能沒有 |
%(message)s | 用戶輸出的消息 |
3. 如果想同時(shí)把log打印在屏幕和日志文件里来破,就需要了解一些復(fù)雜的知識篮灼。
python使用logging
模塊記錄日志涉及四個(gè)主要類,使用官方文檔中的概括最為合適:
- logger 提供了應(yīng)用程序可以直接使用的接口徘禁;
- handler 將(logger創(chuàng)建的)日志記錄發(fā)送到你定義的輸出位置诅诱,可以是屏幕、文件送朱、遠(yuǎn)程網(wǎng)絡(luò)(比如通郵件發(fā)送)
- filter 提供了過濾功能娘荡,可以過濾包含什么字段的怎么去發(fā)送日志,這個(gè)比較復(fù)雜驶沼,用得也比較少炮沐。
- formatter 設(shè)置日志記錄的最終輸出格式。
四個(gè)主要類
logger
- 每個(gè)程序在輸出日志信息之前都要獲得一個(gè)
logger
回怜。logger
通常對應(yīng)了程序的模塊名大年,比如聊天工具的圖形界面模塊可以這樣獲得它的logger
:
logger = logging.getLogger("chat.gui")
核心模塊可以這樣:
logger = logging.getLogger("chat.kernel")
- 指定最低的日志級別,低于設(shè)定的最低日志級別的日志將被忽略玉雾。debug是最低的內(nèi)置級別翔试,critical為最高。
logger.setLevel(logging.DEBUG) #最低日志級別為DEBUG
- 創(chuàng)建一個(gè)日志格式器复旬,規(guī)定了日志輸出格式:
fmter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger的其他操作:
logger.addFilter(filt) #添加指定的filter
logger.removeFilter(filt) #刪除指定的filter
logger.addHandler(hdlr) #增加指定的handler
logger.removeHandler(hdlr) #刪除指定的handler
logger.debug(msg)垦缅、logger.info(msg)、logger.warning(msg)赢底、logger.error(msg)失都、logger.critical(msg) #生成相應(yīng)級別的日志信息
handler
handler對象負(fù)責(zé)發(fā)送相關(guān)的信息到指定目的地。python的日志系統(tǒng)有多種Handler可以使用幸冻。
- logging.StreamHandler() 可以向屏幕輸出日志信息
-
- logging.FileHandler(filename[,mode]) 用于向一個(gè)文件輸出日志信息粹庞。
- filename 是文件名,必須指定一個(gè)文件名洽损;
- mode是文件的打開方式庞溜,參見python內(nèi)置函數(shù)open()的用法。默認(rèn)打開方式是'a'碑定,即追加到文件末尾流码。
-
- handlers.RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]]) 用于切割日志文件,當(dāng)日志文件達(dá)到指定的大小之后延刘,自動把文件改名漫试,然后創(chuàng)建一個(gè)新的同名日志文件繼續(xù)輸出。比如日志文件是chat.log碘赖,當(dāng)chat.log達(dá)到指定的大小之后驾荣,RotatingFileHandler自動把 文件改名為chat.log.1外构。不過,如果chat.log.1已經(jīng)存在播掷,會先把chat.log.1重命名為chat.log.2。垒酬。件炉。最后重新創(chuàng)建 chat.log勘究,繼續(xù)輸出日志信息妻率。
- filename和mode兩個(gè)參數(shù)和FileHandler一樣;
- maxBytes 用于指定日志文件的最大文件大小宫静,單位是Bytes(字節(jié));如果maxBytes為0孤里,意味著日志文件可以無限大捌袜,這時(shí)上面描述的重命名的過程就不會發(fā)生虏等。
- backupCount 用于指定保留的備份文件的個(gè)數(shù)适肠;比如侯养,如果指定為2逛揩,當(dāng)上面描述的重命名過程發(fā)生時(shí)辩稽,原有的chat.log.2并不會被更名逞泄,而是被刪除静檬。
-
- handlers.TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]]) 與RotatingFileHandler類似拂檩,也是用于切割日志文件稻励,不過望抽,它是通過間隔一定時(shí)間來自動創(chuàng)建新的日志文件煤篙;重命名的過程與RotatingFileHandler類似辑奈,不過新的文件不是附加數(shù)字鸠窗,而是當(dāng)前時(shí)間。
- 其中filename參數(shù)和backupCount參數(shù)和RotatingFileHandler具有相同的意義臣嚣;
- interval 是指時(shí)間間隔;
-
when 是一個(gè)字符串揪垄,用來表示時(shí)間間隔的單位饥努,不區(qū)分大小寫酷愧。它有以下取值:
- S 秒
- M 分
- H 小時(shí)
- D 天
- W 每星期(interval==0時(shí)代表星期一)
- midnight 每天凌晨
注:想要使用RotatingFileHandler和TimedRotatingFileHandler 溶浴,需要導(dǎo)入handlers模塊士败,如下:
from logging import handlers
#用法
fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)
handler相關(guān)的其它操作
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG) #指定被處理的信息級別
ch.setFormatter(fmter) #給這個(gè)handler選擇一個(gè)日志輸出格式谅将,fmter是通過logging.Formatter()定義好的饥臂;
ch.addFilter(filter)隅熙、ch.removeFilter(filter) #新增或刪除一上filter對象
Formatter
logging.Formatter()的用法在logger的第3條有所體現(xiàn)
示例一
需求:將日志信息同時(shí)輸出到屏幕和文件中猛们,設(shè)定輸出到屏幕上的日志級別為DEBUG弯淘,設(shè)定輸出到文件中的日志級別為WARNING庐橙。
import logging
#1 創(chuàng)建Logger,設(shè)定最低日志級別
logger = logging.getLogger('Chat.Gui')
logger.setLevel(logging.DEBUG) #最低日志級別為DEBUG
#2 創(chuàng)建屏幕輸出态鳖,和日志級別DEBUG
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
#3 創(chuàng)建文件輸出浆竭,和日志級別為WARNING
fh = logging.FileHandler('access.log',encoding='utf-8')
fh.setLevel(logging.WARNING)
#4 創(chuàng)建一個(gè)日志格式器
fmter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s : %(message)s')
#5 添加日志格式器到 ch 和 fh
ch.setFormatter(fmter)
fh.setFormatter(fmter)
#6 添加ch 和 fh至logger
logger.addHandler(ch)
logger.addHandler(fh)
#7 分別輸出各個(gè)日志級別的信息進(jìn)行測試
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
代碼執(zhí)行后邦泄,輸出到屏幕的日志級別是DEBUG顺囊,所以記錄了DEBUG級別以上的所有日志信息特碳,如下圖:
輸出到日志文件的日志級別是WARNING站宗,所以只記錄WARNING以上級別的日志信息梢灭,如下圖:
示例二
自動分割日志文件
通過規(guī)定文件大小分割或者通過規(guī)定時(shí)間間隔分割
import logging
from logging import handlers
import time
#1 創(chuàng)建logger
logger = logging.getLogger(__name__)
log_file = 'timelog0.log'
#設(shè)定日志級別
logger.setLevel(logging.WARNING)
#生成日志分割處理器
#按日志大小分割
# fh = handlers.RotatingFileHandler(filename=log_file,
# encoding='utf-8',
# maxBytes=10,
# backupCount=3)
#按時(shí)間間隔分割
fh = handlers.TimedRotatingFileHandler(filename=log_file,
encoding='utf-8',
when='S',
interval=5,
backupCount=3)
#生成日志格式器
fmtter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')
#為fh添加fmtter
fh.setFormatter(fmtter)
#添加處理器
logger.addHandler(fh)
#測試
logger.warning('test message 1')
time.sleep(2)
logger.warning('test message 2')
time.sleep(2)
logger.warning('test message 3')
time.sleep(2)
logger.warning('test message 4')
time.sleep(2)
logger.warning('test message 5')
日志輸出到其它位置
logging.handlers.SocketHandler: 遠(yuǎn)程輸出日志到TCP/IP sockets
logging.handlers.DatagramHandler: 遠(yuǎn)程輸出日志到UDP sockets
logging.handlers.SMTPHandler: 遠(yuǎn)程輸出日志到郵件地址
logging.handlers.SysLogHandler: 日志輸出到syslog
logging.handlers.NTEventLogHandler: 遠(yuǎn)程輸出日志到Windows NT/2000/XP的事件日志
logging.handlers.MemoryHandler: 日志輸出到內(nèi)存中的制定buffer
logging.handlers.HTTPHandler: 通過"GET"或"POST"遠(yuǎn)程輸出到HTTP服務(wù)器
由于StreamHandler和FileHandler是常用的日志處理方式,所以直接包含在logging模塊中颂暇,而其他方式則包含在logging.handlers模塊中耳鸯。
上述其它處理方式的使用請參見python3.5官方手冊
通過logging.config模塊配置日志
#logger.conf
###############################################
[loggers]
keys=root,example01,example02
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
###############################################
[handlers]
keys=hand01,hand02,hand03
[handler_hand01]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('myapp.log', 'a')
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form02
args=('myapp.log', 'a', 10*1024*1024, 5)
###############################################
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=
import logging
import logging.config
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example01")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')
import logging
import logging.config
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example02")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')