天天看點

(第六集)python包與常用子產品

子產品介紹

一個子產品就是一個包含了一組功能的python檔案,比如spam.py,子產品名為spam,可以通過import spam使用。

子產品分4個通用類别:

  1 使用python編寫的.py檔案

  2 已被編譯為共享庫或DLL的C或C++擴充

  3 把一系列子產品組織到一起的檔案夾(注:檔案夾下有一個init.py檔案,該檔案夾稱之為包)

  4 使用C編寫并連結到python解釋器的内置子產品

子產品的意義

從檔案級别組織程式,更友善管理。将程式分成一個個的檔案,可以把這些檔案當做腳本去執行,還可把他們當子產品來導入到其他的子產品中,實作了功能的重複利用。

提升開發效率。

使用子產品之import

import的使用
同一個子產品import很多次,僅是對已經加載到記憶體中的子產品對象增加一次引用,不會重新執行子產品内的語句,重複導入會直接引用記憶體中已經加載好的結果:
import spam #隻在第一次導入時才執行spam.py内代碼,此處的顯式效果是隻列印一次'from the spam.py',
當然其他的頂級代碼也都被執行了,隻不過沒有顯示效果.
import spam
import spam
import spam
'''
執行結果:
from the spam.py
'''      
導入子產品時執行的内容(以導入spam子產品為例)

1.為源檔案(spam子產品)建立新的名稱空間.

2.在新建立的命名空間中執行子產品中包含的代碼,見初始導入import spam

3.建立名字spam來引用該命名空間.

被導入子產品有獨立的名稱空間

每個子產品都是一個獨立的名稱空間,在編寫自定義的子產品時,無需擔心定義在自己子產品中的全局變量會在被導入時,與使用者的全局變量沖突.

為子產品名起别名(編寫可擴充的代碼)
import spam as      

根據使用者的輸入,選擇不同的sql功能.

#定義子產品mysql.py
def sqlparse():
    print('from mysql sqlparse')
#定義子產品oracle.py
def sqlparse():
    print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as      
在一行導入多個子產品

import sys,os,re

使用子產品之from … import…

from...import...的使用:
from spam import      
from…import 與import的對比

将spam中的名字直接導入到目前的名稱空間中,在目前名稱空間中,直接使用名字,無需加字首:spam.

好處:使用友善

壞處:容易與目前執行檔案中的名字沖突

驗證
  • 驗證一
目前位置直接使用read1和read2執行時,仍以spam.py檔案全局名稱空間:
#測試一:導入的函數read1,執行時仍然回到spam.py中尋找全局變量money
#test.py
from spam import read1
money=1000
read1() # 執行spam子產品中read1函數中的money變量


#測試二:導入的函數read2,執行時需要調用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()  # 執行spam子產品的read2函數      
  • 驗證二:
#測試三:導入的函數read1,被目前位置定義的read1覆寫掉了
#test.py
from spam import read1
def read1():
    print('==========')
read1() #執行本子產品中的read1()函數
'''
執行結果:
from the spam.py      
  • 驗證三:
from spam import money,read1
money=100 #将目前位置的名字money綁定到了100
print(money) #列印目前的名字
read1() #讀取spam.py中的名字money,仍然為1000      
from…import *(不推薦)

所有的不是以下劃線(_)開頭的名字都導入到目前位置,會覆寫掉之前已定義的名字,且可讀性差

子產品的重載

性能原因,每個子產品隻被導入一次,放入字典sys.module中.python不支援重新加載或解除安裝之前導入的子產品.改變了子產品的内容,必須重新開機程式.

# aa.py檔案:
def func1():
    print('func1')      
import time,importlib
import aa
time.sleep(20)
importlib.reload(aa)
aa.func1()
# 在20秒的等待時間裡,修改aa.py中func1的内容,結果不變      

py檔案區分兩種用途:子產品與腳本

#編寫好的一個python檔案可以有兩種用途:      
#python為我們内置了全局變量__name__,
    當檔案被當做腳本執行時:__name__ 等于'__main__'
    當檔案被當做子產品導入時:__name__等于子產品名
#作用:用來控制.py檔案在不同的應用場景下執行不同的邏輯
    if __name__ == '__main__':
        print()      

子產品搜尋路徑

子產品的查找順序是:記憶體中已經加載的子產品->内置子產品->sys.path路徑中包含的子產品.

1、在第一次導入某個子產品時(比如spam),會先檢查該子產品是否已經被加載到記憶體中(目前執行檔案的名稱空間對應的記憶體),如果有則直接引用。
    ps:python解釋器在啟動時會自動加載一些子產品到記憶體中,可以使用sys.modules檢視。
2、如果沒有,解釋器則會查找同名的内模組化塊。
3、如果還沒有找到就從sys.path給出的目錄清單中依次尋找spam.py檔案。      

包介紹

包是一種通過使用‘.子產品名’來組織python子產品名稱空間的方式,包就是一個包含有init.py檔案的檔案夾,是以其實我們建立包的目的就是為了用檔案夾将檔案/子產品組織起來

包本質是檔案夾,将檔案組織起來,提高程式的結構性和可維護性。

包的導入語句
import #一般用于導入内置子產品
from ... import ... #一般用于導入自定義的子產品      

包的使用

import

單獨導入包名稱時不會導入包中所有包含的所有子子產品。

from … import …
from後import導入的子產品,必須是明确的一個不能帶點,否則會有文法錯誤,如:from a import      
from glance.api import *

此處是想從包api中導入所有,實際上該語句隻會導入包api下init.py檔案中定義的名字

1 #在__init__.py中定義
2 x=10
3 
4 def func():
5     print('from api.__init.py')
6 
7 __all__=['x','func','policy']      
絕對導入和相對導入
  • 示範檔案
glance/                   #Top-level package

├── __init__.py      #Initialize the glance package

├── api                  #Subpackage for api

│   ├── __init__.py

│   ├── policy.py

│   └── versions.py

├── cmd                #Subpackage for cmd

│   ├── __init__.py

│   └── manage.py

└── db                  #Subpackage for db

    ├── __init__.py

    └── models.py      

絕對導入:以glance作為起始 。

相對導入:用.或者..的方式最為起始(隻能在同一個包中使用,不能用于不同目錄内)

例:在glance/api/version.py中想要導入glance/cmd/manage.py

#絕對導入
from glance.cmd import manage
manage.main()

#相對導入
from ..cmd import manage
manage.main()

測試結果:注意一定要在于glance同級的檔案中測試      
包的執行範圍

包以及包所包含的子產品都是用來被導入的,而不是被直接執行的。而環境變量都是以執行檔案為準的。

  • 例如:在glance/api/versions.py中導入glance/api/policy.py
#在version.py中

import policy
policy.get()

#單獨運作version.py可以執行,運作version.py的路徑搜尋是從目前路徑開始的,      
# 在glance同級下的一個test.py檔案中導入version.py
from glance.api import versions
'''
執行結果:
ImportError: No module named 'policy'
'''
'''
分析:
此時導入versions在versions.py中執行
import policy需要先從sys.path也就是從目前目錄找policy.py,
這是找不到的
'''      

軟體開發規範

soft
    bin #可執行檔案
        start.py
    conf #配置檔案
        settions.py
    core #程式源檔案
        src.py
    db #資料檔案
        db.json
    lib # 庫檔案
        common.py
    log #日志檔案
        access.lob
    Readme #程式說明檔案      

time與datetime子產品

時間類型
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      
時間類型轉換關系
  • 時間戳與結構化時間的轉換
# 将時間戳轉換為目前時區的struct_time(結構化時間):
time.localtime()
time.localtime(1473525444.037215)

# 将時間戳轉換為UTC時區(0時區)的struct_time:
time.gmtime()

将struct_time轉化為時間戳:
 print(time.mktime(time.localtime()))      
  • 結構化時間與格式化字元串時間的轉換
# 把struct_time格式化時間(如time.localtime()和 time.gmtime())轉化為格式化的時間字元串:
print(time.strftime("%Y-%m-%d %X", time.localtime()))#2016-09-11 00:49:56

# 把格式化時間字元串轉化為struct_time:
print(time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X'))      
  • 時間格式的其它形式
print(time.asctime())  # 把一個表示時間的元組或者struct_time表示為這種形式:Sun Sep 11 00:43:43 2016
print(time.ctime())  # Sun Sep 11 00:46:38 2016
print(time.ctime(time.time()))  # Sun Sep 11 00:46:38 2016      
datetime子產品
import datetime
# print(datetime.datetime.now()) #傳回 2016-08-19 12:47:03.941925
#print(datetime.date.fromtimestamp(time.time()) )  # 時間戳直接轉成日期格式 2016-08-19
# 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()
# print(c_time.replace(minute=3,hour=2)) #時間替換      

random子產品

import random

print(random.random())#(0,1)----float    大于0且小于1之間的小數

print(random.randint(1,3))  #[1,3]    大于等于1且小于等于3之間的整數

print(random.randrange(1,3)) #[1,3)    大于等于1且小于3之間的整數

print(random.choice([1,'23',[4,5]]))#随機選擇1或者23或者[4,5]

print(random.sample([1,'23',[4,5]],2))#對清單元素任意2個組合

print(random.uniform(1,3))#大于1小于3的小數,如1.927109612082716 


item=[1,3,5,7,9]
random.shuffle(item) #打亂item的順序,相當于"洗牌"
print(item)      
  • 應用示例:
生成随機驗證碼:
import random
def make_code(n):
    res = ''
    for i in rang(n):
        s1=chr(random.randint(65,90))   # 生成字母
        s2 = str(random.randint(0,9))   # 生成數字
        res += random.choice([s1, s2])
    return res
print(make_code(9))      

os子產品

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的大小      
系統中的path問題
在Linux和Mac平台上,該函數會原樣傳回path,
在windows平台上會将路徑中所有字元轉換為小寫,并将所有斜杠轉換為反斜杠(\)
>>> os.path.normcase('c:/windows\\system32\\')   
'c:\\windows\\system32\\'      

規範化路徑,如..和/
 os.path.normpath('c://windows\\System32\\../Temp/')   
'c:\\windows\\Temp'   #在windows中的顯示樣式

>>> a='/Users/jieli/test1/\\\a1/\\\\aa.py/../..'
>>> print(os.path.normpath(a))
/Users/jieli/test1      
os路徑處理
方式一:推薦使用
import os,sys
possible_topdir = os.path.normpath(os.path.join(
    os.path.abspath(__file__), #檔案絕對路徑
    os.pardir, #上一級
    os.pardir,
    os.pardir
))
sys.path.insert(0,possible_topdir) #添加到sys子產品搜尋路徑清單中的第一位中      
方式二:不推薦使用
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))      

sys子產品

.argv           指令行參數List,第一個元素是程式本身路徑
 sys.exit(n)        退出程式,正常退出時exit(0)
 sys.version        擷取Python解釋程式的版本資訊
 sys.maxint         最大的Int值
 sys.path           傳回子產品的搜尋路徑,初始化時使用PYTHONPATH環境變量的值
 sys.platform      
實作列印進度條函數
import sys
import time

def progress(percent,width=50):
    if percent >= 1:
        percent=1
    show_str=('[%%-%ds]' %width) %(int(width*percent)*'#')
    print('\r%s %d%%' %(show_str,int(100*percent)),file=sys.stdout,flush=True,end='')

data_size=1025
recv_size=0
while recv_size < data_size:
    time.sleep(0.1) #模拟資料的傳輸延遲
    recv_size+=1024 #每次收1024

    percent=recv_size/data_size #接收的比例
    progress(percent,width=70) #進度條的寬度70      

shutil檔案處理子產品

進階的 檔案、檔案夾、壓縮包 處理子產品..

将檔案内容拷貝到另一個檔案中.
import shutil
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))      
拷貝檔案
shutil.copyfile('f1.log', 'f2.log') #目标檔案無需存在,會自己建立新檔案      
僅拷貝權限,内容、組、使用者均不變
shutil.copymode('f1.log', 'f2.log') #目标檔案必須存在      
僅拷貝狀态的資訊,包括:mode bits, atime, mtime, flags
shutil.copystat('f1.log', 'f2.log') #目标檔案必須存在      
拷貝檔案和權限
shutil.copy('f1.log', 'f2.log')      
遞歸的去拷貝檔案夾
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
 #目标目錄不能存在,注意對folder2目錄父級目錄要有可寫權限,ignore的意思是排除       
#拷貝軟連結
shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# 通常的拷貝都把軟連接配接拷貝成硬連結,即對待軟連接配接來說,建立新的檔案      
遞歸删除檔案
shutil.rmtree('folder1')      
遞歸去移動檔案,類似mv指令(重命名)
shutil.move('folder1', 'folder3')      
打封包件
import shutil
#将 /data 下的檔案打包放置目前程式目錄
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')

#将 /data下的檔案打包放置 /tmp/目錄
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')      
shutil.make_archive(base_name, format,...)
base_name: 壓縮包的檔案名,也可以是壓縮包的路徑。隻是檔案名時,則儲存至目前目錄,否則儲存至指定路徑,
如 data_bak  =>儲存至目前路徑
如:/tmp/data_bak =>儲存至/tmp/
format: 壓縮包種類,“zip”, “tar”, “bztar”,“gztar”
root_dir:   要壓縮的檔案夾路徑(預設目前目錄)
owner:  使用者,預設目前使用者
group:  組,預設目前組
logger: 用于記錄日志,通常是logging.Logger對象      
對壓縮包的處理(調用 ZipFile 和 TarFile 兩個子產品完成)
import zipfile
# 壓縮
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')    #需壓縮的檔案
z.write('data.data')
z.close()

# 解壓
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.') #提取
z.close()      
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子產品

eval(eval(x)) #報錯,無法解析null類型,而json就可以
 print(json.loads(x))       
序列化(pickling)

把對象(變量)從記憶體中變成可存儲或傳輸的過程稱之為序列化。

序列化意義:
持久儲存狀态:記憶體無法永久儲存資料,在斷電/重新開機程式之前将記憶體中資料都儲存下來(儲存到檔案中),便于下次載入之前的資料,然後繼續執行,這是序列化。

跨平台資料互動:将序列化後的資料通過網絡傳輸到别的機器上,實作了跨平台資料互動。反過來,把變量内容從序列化的對象重新讀到記憶體裡稱之為反序列化,即unpickling      

json

序列化格式的一種,不僅是标準格式,且比XML更快,

可以直接在Web頁面中讀取,非常友善。

# 轉換過程:
記憶體中結構化資料---格式JSON---字元串---儲存檔案中/基于網絡傳輸

# 序列化:
dic={'name':'alvin','age':23,'sex':'male'} #序列化可以單引号,但是反序列化要雙引号,json.dumps(dic)

反序列化:(反序列化要雙引号)
f=open('序列化對象')
data=json.loads(f.read())   #  等價于data=json.load(f)

# 注意點:
import json
dct="{'1':111}"           #json 不認單引号
dct=str({"1":111})       #報錯,因為生成的資料還是單引号:{'one': 1}

dct='{"1":"111"}'  #反序列化時,需是雙引号才能轉換成功
print(json.loads(dct))
#  無論資料是怎樣建立的,隻要滿足json格式,就可以json.loads出來,不一定非要dumps的資料才能loads      
pickle(轉換成位元組類型)
# 轉換過程:
内在中結構化資料---格式pickle---bytes類型---儲存到檔案中/基于網絡傳輸

import pickle 
dic={'name':'alvin','age':23,'sex':'male'}  # 字典格式資料
j=pickle.dumps(dic)  #序列化成位元組格式資料
print(type(j))           #<class 'bytes'>

f=open('序列化對象_pickle','wb')    #注意是w是寫入str,wb是寫入bytes,j是'bytes'
f.write(j)                                         #等價于pickle.dump(dic,f) 
f.close()

#-------------------------反序列化
f=open('序列化對象_pickle','rb') 
data=pickle.loads(f.read())            #  等價于data=pickle.load(f) 
print(data['age'])      

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一樣,傳統公司如金融行業的很多系統的接口還主要是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>      
python操作常用子產品
root.iter('year') #全文搜尋
root.find('country') #在root的子節點找,隻找一個
root.findall('country') #在root的子節點找,找所有      
操作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,child.attrib['name'])
    for i in child:
        print(i.tag,i.attrib,i.text)

#隻周遊year 節點
for node in root.iter('year'):
    print(node.tag,node.text)
#---------------------------------------

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')
    node.set('version','1.0')
tree.write('test.xml')


#删除node
for country in root.findall('country'):
   rank = int(country.find('rank').text)
   if rank > 50:
     root.remove(country)

tree.write('output.xml')

#在country内添加(append)節點year2
import xml.etree.ElementTree as ET
tree = ET.parse("a.xml")
root=tree.getroot()
for country in root.findall('country'):
    for year in country.findall('year'):
        if int(year.text) > 2000:
            year2=ET.Element('year2')
            year2.text='新年'
            year2.attrib={'update':'yes'}
            country.append(year2) #往country節點下添加子節點

tree.write('a.xml.swap')      
建立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) #列印生成的格式      

hashlib子產品

hash:一種算法 ,3.x裡代替了md5子產品和sha子產品,

主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法。

特點
  • 内容相同則hash運算結果相同,内容稍微改變則hash值則變。
  • 不可逆推。
  • 相同算法:無論校驗多長的資料,得到的哈希值長度固定。
import hashlib 
m=hashlib.md5()      # 建立md5對象,m=hashlib.sha256() 
m.update('hello'.encode('utf8'))   # 加密資料
print(m.hexdigest())  # 取出資料:5d41402abc4b2a76b9719d911017c592

m.update('alvin'.encode('utf8'))

print(m.hexdigest())  #92a7e713c30abbb0319fa07da2a5c4af

m2=hashlib.md5()
m2.update('helloalvin'.encode('utf8'))
print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af

'''
注意:把一段很長的資料update多次,與一次update這段長資料,得到的結果一樣
但是update多次為校驗大檔案提供了可能
'''      
模拟撞庫破解密碼
# 加密算法時候存在缺陷,即:通過撞庫可以反解。
是以,有必要對加密算法中添加自定義key再來做加密。

import hashlib
passwds=[
    'tom3714',
    'tom1313',
    'tom94139413',
    'tom123456',
    '123456tom',
    'a123tom',
    ]
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):  # 庫檔案的md5值與傳入值進行比對,屬于暴力破解
    for k,v in passwd_dic.items():
        if v == cryptograph:
            print('密碼是===>\033[46m%s\033[0m' %k)  # 獲得密碼

cryptograph='aee949757a2e698417463d47acac93df'      
hmac子產品
# 内部對我們建立 key 和 内容 進行進一步的處理然後再加密:
import hmac
 h = hmac.new('alvin'.encode('utf8'))
 h.update('hello'.encode('utf8'))
 print (h.hexdigest())#320df9832eab4c038b6c1d7ed73a5940      
#要想保證hmac最終結果一緻,必須保證:
#1:hmac.new括号内指定的初始key一樣時,無論update多少次,校驗的内容累加到一起是一樣的内容
import hmac

h1=hmac.new(b'egon')  # 初始key
h1.update(b'hello')
h1.update(b'world')
print(h1.hexdigest())   # f1bf38d054691688f89dcd34ac3c27f2

h2=hmac.new(b'egon')   # 初始key
h2.update(b'helloworld')
print(h2.hexdigest())   # f1bf38d054691688f89dcd34ac3c27f2

h3=hmac.new(b'egonhelloworld')
print(h3.hexdigest())   # bcca84edd9eeb86f30539922b28f3981      

logging子產品

日志的級别
CRITICAL = 50 #FATAL = CRITICAL 危險
ERROR = 40 # 錯誤
WARNING = 30 #WARN = WARNING 警告
INFO = 20  #資訊
DEBUG = 10  # 調試級别
NOTSET = 0 #不設定      
預設級别為warning,預設列印到終端
import logging

logging.debug('調試debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('錯誤error')
logging.critical('嚴重critical')

'''
WARNING:root:警告warn
ERROR:root:錯誤error
CRITICAL:root:嚴重critical
'''      
為logging子產品指定全局配置,針對所有logger有效,控制列印到檔案中
# 可向配置方法中傳入的參數類型
可在logging.basicConfig()函數中通過具體參數來更改logging子產品預設行為,可用參數有:
filename:用指定的檔案名建立FiledHandler(後邊會具體講解handler的概念),這樣日志會被存儲在指定的檔案中。
filemode:檔案打開方式,在指定了filename時使用這個參數,預設值為“a”還可指定為“w”。
format:指定handler使用的日志顯示格式。 
datefmt:指定日期時間格式。 
level:設定rootlogger的日志級别 
stream:用指定的stream建立StreamHandler。
可以指定輸出到sys.stderr,sys.stdout或者檔案,預設為sys.stderr。
若同時列出了filename和stream兩個參數,則stream參數會被忽略。      
# 傳入參數的值格式:
%(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:使用者輸出的消息      
  • 應用示例:
#========使用
import logging
logging.basicConfig(filename='access.log',
                    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',
                    level=10)

logging.debug('調試debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('錯誤error')
logging.critical('嚴重critical')

#========結果
access.log内容:
2017-07-28 20:32:17 PM - root - DEBUG -test:  調試debug
2017-07-28 20:32:17 PM - root - INFO -test:  消息info
2017-07-28 20:32:17 PM - root - WARNING -test:  警告warn
2017-07-28 20:32:17 PM - root - ERROR -test:  錯誤error
2017-07-28 20:32:17      
logging子產品的Formatter,Handler,Logger,Filter對象
#logger:産生日志的對象。
#Filter:過濾日志的對象。
#Handler:接收日志然後控制列印到不同的地方,FileHandler用來列印到檔案中,StreamHandler用來列印到終端。
#Formatter對象:可以定制不同的日志格式對象,然後綁定給不同的Handler對象使用,以此來控制不同的Handler的日志格式      
  • 應用示例:
'''
critical=50
error =40
warning =30
info = 20
debug =10
'''
import logging

#1、logger對象:負責産生日志,然後交給Filter過濾,然後交給不同的Handler輸出
logger=logging.getLogger(__file__)

#2、Filter對象:不常用,略

#3、Handler對象:接收logger傳來的日志,然後控制輸出
h1=logging.FileHandler('t1.log') #列印到檔案
h2=logging.FileHandler('t2.log') #列印到檔案
h3=logging.StreamHandler() #列印到終端

#4、Formatter對象:日志格式
formmater1=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

formmater2=logging.Formatter('%(asctime)s :  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

formmater3=logging.Formatter('%(name)s %(message)s',)


#5、為Handler對象綁定格式
h1.setFormatter(formmater1)
h2.setFormatter(formmater2)
h3.setFormatter(formmater3)

#6、将Handler添加給logger并設定日志級别
logger.addHandler(h1)
logger.addHandler(h2)
logger.addHandler(h3)
logger.setLevel(10)

#7、測試
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')      
Logger與Handler的級别
# 驗證
import

form=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

ch=logging.StreamHandler() # 列印到終端 
ch.setFormatter(form)
# ch.setLevel(10)
ch.setLevel(20)

l1=logging.getLogger('root')
# l1.setLevel(20)
l1.setLevel(10)

l1.addHandler(ch)

l1.debug('l1 debug')      
Logger的繼承
import logging

formatter=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

ch=logging.StreamHandler()   #設定為列印到終端
ch.setFormatter(formatter)   #設定日志列印格式


logger1=logging.getLogger('root')  #設定Logger的名字
logger2=logging.getLogger('root.child1')
logger3=logging.getLogger('root.child1.child2')


logger1.addHandler(ch)  #為logger1對象添加Handler來控制輸出
logger2.addHandler(ch)
logger3.addHandler(ch)

logger1.setLevel(10)  # 日志級别
logger2.setLevel(10)
logger3.setLevel(10)

logger1.debug('log1 debug')
logger2.debug('log2 debug')
logger3.debug('log3 debug')
'''
2017-07-28 22:22:05 PM - root - DEBUG -test:  log1 debug
2017-07-28 22:22:05 PM - root.child1 - DEBUG -test:  log2 debug
2017-07-28 22:22:05 PM - root.child1 - DEBUG -test:  log2 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug
'''      
  • 應用
# logging配置

import os
import logging.config
# 定義三種日志輸出格式 開始:
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]' #标準格式,其中name為getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' #簡單格式

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# 定義日志輸出格式 結束:
logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log檔案的目錄

logfile_name = 'all2.log'  # log檔案名

# 如果不存在定義的日志目錄就建立一個
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)
# 組裝合成log檔案的全路徑
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        #列印到終端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 列印到螢幕
            'formatter': 'simple'
        },
        #列印到檔案的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 儲存到檔案
            'formatter': 'standard',
            'filename': logfile_path,  # 日志檔案
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日志檔案的編碼,再也不用擔心中文log亂碼了
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default', 'console'],  # 這裡把上面定義的兩個handler都加上,即log資料既寫入檔案又列印到螢幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
    },
}

def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  # 導入上面定義的logging配置字典
    logger = logging.getLogger(__name__)  # 生成一個log執行個體
    logger.info('It works!')  # 記錄該檔案的運作狀态

if __name__ == '__main__':
    load_my_logging_cfg()      
# logging使用測試:
import time
import logging
import my_logging  # 導入自定義的logging配置

logger = logging.getLogger(__name__)  # 生成logger執行個體
def demo():
    logger.debug("start range... time:{}".format(time.time()))
    logger.info("中文測試開始。。。")
    for i in range(10):
        logger.debug("i:{}".format(i))
        time.sleep(0.2)
    else:
        logger.debug("over range... time:{}".format(time.time()))
    logger.info("中文測試結束。。。")
if __name__ == "__main__":
    my_logging.load_my_logging_cfg()  # 在你程式檔案的入口加載自定義的logging配置      
# 如何拿到logger對象

有了上述方式我們的好處是:所有與logging子產品有關的配置都寫到字典中就可以了,更加清晰,友善管理
需要解決的問題是:
    1、從字典加載配置:logging.config.dictConfig(settings.LOGGING_DIC)
    2、拿到logger對象來産生日志
    logger對象都是配置到字典的loggers 鍵對應的子字典中的。按照我們對logging子產品的了解,要想擷取某個東西都是通過名字,也就是key來擷取的,于是我們要擷取不同的logger對象就是  logger=logging.getLogger('loggers子字典的key名')    
    但問題是:如果我們想要不同logger名的logger對象都共用一段配置,那麼肯定不能在loggers子字典中定義n個key   
 'loggers': {    
        'l1': {
            'handlers': ['default', 'console'],  #
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
        'l2: {
            'handlers': ['default', 'console' ], 
            'level': 'DEBUG',
            'propagate': False,  # 向上(更高level的logger)傳遞
        },
        'l3': {
            'handlers': ['default', 'console'],  #
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
}

#我們的解決方式是,定義一個空的key
    'loggers': {
        '': {
            'handlers': ['default', 'console'], 
            'level': 'DEBUG',
            'propagate': True, 
        },
}
這樣我們再取logger對象時
logging.getLogger(__name__),不同的檔案__name__不同,這保證了列印日志時辨別資訊不同,但是拿着該名字去loggers裡找key名時卻發現找不到,于是預設使用key=''的配置      

re正規表達式

正則就是用來描述一類事物的規則,内嵌在Python中,通過 re 子產品實作,被編譯成一系列的位元組碼,然後由用 C 編寫的比對引擎執行。

常用比對模式(元字元)
\w  比對字母數字及下劃線
\W 比對非字母數字及下劃線
\s   比對任意空白字元,等價于[\t\n\r\f]
\S   比對任意非空字元
\d   比對任意數字,等價于[0-9]
\D   比對任意非數字
\A   比對字元串開始
\Z   比對字元串結束,如存在換行,隻比對到換行前的結束字元串
\z   比對字元串結束
\G   比對最後比對完成的位置
\n   比對一個換行符
\t   比對一個制表符
^   比對字元串的開關
    比對字元串的結尾
.   比對任意字元。除了換行符,當re.DOTALL标記被指定時,則可比對包含換行符的任意字元
[……]   用來表示一組字元,單獨列出:[amk]  比對‘a’,'m'或'k'
[^……]   取反,不在[]中的字元:[^abc]比對除了a,b,c之外的字元
*   比對0個或多個的表達式
+   比對1個或多個的表達式
?   比對0個或1個由前面的正規表達式定義的片段,非貪婪方式
{n}   精确比對n個前面表達式
{n,m}   比對n到m次由前面的正規表達式定義的片段,貪婪方式
a|b   比對a或b
()   比對括号内的表達式,也表示一個組      
import re
# print(re.findall('e','alex make love') )   #['e', 'e', 'e'],傳回所有滿足比對條件的結果,放在清單裡
# print(re.search('e','alex make love').group())
#e,隻到找到第一個比對然後傳回一個包含比對資訊的對象,該對象可以通過調用group()方法得到比對的字元串,如果字元串沒有比對,則傳回None。
# print(re.match('e','alex make love'))    #None,同search,不過在字元串開始處進行比對,完全可以用search+^代替match
#為何同樣的表達式search與findall卻有不同結果:
print(re.search('\(([\+\-\*\/]*\d+\.?\d*)+\)',"1-12*(60+(-40.35/5)-(-4*3))").group()) #(-40.35/5)
print(re.findall('\(([\+\-\*\/]*\d+\.?\d*)+\)',"1-12*(60+(-40.35/5)-(-4*3))")) #['/5', '*3']

#看這個例子:(\d)+相當于(\d)(\d)(\d)(\d)...,是一系列分組
print(re.search('(\d)+','123').group()) # 123   group的作用是将所有組拼接到一起顯示出來,
print(re.findall('(\d)+','123')) # ['3']  findall結果是組内的結果,且是最後一個組的結果
-----------------------------------------------------------------------------------------------------
# 分割
# print(re.split('[ab]','abcd'))     #['', '', 'cd'],先按'a'分割得到''和'bcd',再對''和'bcd'分别按'b'分割
# 替換
# print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,預設替換所有
# print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love 替換1個
# print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love 替換2個
# print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex
# # alex位于第1位,空格符位于第2位,make位于第三位,再一個空字元位于第4位,love位于第5位
# print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),結果帶有總共替換的個數

# 生成正規表達式對象
# obj=re.compile('\d{2}')
# print(obj.search('abc123eeee').group()) #12
# print(obj.findall('abc123eeee')) #['12'],重用了obj      
import  re
#\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')) #[' ', ' ']

#\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']

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

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

#\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', ' ']

#\A與\Z
# print(re.findall('\Ahe','hello egon 123')) #['he'],\A==>^
# print(re.findall('123\Z','hello egon 123')) #['he'],\Z==>$

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

# 重複比對:| . | * | ? | .* | .*? | + | {n,m} |
#.
# 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']同上一條意思一樣

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

#?
# 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']


# .*預設為貪婪比對,比對多個任意字元
# print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b']

#.*?為非貪婪比對:推薦使用,要麼比對一個任意字元,要麼比對多個任意字元
# print(re.findall('a.*?b','a1b22222222b')) #['a1b']

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

#{n,m}  比對n到m次由前面的正規表達式定義的片段,貪婪方式
# print(re.findall('ab{2}','abbb')) #['abb']
# print(re.findall('ab{2,4}','abbb')) #['abb']
# print(re.findall('ab{1,}','abbb')) #'ab{1,}' ===> 'ab+'
# print(re.findall('ab{0,}','abbb')) #'ab{0,}' ===> 'ab*'

#[] 用來表示一組字元,單獨列出:[amk]  比對‘a’,'m'或'k'
# print(re.findall('a[1*-]b','a1b a*b a-b')) #[]内的都為普通字元了,且如果-沒有被轉意的話,應該放到[]的開頭或結尾
# print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) #[]内的^代表的意思是取反,是以結果為['a=b']
# print(re.findall('a[0-9]b','a1b a*b a-b a=b')) #[]内代表數字取值範圍,是以結果為['a1b']
# print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb')) #[]内代表小定字母取值範圍,是以結果為['aeb']
# print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) #[]内代表小寫大寫的取值範圍,是以結果為['a=b']

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

#():分組 比對成功後,隻保留括号裡的結果,括号以外的内容不保留
# print(re.findall('ab+','ababab123')) #['ab', 'ab', 'ab']
# print(re.findall('(ab)+123','ababab123')) #['ab'],比對到末尾的ab123中的ab
# print(re.findall('(?:ab)+123','ababab123')) # ['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"']      

補充

import re
print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #{'tag_name': 'h1'} 獲得字典

print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())   # <h1>hello</h1>
print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())      # <h1>hello</h1>      
# 使用|(或),先比對的先生效,|左邊是比對小數,而findall最終結果是檢視分組,所有即使比對成功小數也不會存入結果
#而不是小數時,就去比對(-?\d+),比對到的自然就是非小數的數,在此處即整數
print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整數['1', '-2', '60', '', '5', '-4', '3']      

比對形式(貪婪……)

import re

s='''
http://www.baidu.com
[email protected]
你好
010-3141
'''
#正常比對
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('Hello\s\d\d\d\s\d{3}\s\w{10}.*Demo',content)
# print(res)
# print(res.group())
# print(res.span())

#泛比對
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello.*Demo',content)
# print(res.group())

#比對目标,獲得指定資料
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)
# print(res.group()) #取所有比對的内容
# print(res.group(1)) #取比對的第一個括号内的内容
# print(res.group(2)) #去陪陪的第二個括号内的内容

#貪婪比對:  .* 代表比對0到無窮個任意字元
# content='Hello 123 456 World_This is a Regex Demo'#
# res=re.match('^He.*(\d+).*Demo$',content)
# print(res)
# print(res.group(1)) #隻列印所有數字中的6,因為.*會盡可能多的比對,然後後面跟至少一個數字,其餘數字被其它比對了
#  print(res.group()) # Hello 123 456 World_This is a Regex Demo

#非貪婪比對:?比對盡可能少的字元
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^He.*?(\d+).*Demo$',content)
# print(res.group(1)) #列印123

#比對模式: .不能比對換行符
# content='''Hello 123456 World_This
# is a Regex Demo
# '''
# res=re.match('He.*?(\d+).*?Demo$',content)
# print(res) #輸出None
#
# res=re.match('He.*?(\d+).*?Demo$',content,re.S) #re.S讓.可以比對換行符
# print(res)
# print(res.group(1)) # 123456

#轉義:\
# content='price is $5.00'
# res=re.match('price is $5.00',content)
# print(res)
# res=re.match('price is \$5\.00',content)
# print(res.group())

#總結:盡量精簡,詳細的如下
    # 盡量使用泛比對模式.*
    # 盡量使用非貪婪模式: .*?
    # 使用括号得到比對目标:用group(n)去取得結果
    # 有換行符就用re.S:修改模式

#re.search:會掃描整個字元串,不會從頭開始,找到第一個比對的結果就會傳回
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
# res=re.search('Hello.*?(\d+).*?Demo',content) #
# print(res.group(1)) # 輸出結果為123

# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
# res=re.match('Hello.*?(\d+).*?Demo',content)
# print(res) #輸出結果為Noneb      

比對演練

import
content='''
<tbody>
<tr id="4766303201494371851675" class="even "><td><div class="hd"><span class="num">1</span><div class="rk "><span class="u-icn u-icn-75"></span></div></div></td><td class="rank"><div class="f-cb"><div class="tt"><a href="/song?id=476630320"><img class="rpic" src="http://p1.music.126.net/Wl7T1LBRhZFg0O26nnR2iQ==/19217264230385030.jpg?param=50y50&quality=100"></a><span data-res-id="476630320" "
# res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# print(res.group(1))


#re.findall:找到符合條件的所有結果
# res=re.findall('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# for i in res:
#     print(i)



#re.sub:字元串替換
import
content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

# content=re.sub('\d+','',content)
# print(content)


#用\1取得第一個括号的内容
#用法:将123與456換位置
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# # content=re.sub('(Extra.*?)(\d+)(\s)(\d+)(.*?strings)',r'\1\4\3\2\5',content)
# content=re.sub('(\d+)(\s)(\d+)',r'\3\2\1',content)
# print(content)




# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.search('Extra.*?(\d+).*strings',content)
# print(res.group(1))


# import requests,re
# respone=requests.get('https://book.douban.com/').text

# print(respone)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# res=re.findall('<li.*?cover.*?href="(.*?)".*?title="(.*?)">.*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span.*?</li>',respone,re.S)
# # res=re.findall('<li.*?cover.*?href="(.*?)".*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span>.*?</li>',respone,re.S)
#
#
# for i in res:
#     print('%s    %s    %s   %s' %(i[0].strip(),i[1].strip(),i[2].strip(),i[3].strip()))