re模塊(正則)
#re:一些帶有特殊含義的符號或者符號的組合
#為什么要用re:一堆字符串中找到你所需要的內容,過濾規則是什么樣,通過re模塊功能來告訴計算機你的過濾規則
#應用:在爬蟲中最為常用;使用爬蟲時有其他模塊可以導入幫助clear數據,正則也可用於其他方面
#原理:re模塊的內部實現 不是python 而是 調用了c庫
import re
print(re.findall(' \w','ab 12\+- *&_'))
print(re.findall('\W','ab 12\+- *&_'))
print(re.findall(' \s','ab \r1\n2\t\+- *&_'))
print(re.findall('\S','ab \r1\n2\t\+- *&_'))
print(re.findall(' \d','ab \r1\n2\t\+- *&_'))
print(re.findall('\D','ab \r1\n2\t\+- *&_'))
print(re.findall('\w_sb','egon alex_sb123123wxx_sb,lxx_sb'))
print(re.findall(' \Aalex','abcalex is salexb'))
print(re.findall('\Aalex','alex is salexb'))
print(re.findall(' ^alex','alex is salexb'))
print(re.findall('sb \Z','alexsb is sbalexbsb'))
print(re.findall('sb $','alexsb is sbalexbsb'))
print(re.findall('^ebn$','ebn1')) #^ebn$ 篩出的就是ebn(以ebn開頭,以ebn結尾)
print(re.findall('a \nc','a\nc a\tc a1c'))
#re:一些帶有特殊含義的符號或者符號的組合
#為什么要用re:一堆字符串中找到你所需要的內容,過濾規則是什么樣,通過re模塊功能來告訴計算機你的過濾規則
#應用:在爬蟲中最為常用;使用爬蟲時有其他模塊可以導入幫助clear數據,正則也可用於其他方面
#原理:re模塊的內部實現 不是python 而是 調用了c庫
import re
print(re.findall(' \w','ab 12\+- *&_'))
print(re.findall('\W','ab 12\+- *&_'))
print(re.findall(' \s','ab \r1\n2\t\+- *&_'))
print(re.findall('\S','ab \r1\n2\t\+- *&_'))
print(re.findall(' \d','ab \r1\n2\t\+- *&_'))
print(re.findall('\D','ab \r1\n2\t\+- *&_'))
print(re.findall('\w_sb','egon alex_sb123123wxx_sb,lxx_sb'))
print(re.findall(' \Aalex','abcalex is salexb'))
print(re.findall('\Aalex','alex is salexb'))
print(re.findall(' ^alex','alex is salexb'))
print(re.findall('sb \Z','alexsb is sbalexbsb'))
print(re.findall('sb $','alexsb is sbalexbsb'))
print(re.findall('^ebn$','ebn1')) #^ebn$ 篩出的就是ebn(以ebn開頭,以ebn結尾)
print(re.findall('a \nc','a\nc a\tc a1c'))
\t為制表符,在不同平台表示不同的空個數
\A ^ #使用^
\Z $ #使用$
\A ^ #使用^
\Z $ #使用$
# 重復匹配:
#. ? * + {m,n} .* .*? {m}
1、 .:代表 除了換行符外的 任意一個字符
. 除了換行符之外的任意一個字符, 如果想不除換行符,后加re.DOTALL
#. ? * + {m,n} .* .*? {m}
1、 .:代表 除了換行符外的 任意一個字符
. 除了換行符之外的任意一個字符, 如果想不除換行符,后加re.DOTALL
print(re.findall('a.c','abc a1c aAc aaaaaca\nc'))
print(re.findall('a.c','abc a1c aAc aaaaaca\nc' ,re.DOTALL))
print(re.findall('a.c','abc a1c aAc aaaaaca\nc' ,re.DOTALL))
2、
?:代表
左邊那一個字符重復
0次或1次 默認貪婪(能匹配1次絕不匹配0次)
?不能單獨使用
?不能單獨使用
print(re.findall('ab?','a ab abb abbb abbbb abbbb'))
3、
*:代表
左邊那一個字符出現
0次或無窮次 默認貪婪(能匹配無窮次絕不匹0次)
print(re.findall('ab*','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab*','a ab abb abbb abbbb abbbb a1bbbbbbb'))
4、
+ :代表
左邊那一個字符出現
1次或無窮次 默認貪婪(能匹配無窮次絕不匹配1次)
print(re.findall('ab+','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab+','a ab abb abbb abbbb abbbb a1bbbbbbb'))
5、{m,n}:代表
左邊那一個字符出現
m次到n次 默認貪婪(能匹配n次絕不匹配m次)
print(re.findall('ab?','a ab abb abbb abbbb abbbb'))
print(re.findall('ab{0,1}','a ab abb abbb abbbb abbbb'))
print(re.findall('ab*','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{0,}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab+','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{1,}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{1,3}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab?','a ab abb abbb abbbb abbbb'))
print(re.findall('ab{0,1}','a ab abb abbb abbbb abbbb'))
print(re.findall('ab*','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{0,}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab+','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{1,}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
print(re.findall('ab{1,3}','a ab abb abbb abbbb abbbb a1bbbbbbb'))
6、
.*:匹配
任意長度,任意的字符=====》
貪婪
print(re.findall('a.*c','ac a123c aaaac a *123)()c asdfasfdsadf'))
print(re.findall('a.*c','ac a123c aaaac a *123)()c asdfasfdsadf'))
7、
.*?:
多少次到多少次(貪婪)+ ?=非貪婪
print(re.findall('a.*?c','a123c456c'))
print(re.findall('a.*?c','a123c456c'))
():分組
print(re.findall('(alex)_sb','alex_sb asdfsafdafdaalex_sb'))
print(re.findall(
' href="(.*?)"',
'<li><a id="blog_nav_sitehome" class="menu" href="http://www.cnblogs.com/">博客園</a></li>')
)
print(re.findall('(alex)_sb','alex_sb asdfsafdafdaalex_sb'))
print(re.findall(
' href="(.*?)"',
'<li><a id="blog_nav_sitehome" class="menu" href="http://www.cnblogs.com/">博客園</a></li>')
)
[]:匹配一個指定
范圍內的字符(這一個字符來自於括號內定義的)
[] 內寫什么就是其單獨的意義, 可寫0-9 a-zA-Z
print(re.findall('a[0-9][0-9]c','a1c a+c a2c a9c a11c a-c acc aAc'))
[] 內寫什么就是其單獨的意義, 可寫0-9 a-zA-Z
print(re.findall('a[0-9][0-9]c','a1c a+c a2c a9c a11c a-c acc aAc'))
當-需要被當中普通符號匹配時,只能放到[]的最左邊或最 右邊
a-b有特別的意思,所以如果想讓-表示它本身,要將其放在最左或最右
print(re.findall('a[-+*]c','a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('a[a-zA-Z]c','a1c a+c a2c a9c a*c a11c a-c acc aAc'))
a-b有特別的意思,所以如果想讓-表示它本身,要將其放在最左或最右
print(re.findall('a[-+*]c','a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('a[a-zA-Z]c','a1c a+c a2c a9c a*c a11c a-c acc aAc'))
[]內的^代表取反的意思 (^在[]中表示取反)
print(re.findall('a[^a-zA-Z]c','a c a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('a[^0-9]c','a c a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('([a-z]+)_sb','egon alex_sb123123wxxxxxxxxxxxxx_sb,lxx_sb'))
print(re.findall('a[^a-zA-Z]c','a c a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('a[^0-9]c','a c a1c a+c a2c a9c a*c a11c a-c acc aAc'))
print(re.findall('([a-z]+)_sb','egon alex_sb123123wxxxxxxxxxxxxx_sb,lxx_sb'))
| :或者
print(re.findall('compan(ies|y)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('compan(ies|y)','Too many companies have gone bankrupt, and the next one is my company'))
(?: ):代表取匹配成功的所有內容,而不僅僅只是括號內的內容
((?: )表示匹配的結果都要,不單單要()內的)
print(re.findall('compan(?:ies|y)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('alex|sb','alex sb sadfsadfasdfegon alex sb egon'))
print(re.findall('compan(?:ies|y)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('alex|sb','alex sb sadfsadfasdfegon alex sb egon'))
re模塊的其他方法:
print( re.findall('alex|sb','123123 alex sb sadfsadfasdfegon alex sb egon'))
print (re.search('alex|sb','123213 alex sb sadfsadfasdfegon alex sb egon').group())
print(re.search('^alex','123213 alex sb sadfsadfasdfegon alex sb egon'))
print(re.search('^alex','alex sb sadfsadfasdfegon alex sb egon').group())
re.search, 取第一個結果,若沒有返回None;若想讓結果直接顯示后加group();返回None時用group()會報錯
print( re.findall('alex|sb','123123 alex sb sadfsadfasdfegon alex sb egon'))
print (re.search('alex|sb','123213 alex sb sadfsadfasdfegon alex sb egon').group())
print(re.search('^alex','123213 alex sb sadfsadfasdfegon alex sb egon'))
print(re.search('^alex','alex sb sadfsadfasdfegon alex sb egon').group())
re.search, 取第一個結果,若沒有返回None;若想讓結果直接顯示后加group();返回None時用group()會報錯
re.match 相當於^版本的search
print(re.match('alex','alex sb sadfsadfasdfegon alex sb egon').group())
print(re.match('alex','123213 alex sb sadfsadfasdfegon alex sb egon'))
re.match 相當於^版本的search
print(re.match('alex','alex sb sadfsadfasdfegon alex sb egon').group())
print(re.match('alex','123213 alex sb sadfsadfasdfegon alex sb egon'))
re.match 相當於^版本的search
re.split與split相比,內部可以使用正則表達式
info='a:b:c:d'
print(info.split(':'))
print(re.split(':',info))
info='a:b:c:d'
print(info.split(':'))
print(re.split(':',info))
info=r'get :a.txt\3333/rwx'
print(re.split('[ :\\\/]',info))
print(re.split('[ :\\\/]',info))
re.sub 與replace相比,內部可以使用正則表達式
print('egon is beutifull egon'.replace('egon','EGON',1))
print(re.sub('(.*?)(egon)(.*?)(egon)(.*?)',r'\1\2\3EGON\5','123 egon is beutifull egon 123'))
print('egon is beutifull egon'.replace('egon','EGON',1))
print(re.sub('(.*?)(egon)(.*?)(egon)(.*?)',r'\1\2\3EGON\5','123 egon is beutifull egon 123'))
print(re.sub('(lqz)(.*?)(SB)',r'\3\2\1',r'lqz is SB'))
print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)',r'\5\2\3\4\1',r'lqzzzz123+ is SB'))
print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)',r'\5\2\3\4\1',r'lqzzzz123+ is SB'))
太常用的表達式可以做成
pattern= re.compile('alex')
print (pattern.findall('alex is alex alex'))
print(pattern.findall('alexasdfsadfsadfasdfasdfasfd is alex alex'))
pattern= re.compile('alex')
print (pattern.findall('alex is alex alex'))
print(pattern.findall('alexasdfsadfsadfasdfasdfasfd is alex alex'))
斷言
正則表達式中的斷言,作為
高級應用出現,倒不是因為它有多難,而是概念比較抽象,不容易理解而已。
如果不用斷言,以往用過的那些表達式,僅僅能獲取到有規律的字符串,而不能獲取無規律的字符串。
如果不用斷言,以往用過的那些表達式,僅僅能獲取到有規律的字符串,而不能獲取無規律的字符串。
舉個例子,比如html源碼中有<title>xxx</title>標簽,用以前的知識,我們只能確定源碼中的<title>和</title>是固定不變的。因此,如果想獲取頁面標題(xxx),充其量只能寫一個類似於這樣的表達式:<title>.*</title>,而這樣寫匹配出來的是完整的<title>xxx</title>標簽,並不是單純的頁面標題xxx。
想解決以上問題,就要用到斷言知識。
在講斷言之前,讀者應該 先了解分組,這有助於理解斷言。
分組在正則中用()表示,根據小菜理解,分組的作用有兩個:
n 將某些規律看成是一組,然后進行組級別的重復,可以得到意想不到的效果。
n 分組之后,可以通過后向引用簡化表達式。
先來看第一個作用,對於IP地址的匹配,簡單的可以寫為如下形式:
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
但仔細觀察,我們可以發現一定的規律,可以把.\d{1,3}看成一個整體,也就是把他們看成一組,再把這個組重復3次即可。表達式如下:
\d{1,3}(.\d{1,3}){3}
這樣一看,就比較簡潔了。
再來看第二個作用,就拿匹配<title>xxx</title>標簽來說,簡單的正則可以這樣寫:
<title>.*</title>
可以看出,上邊表達式中有兩個title,完全一樣,其實可以通過分組簡寫。表達式如下:
<(title)>.*</\1>
這個例子實際上就是 反向引用的實際應用。 對於分組而言,整個表達式永遠算作第0組,在本例中,第0組是<(title)>.*</\1>,然后從左到右,依次為分組編號,因此,(title)是第1組。
用\1這種語法,可以引用某組的文本內容,\1當然就是引用第1組的文本內容了,這樣一來,就可以簡化正則表達式,只寫一次title,把它放在組里,然后在后邊引用即可。
以此為啟發,我們可不可以簡化剛剛的IP地址正則表達式呢?原來的表達式為\d{1,3}(.\d{1,3}){3},里邊的\d{1,3}重復了兩次,如果利用后向引用簡化,表達式如下:
(\d{1,3})(.\1){3}
簡單的解釋下,把\d{1,3}放在一組里,表示為(\d{1,3}),它是第1組,(.\1)是第2組,在第2組里通過\1語法,后向引用了第1組的文本內容。
經過實際測試,會發現這樣寫是錯誤的,為什么呢?
小菜一直在強調,后向引用,引用的僅僅是文本內容,而不是正則表達式!
也就是說,組中的內容一旦匹配成功,后向引用,引用的就是匹配成功后的內容,引用的是結果,而不是表達式。
因此,(\d{1,3})(.\1){3}這個表達式實際上匹配的是四個數都相同的IP地址,比如:123.123.123.123。
至此,讀者已經掌握了傳說中的后向引用,就這么簡單。
接下來說說什么是斷言。
所謂斷言,就是指明某個字符串前邊或者后邊,將會出現滿足某種規律的字符串。
就拿文章開篇的例子來說, 我們想要的是xxx,它沒有規律,但是它前邊肯定會有<title>,后邊肯定會有</title>,這就足夠了。
想指定xxx前肯定會出現<title>,就用正后發斷言,表達式:(?<=<title>).*
向指定xxx后邊肯定會出現</title>,就用正先行斷言,表達式:.*(?=</title>)
兩個加在一起,就是(?<=<title>).*(?=</title>)
這樣就能匹配到xxx。
其實掌握了規律,就很簡單了,無論是先行還是后發,都是相對於xxx而言的,也就是相對於目標字符串而言。
假如目標字符串后邊有條件,可以理解為目標字符串在前,就用先行斷言,放在目標字符串之后。
假如目標字符串前邊有條件,可以理解為目標字符串在后,就用后發斷言,放在目標字符串之前。
假如指定滿足某個條件,就是正。
假如指定不滿足某個條件,就是負。
斷言只是條件,幫你找到真正需要的字符串,本身並不會匹配!
想解決以上問題,就要用到斷言知識。
在講斷言之前,讀者應該 先了解分組,這有助於理解斷言。
分組在正則中用()表示,根據小菜理解,分組的作用有兩個:
n 將某些規律看成是一組,然后進行組級別的重復,可以得到意想不到的效果。
n 分組之后,可以通過后向引用簡化表達式。
先來看第一個作用,對於IP地址的匹配,簡單的可以寫為如下形式:
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
但仔細觀察,我們可以發現一定的規律,可以把.\d{1,3}看成一個整體,也就是把他們看成一組,再把這個組重復3次即可。表達式如下:
\d{1,3}(.\d{1,3}){3}
這樣一看,就比較簡潔了。
再來看第二個作用,就拿匹配<title>xxx</title>標簽來說,簡單的正則可以這樣寫:
<title>.*</title>
可以看出,上邊表達式中有兩個title,完全一樣,其實可以通過分組簡寫。表達式如下:
<(title)>.*</\1>
這個例子實際上就是 反向引用的實際應用。 對於分組而言,整個表達式永遠算作第0組,在本例中,第0組是<(title)>.*</\1>,然后從左到右,依次為分組編號,因此,(title)是第1組。
用\1這種語法,可以引用某組的文本內容,\1當然就是引用第1組的文本內容了,這樣一來,就可以簡化正則表達式,只寫一次title,把它放在組里,然后在后邊引用即可。
以此為啟發,我們可不可以簡化剛剛的IP地址正則表達式呢?原來的表達式為\d{1,3}(.\d{1,3}){3},里邊的\d{1,3}重復了兩次,如果利用后向引用簡化,表達式如下:
(\d{1,3})(.\1){3}
簡單的解釋下,把\d{1,3}放在一組里,表示為(\d{1,3}),它是第1組,(.\1)是第2組,在第2組里通過\1語法,后向引用了第1組的文本內容。
經過實際測試,會發現這樣寫是錯誤的,為什么呢?
小菜一直在強調,后向引用,引用的僅僅是文本內容,而不是正則表達式!
也就是說,組中的內容一旦匹配成功,后向引用,引用的就是匹配成功后的內容,引用的是結果,而不是表達式。
因此,(\d{1,3})(.\1){3}這個表達式實際上匹配的是四個數都相同的IP地址,比如:123.123.123.123。
至此,讀者已經掌握了傳說中的后向引用,就這么簡單。
接下來說說什么是斷言。
所謂斷言,就是指明某個字符串前邊或者后邊,將會出現滿足某種規律的字符串。
就拿文章開篇的例子來說, 我們想要的是xxx,它沒有規律,但是它前邊肯定會有<title>,后邊肯定會有</title>,這就足夠了。
想指定xxx前肯定會出現<title>,就用正后發斷言,表達式:(?<=<title>).*
向指定xxx后邊肯定會出現</title>,就用正先行斷言,表達式:.*(?=</title>)
兩個加在一起,就是(?<=<title>).*(?=</title>)
這樣就能匹配到xxx。
其實掌握了規律,就很簡單了,無論是先行還是后發,都是相對於xxx而言的,也就是相對於目標字符串而言。
假如目標字符串后邊有條件,可以理解為目標字符串在前,就用先行斷言,放在目標字符串之后。
假如目標字符串前邊有條件,可以理解為目標字符串在后,就用后發斷言,放在目標字符串之前。
假如指定滿足某個條件,就是正。
假如指定不滿足某個條件,就是負。
斷言只是條件,幫你找到真正需要的字符串,本身並不會匹配!

(?=X )
零寬度正先行斷言。僅當子表達式 X 在 此位置的右側匹配時才繼續匹配。例如,/w+(?=/d) 與后跟數字的單詞匹配,而不與該數字匹配。 此構造不會回溯。
(?!X)
零寬度負先行斷言。僅當子表達式 X 不在 此位置的右側匹配時才繼續匹配。例如,例如,/w+(?!/d) 與后不跟數字的單詞匹配,而不與該數字匹配 。
(?<=X)
零寬度正后發斷言。僅當子表達式 X 在 此位置的左側匹配時才繼續匹配。例如,(?<=19)99 與跟在 19 后面的 99 的實例匹配。 此構造不會回溯。
(?<!X)
零寬度負后發斷言。僅當子表達式 X 不在此位置的左側匹配時才繼續匹配。例如,(?<!19)99 與不跟在 19 后面的 99 的實例匹配