今天因為看一個爬蟲的例子,看到數(shù)據(jù)抓取的時候別人用的正則表達式去匹配想要的數(shù)據(jù).當即對這個表達式感興趣起來,仔細閱讀了一下相關文檔,對其有了大概的認識,索性寫了一篇文章來介紹python中相關正則表達式的用法,以便自己日后參閱!
相關介紹
正則表達式是一種高度精度化的語言,我們可以用一段特定字符串生成的正則來過濾,替換,查找我們需要的數(shù)據(jù).正則表達式在運行的的時候會被編譯成一系列的碼,并由C編寫的匹配引擎執(zhí)行,一般來說效率比一般的算法效率更高,但是卻更不易于理解.大家也可以參考官方文檔.
相關的庫
Python的正則相關庫用的最多的是RE模塊了,因為是python的內(nèi)置模塊,我們只需要直接導入re模塊即可使用其功能.
re模塊常用函數(shù)用法
下面要介紹的相關函數(shù)非常重要,我們需要詳細講解一下!
1.compile 函數(shù)
re.compile(pattern, flags=0)
compile函數(shù)類似一個正則工廠,他返回的是一個正則表達式的模式對象,我們可以用這個對象去加工任何需要加工的字符串.第一個參數(shù)傳入的是正則的表達式字符串,第二個參數(shù)傳遞的是匹配的模式.
2.match 函數(shù)
pattern=re.compile('正則字符串')
pattern.match('要匹配的字符串')
match函數(shù)是一個匹配函數(shù),我們用compile函數(shù)生成的工廠對象,對要加工的字符串進行匹配,如果有響應的字符串,就會返回,否者返回None.需要注意的是,match函數(shù)匹配字符串是從字符串開頭匹配的,假如開頭沒有找到,就不會繼續(xù)找下去!而且他只會返回最先匹配到的字符串,也就是說,假如可能一個字符串中有兩個可以匹配的地方,他只會返回最先匹配到的那部分!
舉個例子:
pattm=re.compile('a') #a為正則字符串
pstr=pattm.match('aabcad') #abcd是要匹配的字符串
if pstr!=None:
print(pstr.group())
else:
print('無匹配!')
>>> a #運行結(jié)果為a ,注意這個a是第一個a!
由于'adcd'字符串中第一位有我們需要匹配的正則字符a,所以成功匹配到了a!
pattm=re.compile('b') #a為正則字符串
pstr=pattm.match('abcd') #abcd是要匹配的字符串
if pstr!=None:
print(pstr.group()) #這里group函數(shù)下面會說
else:
print('無匹配!')
>>> 無匹配! #運行沒有匹配到
上面我們改了一下正則規(guī)則為b,就是說匹配的字符串第一個字符必須為b,否者匹配不成功!
3.search 函數(shù)
pattern=re.compile('正則字符串')
pattern.search('要匹配的字符串')
search 函數(shù)和match函數(shù)有個相同點就是,他只會返回最先匹配到那部分字符串!但是他和match不用的是,他不會只局限于在字符串的開頭查找,他會查找整個字符串,直到找到匹配的部分為止!
pattm=re.compile('a')
pstr=pattm.search('baabcad')
if pstr!=None:
print(pstr.group())
else:
print('無匹配!')
>>>> a #結(jié)果為a,這里的a是字符串出現(xiàn)的第一個a
假如字符串中沒有匹配的字符,返回None
pattm=re.compile('f')
pstr=pattm.search('baabcad')
if pstr!=None:
print(pstr.group())
else:
print('無匹配!')
>>> 無匹配! #字符串中沒有匹配到f字符!
3.findAll 函數(shù)
findAll函數(shù)不但會查找整個字符串,并且會將匹配到的所有字符返回!而不僅限于返回第一個匹配到的!他會將返回的所有字符裝載到一個列表對象返回.
pattm=re.compile('a')
pstr=pattm.findall('baabcad')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>>> ['a', 'a', 'a'] #返回了所有匹配到的a
同樣,假如沒有匹配到,會返回一個空列表對象
4.split函數(shù)
分割函數(shù),類似于字符串分割.會根據(jù)正則字符為基準分割我們需要的字符串.
pattm=re.compile(':')
pstr=pattm.split('baa:bcad')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['baa', 'bcad'] #根據(jù):分割成了兩個字符串
同樣,假如沒有匹配到,會返回一個空列表對象
正則中常用的特殊序列
符號 | 描述 |
---|---|
\d | 匹配任何十進制數(shù)字 |
\D | 匹配任何非數(shù)字字符 |
\s | 匹配任何空白字符(也就是空格) |
\S | 匹配任何非空格字符 |
\w | 匹配任何字母數(shù)字字符 |
\W | 匹配任何非字母數(shù)字字符 |
以上是6個常用的特殊序列,基本涵蓋了我們所有字符串的過濾需求,序列間可以混用.
舉個簡單例子:
pattm=re.compile('\d') #找出所有的十進制數(shù)字
pstr=pattm.findall('abcd1234')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>>> ['1', '2', '3', '4'] #返回一個數(shù)組
混用例子:
pattm=re.compile('\D\d') #找出第一位是字母,第二位是數(shù)字的組合
pstr=pattm.findall('abcd1234')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['d1'] #成功匹配到d1
正則中常用的元字符
元字符在正則表達式中是最常用也是最不容易理解的字符,下面我們會來一一介紹幾種最常用的元字符!
常用的元字符有以下幾種:
.元字符:
. ^ $ * + ? { } [ ] \ | ( )
我們來一一舉例子講解:
pattm=re.compile('.',re.S) #re.s代表匹配的模式,我們這里選擇了所有的匹配模式,所有換行符也會被匹配
pstr=pattm.findall('abcd1234\n')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['a', 'b', 'c', 'd', '1', '2', '3', '4', '\n']
. 代表一個任意的字符,這個字符默認包含所有的不包括換行符在內(nèi)的所有字符,但通過改變匹配的模式,我們也可以做到匹配換行符,所有結(jié)果中返回了所有的匹配的結(jié)果!
^ 元字符符:
pattm=re.compile('^abc',re.S) #匹配abc為開頭的字符
pstr=pattm.findall('abcd1234\n')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['abc']
^ 號表示匹配開頭,上面例子我們相當于匹配字符串a(chǎn)bc,切記這里不是匹配到整個abc開頭的字符串哦!
$ 元字符
pattm=re.compile('abc$',re.S)
pstr=pattm.findall('abcd1234abc')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['abc']
$ 表示匹配結(jié)尾,上面例子就是相當于匹配abc,注意不是得到整個以abc結(jié)尾的字符串!
* 元字符
pattm=re.compile('a*',re.S) #找出所有0個a乃至n個a的匹配
pstr=pattm.findall('bcaacaaab')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['', '', 'aa', '', 'aaa', '', '']
*代表倍數(shù),只對該符號前一個字符有效,可以是0倍也可以是任意倍數(shù),所以上面結(jié)果中,返回了空字符,因為0倍就是空字符!!
+ 元字符
pattm=re.compile('a+',re.S)
pstr=pattm.findall('bcaacaaab')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['aa', 'aaa']
元字符+和元字符*有點類似,只對該符號前一個字符有效,也是指重復,但是+不能匹配0倍,這是根本區(qū)別!
? 元字符
pattm=re.compile('ca?t',re.S)
pstr=pattm.findall('catdddct')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['cat', 'ct']
元字符?號也是重復類字符,但他表示可選,上面例子中a字符表示可選字符,他可匹配也可以不匹配,所以返回了兩種匹配結(jié)果!
{ } 元字符
元字符{} 也是重復字符中的一員,只對該符號前一個字符有效,他比+和*更加的靈活
pattm=re.compile('a{1,2}',re.S) #最少匹配1個a,最多匹配兩個a
pstr=pattm.findall('fcabcdaaaef^')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['a', 'aa', 'a']
{}字符中可以有兩個變量{m,n}, m表示最少的匹配倍數(shù),n表示最大的匹配倍數(shù).也可以只寫一個變量{n},表示最大匹配n倍字符!
[] 元字符
pattm=re.compile('[abc]',re.S)
pstr=pattm.findall('abcdef')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['a', 'b', 'c']
元字符[] 表示一個范圍,相當于指定匹配一個范圍類的字符,上圖中想到與能匹配a,b,c三個字符范圍,也可以寫成[a-b]兩者的效果相同,很多手機號的正則就是利用了該元字符,比如[0-9]取0到9范圍內(nèi)的一個數(shù)字!
還需要特別注意一點的是,在[]類中其他元字符將不會再有原有功能!
舉個例子:
pattm=re.compile('[abc^]',re.S) #按道理,^表示匹配開頭,應該匹配abc開頭的字符
pstr=pattm.findall('fcabcdef^')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['c', 'a', 'b', 'c', '^'] #但結(jié)果卻可以看出,因為[]類的原因,^元字符的作用消失了,被當成了普通的一個字符,返回了所有[]內(nèi)字符范圍的匹配
\ 元字符
\字符是一個比較有意思的字符,他主要有兩種功能
一種是轉(zhuǎn)義:
pattm=re.compile('\{',re.S) #將{轉(zhuǎn)義成普通字符匹配
pstr=pattm.findall('fc{aa{ef[')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['{', '{']
我們可以通過\將其他的元字符當成普通字符來匹配!
第二種是組合序列,通過一些特定的組合,組合成了一些特定功能的序列,比如我們上面提到的特殊序列\(zhòng)s,\w等
| 元字符
pattm=re.compile('a|b',re.S)
pstr=pattm.findall('abcdbcda')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['a', 'b', 'b', 'a']
| 字符和java中的或有點類似,表示匹配前面部分或者后面部分,需要注意的是整個前面和后面部分!假如上面例子是abc|a表達式,表示匹配abc或者b,而不是先匹配ab,然后在c|a中選擇一個,這種理解是錯誤的!
( ) 元字符
()字符代表分組,代表一個整體
pattm=re.compile('(abc)',re.S) #代表匹配abc字符
pstr=pattm.findall('abcccababab')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['abc']
當然()的用法遠遠不止上面這么簡單,我們可以在()中加入任何的一個匹配規(guī)則組成一個組,就可以實現(xiàn)無數(shù)種功能
比如:
pattm=re.compile('(^abc.+)',re.S)
pstr=pattm.findall('abcccababab')
if pstr!=None:
print(pstr)
else:
print('無匹配!')
>>> ['abcccababab']
上面'(^abc.+)' 正則,如果你認真看了上面所有的元字符,應該不難理解,表示匹配abc開頭的任意長度的字符串,所以上面返回了整個字符串.
還有一點需要注意,記得最上面我們使用match和search函數(shù)的時候,往往打印都會使用group函數(shù),入下:
pattm=re.compile('(^abc.+)',re.S)
pstr=pattm.search('abcccababab')
if pstr!=None:
print(pstr.group()) #使用過了group函數(shù)
else:
print('無匹配!')
其實這里的group函數(shù)對應的就是一個正則中的()組,group(1)代表第一個出現(xiàn)的()組的匹配,groupe(1,3)代表出現(xiàn)的第1個和第三個()組的匹配!
Q&A
以上我們就介紹完了,其實正則是一個很龐大的知識,遠遠不止我們文章介紹的那幾種,但是基礎我們一定要知道,這樣在以后遇到一個復雜正則表達式的時候,至少能夠看懂一個大概的樣子.而不是一無所知!