1 模塊 re模塊經典案例-發紅包
1.1 什么是模塊?
簡言之,模塊就是一組功能的集合
大家之前在編寫簡單的功能實現時,思路是先將程序中都需要有哪些功能定義出來,然后在需要用的地方調用即可。比起之前通篇壘代碼的方式,將重復要用的功能定義成函數會讓程序更加簡潔,這不能不算做是一種進步,但問題是,隨着程序功能越來越多,再將所有的代碼都放到一起,程序的組織結構仍然會不清晰,不方便管理,以后我們寫程序,都是分文件的,如果多個文件中都需要用到同一段功能,難道我們要重復編寫該功能嗎?很明顯不能。這就需要我們找到一種解決方案,能夠將程序中經常要用的功能集合到一起,然后在想用的地方隨時導入使用,這幾乎就是模塊的全部含義了
1.2 模塊類型
內置模塊 |
不需要自己安裝的,python自己自帶的 |
第三方模塊 |
需要自己安裝的 |
自定義模塊 |
自己寫的 |
1 使用python編寫的.py文件
2 已被編譯為共享庫或DLL的C或C++擴展
3 把一系列模塊組織到一起的文件夾(注:文件夾下有一個__init__.py文件,該文件夾稱之為包)
4 使用C編寫並鏈接到python解釋器的內置模塊
1.3 如何使用模塊
1.3.1 Import 的使用
import語句是可以在程序中的任意位置使用的,且針對同一個模塊很import多次,為了防止你重復導入,python的優化手段是:第一次導入后就將模塊名加載到內存了,后續的import語句僅是對已經加載到內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句
1.3.2 在第一次導入模塊時會做三件事,重復導入會直接引用內存中已經加載好的結果
為源文件(XX模塊)創建新的名稱空間
在新創建的命名空間中執行模塊中包含的代碼
創建名字XX來引用該命名空間
1.3.3 被導入模塊有獨立的名稱空間
每個模塊都是一個獨立的名稱空間,定義在在這個模塊中的函數,把這個模塊的名稱空間當作全局名稱空間,這與使用者自己的全局變量不會有沖突。
1.3.4 為模塊名起別名
為已經導入的模塊起別名的方式對編寫可擴展的代碼很有用
1 import spam as sm
2 print(sm.money)
1.3.5 在一行導入多個模塊
1 import sys,os,re
1.3.6 from ...... import..
from spam import read1,read2
(1)from xxx模塊 import 方法1(函數),方法2
(2)from xxx模塊 import * (把xx中所有的不是以下划線(_)開頭的名字都導入到當前位置)
--->可以使用__all__來控制*(用來發布新版本),在spam.py中新增一行
__all__=['money','read1'] #這樣在另外一個文件中用from spam import *就這能導入列表中規定的兩個名字
1.3.6.1 from ...... import..與import的不同
from … import…就是把模塊中的名字直接導入到當前的名稱中,所以在當前名稱空間中,直接使用名字就可以了,無需加模塊前綴。
好處:使用起來方便了
壞處:容易與當前執行文件中的名字沖突
1.3.6.2 也支持as
1 from my_module import read1 as read
也支持導入多行
1 from my_module import (read1,
2 read2,
3 money)
1.3.7 模塊的循環引用問題
思考:假如有兩個模塊a,b。我可不可以在a模塊中import b ,再在b模塊中import a?
模塊循環/嵌套導入拋出異常的根本原因是由於在python中模塊被導入一次之后,就不會重新導入,只會在第一次導入時執行模塊內代碼
在我們的項目中應該盡量避免出現循環/嵌套導入,如果出現多個模塊都需要共享的數據,可以將共享的數據集中存放到某一個地方
1.4 模塊的加載和修改
考慮到性能的原因,每個模塊只被導入一次,放入字典sys.modules中,如果你改變了模塊的內容,你必須重啟程序,python不支持重新加載或卸載之前導入的模塊,
有的同學可能會想到直接從sys.modules中刪除一個模塊不就可以卸載了嗎,注意了,你刪了sys.modules中的模塊對象仍然可能被其他程序的組件所引用,因而不會被清除。
特別的對於我們引用了這個模塊中的一個類,用這個類產生了很多對象,因而這些對象都有關於這個模塊的引用。
2 模塊re
使用python的re模塊,盡管不能滿足所有復雜的匹配情況,但足夠在絕大多數情況下能夠有效地實現對復雜字符串的分析並提取出相關信息。python 會將正則表達式轉化為字節碼,利用 C 語言的匹配引擎進行深度優先的匹配
2.1 查找
2.1.1 findall()
2.1.1.1 語法:
re.findall(pattern, string[, flags])
以list的形式返回string中所有與pattern匹配的不重疊的字符串。String從左向右掃描,匹配的返回結果也是以這個順序。
--------》re.findall(正則表達式,字符串,flags)
2.1.1.2 實例:
1 ret=re.findall('\d+','sjkhk172按實際花費928') 2 print(ret)
--》結果:['172', '928']
2.1.1.3 作用:
匹配所有,每一項都是列表中的一個元素
2.1.2 Search()
2.1.2.1 語法:
re.search(string[, pos[, endpos]])
掃描字符串string,查找與正則表達式匹配的位置。如果找到一個匹配就返回一個MatchObject對象(並不會匹配所有的)。如果沒有找到那么返回None。
第二個參數表示從字符串的那個位置開始,默認是0
第三個參數endpos限定字符串最遠被查找到哪里。默認值就是字符串的長度。.
--------》re.findall(正則表達式,字符串,flags)
2.1.2.2 實例:
1 ret=re.search('\d+','sjkhk172按實際花費928') 2 print(ret) #<_sre.SRE_Match object; span=(5, 8), match='172'> 3 print(ret.group()) #172
---->通過group()獲取結果
2.1.2.3 正確的寫法
1 ret=re.search('\d+','sjkhk172按實際花費928') 2 print(ret) #<_sre.SRE_Match object; span=(5, 8), match='172'> 3 if ret: 4 print(ret.group()) #172
2.1.2.4 作用:
只匹配從左到右的第一個,得到的不是直接的結果,而是一個變量,通過這個變量的group方法來獲取結果
如果沒有匹配到,會返回None,使用group會報錯
2.1.3 match()
2.1.3.1 語法:
re.match(pattern, string, flags=0)
2.1.3.2 實例:
1 ret=re.match('\d+','sjkhk172按實際花費928') 2 print(ret) #None 3 ret=re.search('^\d+','sjkhk172按實際花費928') 4 print(ret) #None
----->功能相同
2.1.3.3 作用:
match 從頭開始匹配,相當於search中的正則表達式加上一個^
2.2 字符串的處理
2.2.1 切割split()
2.2.1.1 語法:
split(pattern, string, maxsplit=0, flags=0)
(正則表達式,字符串)
2.2.1.2 案例:
1 s='alex83taibei40egon25' 2 ret = re.split('\d+',s) 3 print(ret) #['alex', 'taibei', 'egon', ''] 4 ret = re.split('(\d+)',s) 5 print(ret) #['alex', '83', 'taibei', '40', 'egon', '25', '']
------》注意:加括號數字可以保留
2.2.2 替換
2.2.2.1 Sub()
2.2.2.1.1 語法:
sub(pattern, repl, string, count=0, flags=0)
(正則表達式,repl,舊字符串,替換次數)
2.2.2.1.2 案例:
1 ret=re.sub('\d+','H','alex83taibei40egon25') 2 print(ret) #alexHtaibeiHegonH 3 ret=re.sub('\d+','H','alex83taibei40egon25',1) 4 print(ret) # alexHtaibei40egon25
2.2.2.2 Subn()
返回一個元組,第二個元素是替換的次數
2.2.2.2.1 語法:
subn(pattern, repl, string, count=0, flags=0)
2.2.2.2.2 案例:
1 ret=re.subn('\d+','H','alex83taibei40egon25') 2 print(ret) #('alexHtaibeiHegonH', 3) 3 4
2.3 優化處理
2.3.1 節省時間compile()
2.3.1.1 語法:
compile(pattern, flags=0)
2.3.1.2 案例:
1 ret = re.compile('\d+') #編譯 2 print(ret) #re.compile('\\d+') 3 res = ret.findall('alex83taibei40egon25') 4 print(res) #['83', '40', '25'] 5 res= ret.search('alex83taibei40egon25') 6 if res: 7 print(res.group()) #83
2.3.1.3 作用:
節省使用正則表達式解決問題的時間
-----》編譯:把正則表達式編譯成字節碼,在多次使用的過程中不會多次編譯
2.3.2 節省空間/內存finditer()
2.3.2.1 語法:
finditer(pattern, string, flags=0)
(正則表達式,字符串)
finditer 返回一個迭代器,所有匹配到的內容需要迭代取到,迭代取到的每一個結果都需要group取具體值
# -- 節省內存空間
2.3.2.2 案例:
ret = re.finditer('\d+','alex83taibei40egon25') print(ret) #<callable_iterator object at 0x000001C80A1DDC88> for i in ret: print(i) #<_sre.SRE_Match object; span=(4, 6), match='83'> print(i.group())
2.3.2.3 作用:
--->節省使用正則表達式解決問題的空間/內存-->迭代器節省內存
3 re模塊中使用正則表達式的特點和問題
3.1 分組()在re模塊中的使用
分組:()
1.給不止一個字符的整體做量詞約束的時候 www(\.[\w]+)+ www.baidu.com
2.優先顯示,當要匹配的內容和不想匹配的內容混在一起的時候,
就匹配出所有內容,但是對實際需要的內容進行分組
3.分組和re模塊中的方法 :
findall : 分組優先顯示 取消(?:正則)
search :
可以通過.group(index)來取分組中的內容
可以通過.group(name)來取分組中的內容
正則 (?P<name>正則)
使用這個分組 ?P=name
split : 會保留分組內的內容到切割的結果中
3.1.1 分組優先
3.1.1.1 案例:
1 s = '<a>wahaha</b>' 2 ret = re.search('<(\w+)>(\w+)</\w+>',s) 3 if ret: 4 print(ret.group()) #<a>wahaha</b>顯示所有結果 5 print(ret.group(1)) #a 匹配分組中第一部分 6 print(ret.group(2)) #wahaha 7 print(ret.group(3)) #到這會報錯,正則中有兩個分組,只能識別兩個 8 #IndexError: no such group
1 s = '<a>wahaha</b>' 2 ret = re.search('<(\w+)>(\w+)</\w+>',s) 3 ret = re.findall('<(\w+)>(\w+)</\w+>',s) 4 print(ret) #[('a', 'wahaha')]
3.1.1.2 結論:
()分組優先,分組外的不顯示
3.1.1.3 關於分組
對於正則表達式來說 有些時候我們需要進行分組,來整體約束某一組字符出現的次數
-----à (\.[\w]+)?
這里要清楚,對於python語言來說 分組可以幫助你更好更精准的找到你真正需要的內容
3.1.2 取消分組優先(?:正則表達式)
3.1.2.1 案例:
1 ret = re.findall('\d(\.\d+)?','1.234*4') 2 print(ret) #['.234', ''] 3 ret = re.findall('\d(?:\.\d+)?','1.234*4') 4 print(ret) #['1.234', '4']
3.1.3 分組在split中的使用
參考上面split
3.2 分組()命名 :(?P<這個組的名字>正則表達式)
案例1:
1 s = '<a>wahaha</a>' 2 # ret = re.search('>(?P<con>\w+)<',s) 3 # print(ret.group(1)) #wahaha 4 # print(ret.group('con')) #wahaha
案例2:
分組命名的用途:
# 判斷標簽中<>中內容是否一致 #方法一: 利用group s = '<a>wahaha</a>' pattern = '<(\w+)>(\w+)</(\w+)>' ret = re.search(pattern,s) print(ret.group(1) == ret.group(3)) #True # 方法二: 利用分組命名 # 使用前面的分組 要求使用這個名字的分組和前面同名分組中的內容匹配的必須一致 pattern = '<(?P<tab>\w+)>(\w+)</(?P=tab)>' ret = re.search(pattern,s) print(ret) #<_sre.SRE_Match object; span=(0, 13), match='<a>wahaha</a>'>
4 使用表達式的技巧
1 ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))") 2 print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']我們想把小數和整數區分開,這樣不行 3 4 ret=re.findall(r"\d+\.\d+|\d+","1-2*(60+(-40.35/5)-(-4*3))") 5 print(ret) #['1', '2', '60', '40.35', '5', '4', '3'] 6 ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))") 7 print(ret) #['1', '2', '60', '', '5', '4', '3']-->分組優先的原則可以把小數過濾掉 8 ret.remove('') 9 print(ret) #['1', '2', '60', '5', '4', '3']
---》正則表達式如果寫的足夠好的話 能夠最大限度的簡化我們的操作
5 爬蟲的例子
1 from urllib import request 2 ret = request.urlopen('https://movie.douban.com/top250?start=50&filter=') 3 res = ret.read().decode('utf-8') #轉碼 4 print(res)
注意:re.S參數是忽略換行符