正則表達式與Python中re模塊的使用

正則表達式與Python中re模塊的使用

最近作了點爬蟲,正則表達式使用的很是多,用Python作的話會用到re模塊
本文總結一下正則表達式re模塊的基礎與使用。
另外,給你們介紹一個在線測試正則表達式的神器網站http://tool.chinaz.com/regex
你們能夠去這裏練習正則表達式

正則表達式

使用場景

關於正則表達式的基本概念這裏就不贅述了,你們能夠去各類百科裏查找它的定義。正則的使用場景主要分爲兩個:
一是:檢測某一段字符串是否符合規則,也就是咱們常說的"校驗"
二是:從一大段字符串中找到符合規則的字符串,能夠理解爲"檢索"
對於第一種使用場景,咱們在登錄或者註冊時填寫郵箱、手機號等內容的時候常常會見到——底層的實現思路就是利用正則「校驗」咱們輸入的內容是否「規範」。
另外,正則的「檢索」功能大量使用在爬蟲裏,簡單的說,爬蟲能從一個網站大量的數據中獲得用戶想要的內容等等......

須要特別注意,正則表達式只是用來處理字符串的

元字符

一:能夠靈活使用的元字符:[] 與 [^]
(1)數字  [0-9]
(2)小寫字母  [a-z]
(3)大寫字母  [A-Z]
(4)大小寫字母  [A-Za-z]
(5)大小寫字母+數字  [0-9A-Za-z]
(6)注意一個字符組不限制個數:匹配三個的話:[0-9A-Za-z][0-9A-Za-z][0-9A-Za-z]
(7)大小寫字母+ _與%   [A-Za-z_%]
(8-1)匹配1-5   [1\-5]  用轉義符,- 有特殊含義
(8-2)字符組中 - 是有特殊意義的,須要使用\做爲轉義符!
(9)[^123]——除了123都匹配,包括換行
二:匹配字符
(1)\d——全部數字
(2)\w——字母數字下劃線
(3)\s——空白
(4)空格匹配空格
(5)\t——製表符
(6)\n——換行符
(7)\b——單詞的邊界   o\b——hello的'o'(o是單詞的最後一個)    \bo——ok的'o'(o是單詞的第一個)
(8)\W——除了字母數字下劃線
(9)\D——除了數字
(10)\S——除了空白符
(11).——匹配除了換行符全部的
(12)[\D\d]、[\W\w]、[\s\S]——匹配全部的
(13)[^123]——除了123都匹配,包括換行
(14)^——開始字符
(15)$——結束字符
(16)^.....$——開始+結尾,中間是5個除了換行的符號
(17)|——或   長的放在前面!
(18)()——分組   www\.(baidu|oldboy)\.com  匹配www.baidu.com或者www.oldboy.com

量詞

量詞用來限制匹配的次數
(1){n}——匹配n次
(2){n,}——匹配至少n次
(3){n,m}——匹配n到m次
(4)+——一次或者屢次  匹配小數點的先後必須有數      \d+\.\d+
(5)*——0次或者屢次    匹配整數:第一位是1-9不要是0:     [1-9]\d*|0
(6)?——0次或者一次    匹配一個整數或者小數: \d+(\.\d+)?

貪婪匹配與惰性匹配

在正則中,默認的匹配模式是「貪婪匹配」,也就是說,在符合匹配規則的前提下儘量多的去匹配字符,可是有些時候,咱們不須要匹配太多的內容,只要獲得須要的「片斷」內容就行了。
量詞是匹配屢次的,這時咱們能夠在量詞的後面加上?就可讓匹配「適可而止」了,這樣的匹配規則就是惰性匹配
這裏舉一個貪婪匹配惰性匹配對比的例子:
有字符串"aasdasdasdxxxxxxasdasd",如今想匹配到x結束
(1)用"惰性匹配"的話:
    語法:a.*?x
    說明:碰見一個先檢測是否是x,若是是的話就中止,不是的話就匹配
    結果:aasdasdasdx
(2)默認的"貪婪匹配":
    語法:a.*x
    說明:這裏會匹配後面全部的x
    結果:aasdasdasdxxxxxx
固然,何時用貪婪匹配何時用惰性匹配須要咱們具體狀況具體分析。

re模塊

re模塊是Python的內置模塊,是專門用來處理與正則表達式相關需求的模塊。
使用方法:直接在Python程序中import便可:
import re

根據正則的規則從一段內容中查找結果

findall

找到全部符合正則規則的字符串,並以列表的形式返回結果:
ret = re.findall('\d+','whw123w1233')
print(ret)
#結果:
['123', '1233']
找到第一個,返回結果集,須要經過group方法取值。沒取到的話返回None,此時用group方法會報錯!所以須要提早判斷下:
ret = re.search('\d+','wanghw123ww')
print(ret)
if ret:
    print(ret.group())
#結果:
<_sre.SRE_Match object; span=(6, 9), match='123'>
123

match

從頭開始找第一個,返回一個結果集,須要經過group取值。沒取到的話返回None,此時用group方法會報錯!所以須要提早判斷下:
下面是匹配到結果的:
ret = re.match('\d+','23whw22a')
print(ret)
if ret:
    print(ret.group())
#結果:
<_sre.SRE_Match object; span=(0, 2), match='23'>
23
沒有匹配到的話能夠利用異常處理或者像上面同樣的條件判斷:
ret = re.match('\d+','whw22a')
print(ret)
try:
    print(ret.group())
except AttributeError as e:
    print(e,':','沒有匹配到!')
#結果:
None
'NoneType' object has no attribute 'group' : 沒有匹配到!
咱們能夠看到,match的返回結果與search是同樣的;並且,match的匹配語法其實就是至關於search在其匹配規則上加上^,所以:match能夠被search代替,實際中match用的比較少。
re.search('^\d+','123asd') 
等同於:
re.match('\d+','123asd')

替換與切割

sub

咱們能夠利用re模塊與正則表達式結合進行字符串的批量替換:
有以下需求:
字符串:s1 = 'wanghw123whw456',將s1中全部的數字替換成字符串"HERO"。
咱們能夠利用re模塊的sub方法來作:
s1 = 'wanghw123whw456'
ss = re.sub('\d+','HERO',s1)
print(ss)
#結果:
wanghwHEROwhwHERO
sub方法的第一個參數是正則的規則,第二個參數是要被替換成的字符串,第三個參數是須要被操做的字符串。固然,聰明的你確定想到一個問題:這默認替換的是全部符合規則的字符串呀!我若是想限定替換的次數怎麼作呢?咱們能夠指定第四個參數,來達到限定替換次數的做用:
s1 = 'wanghw123whw456'
ss = re.sub('\d+','HERO',s1,1)
print(ss)
#結果:
wanghwHEROwhw456

subn

subn的用法跟sub如出一轍,只不過返回值是有區別的,咱們能夠執行一下看看:
s1 = 'wanghw123whw456'
ss = re.subn('\d+','HERO',s1)
print(ss)
#結果:
('wanghwHEROwhwHERO', 2)
固然也能夠指定替換的次數:
s1 = 'wanghw123whw456'
ss = re.subn('\d+','HERO',s1,1)
print(ss)
#結果:
('wanghwHEROwhw456', 1)
你們能夠看到,subn方法返回一個元組:第一個元素是獲得的結果,第二個元素是替換的次數。

split

split方法與字符串的切割方法同樣,返回的也是一個列表,只不過,此次咱們用的是正則匹配的結果進行切割的!
s1 = 'wanghw123whw456qwe'
sq = re.split('\d+',s1)
print(sq)
#結果:
['wanghw', 'whw', 'qwe']
split的第一個參數放正則規則,第二個參數放被操做的字符串。

預編譯

咱們都知道,Python是一種解釋型的編程語言,Python的代碼須要先在解釋器中轉換爲機器碼,最終轉換爲計算機能識別的編碼才能運行。
對於正則匹配來講,若是一個一樣的正則表達式須要用到幾十次甚至上百次去匹配一段長文字的話,用普通的方法,咱們須要將這個正則表達式運行幾十次甚至上百次來進行每一次的匹配。這樣大大的下降了咱們程序的效率,是咱們最不肯意看到的!
而在Python中爲咱們提供了預編譯的方法去解決這個難題。
所謂的預編譯,在正則中咱們實際用在下面這樣的場景中:對於一個常常被重複使用的正則表達式,咱們能夠先進行一次編譯,將這個正則表達式須要作的事預先編譯,這樣,以後只要用到了這個表達式咱們能夠將編譯好的結果直接拿來用就好了,醬紫大大的省了代碼的執行時間!
預編譯的使用舉例:
好比說,咱們想匹配三段(實際中多是上百個)用到相同規則匹配的字符串中的內容。首先,咱們能夠將這個規則進行預編譯,而後利用這個預編譯的結果去匹配每一段須要匹配的字符串:
ss = 'wangh123qwe3123'
ss1 = 'wangh123qwe3123312'
ss2 = 'wangh123qwe3123qwqasd5412'

par = re.compile('\d+')
print(par)
rets = par.findall(ss)
rets1 = par.findall(ss1)
rets2 = par.findall(ss2)
print(rets)
print(rets1)
print(rets2)
看到這裏,相信聰明的你又發現問題了!這樣當然下降了時間複雜度,可是,若是實際中有幾萬個大字符串,你這樣一會兒讀取出來,內存不是直接爆炸了麼!
對!沒錯,針對下降空間複雜度的問題,Python爲咱們提供了另一個方法————finditer

finditer

須要注意的是,finditer方法獲得的是一個迭代器。下面咱們模擬匹配10個相同字符串的例子:
re=  re.finditer('\d+','23adq9009qwe'*10)
print(re)
for i in re:
    print(i)
    print(i.group())
#結果(只拿一個結果舉例):
#第一個拿到的是re——迭代器對象
<callable_iterator object at 0x000001157E942940>
#遍歷re中拿到的是"結果集"
<_sre.SRE_Match object; span=(0, 2), match='23'>
#最終的結果須要用group()方法取到:
23
下面就來一發compile與finditer結合同時下降時間複雜度與空間複雜度的方法
par = re.compile('\d+')
ret = par.finditer('wanghe123asdh2312asd33'*10)
print(ret)
for i in ret:
    print(i.group())
#結果(只拿前1組):
<callable_iterator object at 0x000001DA20362940>
123
2312
33

「分組」與re模塊的結合使用

在實際中,正則表達式的「分組」思想與re模塊的方法結合使用的狀況很是多

findall與分組

有以下需求:在標籤字符串中提取出標籤中的內容:
通常的方法是這樣的:
s = '<title>whwHERO</title>'
print(ret)
print(ret[0].split('>'))
print(ret[0].split('>')[1].split('<')[0])
#結果:
['>whwHERO<']
['', 'whwHERO<']
whwHERO
這樣的方法看起來比較麻煩,正則匹配到之後咱們還得用Python的方法去處理。
咱們能夠利用正則的分組與re模塊的方法結合區實現:
ret = re.findall('>(\w+)<',r'<title>whwHERO<\\title>')
print(ret)
#結果:
['whwHERO']
咱們能夠看到:findall優先顯示分組中的內容!簡直不要太棒!
這樣的效果在本例中效果很棒,可是,在一下狀況下咱們是不想醬紫的:
若是咱們想匹配www.baidu.com或者www.taobao.com。正則表達式利用分組咱們能夠醬紫寫:www\.(baidu|taobao)\.com
可是,與findall方法結合會出現問題:
ret = re.findall('www\.(baidu|taobao)\.com','www.baidu.com')
print(ret)
#結果:
['baidu']
再好比,咱們想獲取一串字符串中的數字(包括小數):
digit = re.findall('\d+(\.\d+)?',r'1.23+2.33')
print(digit)
#結果:['.23', '.33']
很顯然,醬紫的分組優先是咱們不想要的。那麼如何取消findall下的分組優先呢?
在分組裏的最前面加上?:就能夠了,上面兩個例子咱們能夠醬紫修改:
ret = re.findall('www\.(?:baidu|taobao)\.com','www.baidu.com')
print(ret)
#結果:['www.baidu.com']
digit = re.findall('\d+(?:\.\d+)?','1.23+2.33')
print(digit)
#結果:
['1.23', '2.33']

split與分組

split與分組結合跟普通的split方法的區別是:與分組結合的話會保留在分組中「切掉」的內容,用一個例子來說就很直觀了:
ret = re.split('\d(\d)','wanghw211whw222www233ee')
print(ret)
#結果:
['wanghw', '1', '1whw', '2', '2www', '3', '3ee']
咱們能夠看到:匹配的第二個數字在分組中,咱們利用匹配到的兩個數字進行切割,可是因爲只有第二個數字在分組中,所以第一個數字被「幹掉」了,切割後的列表只保留了第二個數字!

search與分組

在講search與分組的關係時,請你們記住下面作數據處理的思想:
在爬蟲\數據清洗的過程當中最經常使用的正則表達式的操做
而大多數時候咱們並非把咱們要的內容都用正則寫出來
而是把整個頁面都用正則描述下來,而後把我須要的內容放在分組裏
這樣就可以經過分組取到我想要的內容了
(心中默唸一遍後)看下面這個例子:
好比說咱們從網頁中爬取了下面字符串(固然實際中要大得多,這裏只作模擬)
s="<title>whwisahero<\title>"
咱們想用search方法獲取裏面的內容,根據上面介紹的處理數據的思路,咱們先利用search拿到結果集,而後在這個結果集中取數據:
ret = re.search(r'<(\w+)>(\w+)<\\(\w+)>',r'<title>whwisahero<\title>')
print(ret.group())
print(ret.group(0))
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))
#結果:
<title>whwhwhw<\title>
<title>whwhwhw<\title>
title
whwisahero (這是咱們想要的內容)
title
醬紫咱們就拿到告終果。
可是,相信聰明的你叕叕叕叕叕發現問題了!我怎麼知道我想要的內容在哪一個索引裏!沒錯!這裏就須要咱們利用分組命名了!
ret = re.search('<(?P<title>\w+)>(?P<content>\w+)</(?P<title2>\w+)>',r'<title>whwisahero</title>')
print(ret.group('title'))
print(ret.group('title2'))
print(ret.group('content'))
#結果:
title
title
whwisahero
能夠看到,咱們在分組內部的前面利用?P<分組名>的語法爲分組起了個名字,在最後取值的時候將分組名加在group方法的參數裏就OK了!
另外,分組名還能夠這麼玩:
若是兩個分組名同樣能夠醬紫寫:
ret1 = re.search('<(?P<title>\w+)>(?P<content>\w+)</(?P=title)>',r'<title>whwisahero</title>')
print(ret1.group('title'))
print(ret1.group('content'))
#結果:
title
whwisahero
也能夠醬紫寫:
ret2 = re.search(r'<(?P<title>\w+)>(?P<content>\w+)</\1>',r'<title>whwisahero</title>')
print(ret2.group('title'))
print(ret2.group('content'))
#結果:
title
whwisahero

篩選數據的思想

接着跟你們下篩選數據的思路:
當咱們要匹配的內容混在不想匹配的內容中
只能把不想要的也匹配出來,而後去掉不想要的就是想要的
看下面的例子:
我想拿到一串字符串中的正整數,利用上面的思路能夠醬紫作:先將不須要的數據也匹配出來,而後進行進一步的加工。由於實際中正則表達式並非萬能或者一勞永逸的,獲得的數據還須要咱們進行進一步的處理:
ret = re.findall('-*\d+\.\d+|(-*\d+)','2*(60+(-40.35/5)-(-4*3))')
print(ret)#['2', '60', '', '5', '-4', '3']
for i in ret:
    if not i or i.startswith('-'):
        ret.remove(i)
print(ret)#['2', '60', '5', '3']

常見的幾個正則表達式例子:

最後給你們分享常見的幾個正則表達式:
#郵箱的規則:
    (1)@以前必須有內容且只能是字母(大小寫)、數字、下劃線(_)、減號(-)、點(.)
    (2)@和最後一個點(.)之間必須有內容且只能是字母(大小寫)、數字、點(.)、減號(-),且兩個點不能挨着
    (3)最後一個點(.)以後必須有內容且內容只能是字母(大小寫)、數字且長度爲大於等於2個字節,小於等於6個字節
正則表達式:[\w\-\.]+@([a-zA-Z\d\-]+\.)+[a-zA-Z\d]{2,6}
1.整數或者小數 包括正數和負數
-?\d+(\.\d+)?

2.年月日
2018-9-20
\d{1,4}-\d{1,2}-\d{1,2}
\d{1,4}-(1[0-2]|0?[1-9])-(3[01]|[12]\d|0?[1-9])

3.匹配qq號
4位 11位
[1-9]\d{4,11}

4.8-10位的密碼 數字字母下劃線
\w{8,10}

5.驗證碼
 [\da-zA-Z]{4}
固然,這些寫法並非固定的,你們多去上面介紹的網站上多練習,畢竟勤能補拙是良訓,一分辛苦一分才
相關文章
相關標籤/搜索