第四章——續2
4.9 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>
xml協議在各個語言裏的都 是支持的,在python中可以用以下模塊操作xml
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() print(root.tag) #遍歷xml文檔 for child in root: print(child.tag, child.attrib) for i in child: print(i.tag,i.text) #只遍歷year 節點 for node in root.iter(‘year‘): print(node.tag,node.text)
修改和刪除xml文檔內容
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() #修改 for node in root.iter(‘year‘): new_year = int(node.text) + 1 node.text = str(new_year) node.set("updated","yes") tree.write("xmltest.xml")
#刪除node
for country in root.findall(‘country‘):
rank = int(country.find(‘rank‘).text)
if rank > 50:
root.remove(country)
tree.write(‘output.xml‘)
自己創建xml文檔
import xml.etree.ElementTree as ET new_xml = ET.Element("namelist") name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"}) age = ET.SubElement(name,"age",attrib={"checked":"no"}) sex = ET.SubElement(name,"sex") sex.text = ‘33‘ name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"}) age = ET.SubElement(name2,"age") age.text = ‘19‘ et = ET.ElementTree(new_xml) #生成文檔對象 et.write("test.xml", encoding="utf-8",xml_declaration=True) ET.dump(new_xml) #打印生成的格式
4.10 configparser模塊
此模塊用於生成和修改常見配置文檔,當前模塊的名稱在 python 3.x 版本中變更為 configparser。
看一個好多軟件的常見配置文件格式如下
```cnf [DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no ```
解析配置文件
```py >>> import configparser # 導入模塊 >>> config = configparser.ConfigParser() #實例化(生成對象) >>> config.sections() #調用sections方法 [] >>> config.read(‘example.ini‘) # 讀配置文件(註意文件路徑) [‘example.ini‘] >>> config.sections() #調用sections方法(默認不會讀取default) [‘bitbucket.org‘, ‘topsecret.server.com‘] >>> ‘bitbucket.org‘ in config #判斷元素是否在sections列表內 True >>> ‘bytebong.com‘ in config False >>> config[‘bitbucket.org‘][‘User‘] # 通過字典的形式取值 ‘hg‘ >>> config[‘DEFAULT‘][‘Compression‘] ‘yes‘ >>> topsecret = config[‘topsecret.server.com‘] >>> topsecret[‘ForwardX11‘] ‘no‘ >>> topsecret[‘Port‘] ‘50022‘ >>> for key in config[‘bitbucket.org‘]: print(key) # for循環 bitbucket.org 字典的key ... user compressionlevel serveraliveinterval compression forwardx11 >>> config[‘bitbucket.org‘][‘ForwardX11‘] ‘yes‘ ```
其它增刪改查語法
```python [group1] # 支持的兩種分隔符“=”, “:” k1 = v1 k2:v2 [group2] k1 = v1 import ConfigParser config = ConfigParser.ConfigParser() config.read(‘i.cfg‘) # ########## 讀 ########## #secs = config.sections() #print(secs) #options = config.options(‘group2‘) # 獲取指定section的keys #print(options) #item_list = config.items(‘group2‘) # 獲取指定 section 的 keys & values ,key value 以元組的形式 #print(item_list) #val = config.get(‘group1‘,‘key‘) # 獲取指定的key 的value #val = config.getint(‘group1‘,‘key‘) # ########## 改寫 ########## #sec = config.remove_section(‘group1‘) # 刪除section 並返回狀態(true, false) #config.write(open(‘i.cfg‘, "w")) # 對應的刪除操作要寫入文件才會生效 #sec = config.has_section(‘wupeiqi‘) #sec = config.add_section(‘wupeiqi‘) #config.write(open(‘i.cfg‘, "w")) # #config.set(‘group2‘,‘k1‘,11111) #config.write(open(‘i.cfg‘, "w")) #config.remove_option(‘group2‘,‘age‘) #config.write(open(‘i.cfg‘, "w")) ```
4.11 hashlib模塊
Hash,一般翻譯做“散列”,也有直接音譯為”哈希”的,就是把任意長度的輸入(又叫做預映射,pre-image),通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一的確定輸入值。
簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數。
HASH主要用於信息安全領域中加密算法,他把一些不同長度的信息轉化成雜亂的128位的編碼裏,叫做HASH值.也可以說,hash就是找到一種數據內容和數據存放地址之間的映射關系
什麽是MD5算法
MD5訊息摘要演算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼雜湊函數,可以產生出一個128位的散列值(hash value),用於確保信息傳輸完整一致。MD5的前身有MD2、MD3和MD4。
MD5功能
輸入任意長度的信息,經過處理,輸出為128位的信息(數字指紋);
不同的輸入得到的不同的結果(唯一性);
MD5算法的特點
- 壓縮性:任意長度的數據,算出的MD5值的長度都是固定的
- 容易計算:從原數據計算出MD5值很容易
- 抗修改性:對原數據進行任何改動,修改一個字節生成的MD5值區別也會很大
- 強抗碰撞:已知原數據和MD5,想找到一個具有相同MD5值的數據(即偽造數據)是非常困難的。
MD5算法是否可逆?
MD5不可逆的原因是其是一種散列函數,使用的是hash算法,在計算過程中原文的部分信息是丟失了的。
MD5用途
-
防止被篡改:
-
比如發送一個電子文檔,發送前,我先得到MD5的輸出結果a。然後在對方收到電子文檔後,對方也得到一個MD5的輸出結果b。如果a與b一樣就代表中途未被篡改。
-
比如我提供文件下載,為了防止不法分子在安裝程序中添加木馬,我可以在網站上公布由安裝文件得到的MD5輸出結果。
-
SVN在檢測文件是否在CheckOut後被修改過,也是用到了MD5.
-
-
防止直接看到明文:
- 現在很多網站在數據庫存儲用戶的密碼的時候都是存儲用戶密碼的MD5值。這樣就算不法分子得到數據庫的用戶密碼的MD5值,也無法知道用戶的密碼。(比如在UNIX系統中用戶的密碼就是以MD5(或其它類似的算法)經加密後存儲在文件系統中。當用戶登錄的時候,系統把用戶輸入的密碼計算成MD5值,然後再去和保存在文件系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟,系統在並不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統的合法性。這不但可以避免用戶的密碼被具有系統管理員權限的用戶知道,而且還在一定程度上增加了密碼被破解的難度。)
-
防止抵賴(數字簽名):
- 這需要一個第三方認證機構。例如A寫了一個文件,認證機構對此文件用MD5算法產生摘要信息並做好記錄。若以後A說這文件不是他寫的,權威機構只需對此文件重新產生摘要信息,然後跟記錄在冊的摘要信息進行比對,相同的話,就證明是A寫的了。這就是所謂的“數字簽名”。
SHA-1
安全哈希算法(Secure Hash Algorithm)主要適用於數字簽名標準(Digital Signature Standard DSS)裏面定義的數字簽名算法(Digital Signature Algorithm DSA)。對於長度小於2^64位的消息,SHA1會產生一個160位的消息摘要。當接收到消息的時候,這個消息摘要可以用來驗證數據的完整性。
SHA是美國國家安全局設計的,由美國國家標準和技術研究院發布的一系列密碼散列函數。
由於MD5和SHA-1於2005年被山東大學的教授王小雲破解了,科學家們又推出了SHA224, SHA256, SHA384, SHA512,當然位數越長,破解難度越大,但同時生成加密的消息摘要所耗時間也更長。目前最流行的是加密算法是SHA-256 .
MD5與SHA-1的比較
由於MD5與SHA-1均是從MD4發展而來,它們的結構和強度等特性有很多相似之處,SHA-1與MD5的最大區別在於其摘要比MD5摘要長32 比特。對於強行攻擊,產生任何一個報文使之摘要等於給定報文摘要的難度:MD5是2128數量級的操作,SHA-1是2160數量級的操作。產生具有相同摘要的兩個報文的難度:MD5是264是數量級的操作,SHA-1 是280數量級的操作。因而,SHA-1對強行攻擊的強度更大。但由於SHA-1的循環步驟比MD5多80:64且要處理的緩存大160比特:128比特,SHA-1的運行速度比MD5慢。
Python的提供的相關模塊
用於加密相關的操作,3.x裏代替了md5模塊和sha模塊,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
import hashlib m = hashlib.md5() m.update(b"Hello") m.update(b"It‘s me") print(m.digest()) m.update(b"It‘s been a long time since last time we ...") print(m.digest()) #2進制格式hash print(len(m.hexdigest())) #16進制格式hash ‘‘‘ def digest(self, *args, **kwargs): # real signature unknown """ Return the digest value as a string of binary data. """ pass def hexdigest(self, *args, **kwargs): # real signature unknown """ Return the digest value as a string of hexadecimal digits. """ pass ‘‘‘ 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())
4.12 subprocess模塊
我們經常需要通過Python去執行一條系統命令或腳本,系統的shell命令是獨立於你的python進程之外的,每執行一條命令,就是發起一個新進程,通過python調用系統命令或腳本的模塊在python2有os.system,如:
>>> os.system(‘uname -a‘) Darwin Alexs-MacBook-Pro.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun 4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64 0
這條命令的實現原理是什麽呢?(視頻中講,解釋進程間通信的問題...)
除了os.system可以調用系統命令,,commands,popen2等也可以,比較亂,於是官方推出了subprocess,目地是提供統一的模塊來實現對系統命令或腳本的調用
The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:
- os.system
- os.spawn*
The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.
The run() function was added in Python 3.5; if you need to retain compatibility with older versions, see the Older high-level API section.
三種執行命令的方法
-
subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推薦
-
subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面實現的內容差不多,另一種寫法
-
subprocess.Popen() #上面各種方法的底層封裝
run()方法
Run command with arguments and return a CompletedProcess instance.The returned instance will have attributes args, returncode, stdout and stderr. By default, stdout and stderr are not captured, and those attributes will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
If check is True and the exit code was non-zero, it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute, and output & stderr attributes if those streams were captured.
If timeout is given, and the process takes too long, a TimeoutExpired exception will be raised.
The other arguments are the same as for the Popen constructor.
標準寫法
subprocess.run([‘df‘,‘-h‘],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True
涉及到管道|的命令需要這樣寫
subprocess.run(‘df -h|grep disk1‘,shell=True) #shell=True的意思是這條命令直接交給系統去執行,不需要python負責解析
call()方法
#執行命令,返回命令執行狀態 , 0 or 非0 >>> retcode = subprocess.call(["ls", "-l"]) #執行命令,如果命令結果為0,就正常返回,否則拋異常 >>> subprocess.check_call(["ls", "-l"]) 0 #接收字符串格式命令,返回元組形式,第1個元素是執行狀態,第2個是命令結果 >>> subprocess.getstatusoutput(‘ls /bin/ls‘) (0, ‘/bin/ls‘) #接收字符串格式命令,並返回結果 >>> subprocess.getoutput(‘ls /bin/ls‘) ‘/bin/ls‘ #執行命令,並返回結果,註意是返回結果,不是打印,下例結果返回給res >>> res=subprocess.check_output([‘ls‘,‘-l‘]) >>> res b‘total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n‘
Popen()方法
常用參數:
- args:shell命令,可以是字符串或者序列類型(如:list,元組)
- stdin, stdout, stderr:分別表示程序的標準輸入、輸出、錯誤句柄
- preexec_fn:只在Unix平臺下有效,用於指定一個可執行對象(callable object),它將在子進程運行之前被調用
- shell:同上
- cwd:用於設置子進程的當前目錄
- env:用於指定子進程的環境變量。如果env = None,子進程的環境變量將從父進程中繼承。
下面這2條語句執行會有什麽區別?
a=subprocess.run(‘sleep 10‘,shell=True,stdout=subprocess.PIPE)
a=subprocess.Popen(‘sleep 10‘,shell=True,stdout=subprocess.PIPE)
區別是Popen會在發起命令後立刻返回,而不等命令執行結果。這樣的好處是什麽呢?
如果你調用的命令或腳本 需要執行10分鐘,你的主程序不需卡在這裏等10分鐘,可以繼續往下走,幹別的事情,每過一會,通過一個什麽方法來檢測一下命令是否執行完成就好了。
Popen調用後會返回一個對象,可以通過這個對象拿到命令執行結果或狀態等,該對象有以下方法
poll()
Check if child process has terminated. Returns returncode
wait()
Wait for child process to terminate. Returns returncode attribute.
terminate()
終止所啟動的進程Terminate the process with SIGTERM
kill()
殺死所啟動的進程 Kill the process with SIGKILL
communicate()
與啟動的進程交互,發送數據到stdin,並從stdout接收輸出,然後等待任務結束
>>> a = subprocess.Popen(‘python3 guess_age.py‘,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True) >>> a.communicate(b‘22‘) (b‘your guess:try bigger\n‘, b‘‘)
send_signal(signal.xxx)
發送系統信號
pid
拿到所啟動進程的進程號
第四章——續2