官术网_书友最值得收藏!

2.5.1 正則表達(dá)式的應(yīng)用

正則表達(dá)式主要用于搜索、替換和解析字符串,它遵循一定的語法規(guī)則,使用非常靈活,功能強(qiáng)大。使用正則表達(dá)式編寫一些邏輯驗(yàn)證非常方便,例如進(jìn)行電子郵件格式或IP地址的驗(yàn)證。正則表達(dá)式在Python爬蟲中也是必不可少的利器。re模塊使Python語言擁有全部的正則表達(dá)式功能,在處理復(fù)雜的字符串需求時(shí)它也是Python開發(fā)工作必不可少的模塊之一。

1.正則表達(dá)式簡介

正則表達(dá)式是由字母、數(shù)字和特殊字符(括號、星號和問號等)組成的。正則表達(dá)式中有許多特殊的字符(也稱為元字符),這些特殊字符是構(gòu)成正則表達(dá)式的要素。表2-3說明了正則表達(dá)式中特殊字符的含義。

表2-3 Python中各元字符明細(xì)表

注意

^與[^m]的定義完全不同,后者中的“^”表示“除了......”的意思;另外,表中的(?P<name>)與(?P=name)是Python中獨(dú)有的寫法,其他的符號在各種編程語言中都是通用的。

(1)原子

原子是正則表達(dá)式中最基本的組成單位,每個(gè)正則表達(dá)式中至少要包含一個(gè)原子,常見的原子由普通字符或通用字符和原子表構(gòu)成。

原子表是由一組地位平等的原子組成的,匹配的時(shí)候會(huì)取該原子表中的任意一個(gè)原子來進(jìn)行匹配。在Python中,原子表用[]表示,[xyz]就是一個(gè)原子表,這個(gè)原子表中定義了3個(gè)原子,這3個(gè)原子的地位平等。

如果我們要對正則表達(dá)式進(jìn)行嵌套,就需要使用分組“()”。即我們可以使用“()”將一些原子組合成一個(gè)大原子,小括號括起來的部分會(huì)被當(dāng)作一個(gè)整體來使用。

(2)貪婪模式與懶惰模式

其實(shí)從字面上就能很好地理解,貪婪模式就是盡可能多地匹配,而懶惰模式就是盡可能少地匹配。下面通過一個(gè)實(shí)例來理解,代碼如下:


#-*-encoding:utf-8-*-
import re

string = 'helolomypythonhistorypythonourpythonend'
p1 = "p.*y"  #貪婪模式
p2 = "p.*?y" #懶惰模式

r1 = re.search(p1,string)
r2 = re.search(p2,string)
print r1.group()
print r2.group()

代碼輸出結(jié)果如下:


pythonhistorypythonourpy
py

通過對比可發(fā)現(xiàn),懶惰模式下采用的是就近匹配原則,可以讓匹配更為精確;而在貪婪模式下,就算已經(jīng)找到一個(gè)最近的結(jié)尾y字符,仍然不會(huì)停止搜索,直到找不到結(jié)尾字符y為止,此時(shí)結(jié)尾的y字符即為源字符串中最右邊的y字符。

例如,要將3位數(shù)字重復(fù)兩次,可以使用下面的正則表達(dá)式:


(\d\d\d){2}

請將其與下面的正則表達(dá)式進(jìn)行區(qū)分:


\d\d\d{2}

該表達(dá)式相當(dāng)于“\d\d\d\d”,匹配的結(jié)果為“1234”和“5678”。

如果要匹配電話號碼,例如“010-12345678”這樣的電話號碼,我們一般會(huì)采用“\d\d\d-\d\d\d\d\d\d\d\d”這樣的正則表達(dá)式。這其中出現(xiàn)了11次“\d”,表達(dá)方式極為煩瑣,而且有些地區(qū)的區(qū)號也有可能是3位數(shù)字或4位數(shù)字,因此這種正則表達(dá)式就不能滿足需求了。另外,電話號碼還有很多寫法,例如01012345678,或者(010)12345668等,所以我們需要設(shè)計(jì)一個(gè)通用的正則表達(dá)式,如下:


[\(]?\d{3}[\)-]?\d{8}|[\(]?\d{4}[\)-]?\d{7}

有興趣的讀者可以關(guān)注與電話號碼相關(guān)的正則代碼,示例如下:


import re
#coding:utf-8

te1 = "027-86912233"
print re.findall(r'\d{3}-\d{8}|\d{4}-\d{7}',te1)

te2 = "0755-1234567"
print re.findall(r'\d{3}-\d{8}|\d{4}-\d{7}',te2)

te3= "(010)12345678"
print re.findall(r'[\(]?\d{3}[\)-]?\d{8}',te3)

te4 = "010-12345678"
print re.findall(r'[\(]?\d{3}[\)-]?\d{8}',te4)

結(jié)果是可以按照正則匹配打印出相應(yīng)的電話號碼。

2.使用re模塊處理正則表達(dá)式

Python的re模塊具有正則表達(dá)式的功能。re模塊提供了一些根據(jù)正則表達(dá)式查找、替換、分隔字符串的函數(shù),這些函數(shù)使用正則表達(dá)式作為第一個(gè)參數(shù)。re模塊常用的函數(shù)見表2-4。

表2-4 re模塊各函數(shù)的作用明細(xì)表

re模塊的很多函數(shù)中都有一個(gè)flags標(biāo)志位,該參數(shù)用于設(shè)置匹配的附加選項(xiàng)。例如,是否忽略大小寫、是否支持多行匹配等,具體見表2-5。

表2-5 re模塊標(biāo)志位的作用描述

正則表達(dá)式的解析非常費(fèi)時(shí),對此我們可以使用compile()進(jìn)行預(yù)編譯,compile()函數(shù)返回1個(gè)pattern對象。該對象提供一系列方法來查找、替換或擴(kuò)展字符串,從而提高字符串的匹配速度。此函數(shù)通常與match()和search()一起用于對含有分組的正則表達(dá)式進(jìn)行解析。正則表達(dá)式的分組從左往右開始計(jì)數(shù),第1個(gè)出現(xiàn)的為第1組,以此類推。此外還有0號組,0號組用于存儲匹配整個(gè)正則表達(dá)式的結(jié)果。

(1)常見函數(shù)說明

1)re.match()函數(shù)。其使用格式為:


math(pattern,string,flags=0)

第一個(gè)參數(shù)代表對應(yīng)的正則表達(dá)式,第二個(gè)參數(shù)代表對應(yīng)的源字符,第三個(gè)參數(shù)是可選的flag標(biāo)志位。

2)re.search()函數(shù)。其使用格式為:


search(pattern,string,flags=0)

第一個(gè)參數(shù)代表對應(yīng)的正則表達(dá)式,第二個(gè)參數(shù)代表對應(yīng)的源字符,第三個(gè)參數(shù)是可選的flag標(biāo)志位。

re.match()和re.search()的基本語法是一模一樣的,那么,它們的區(qū)別在哪里呢?re.match只匹配字符串的開始,如果字符串的開始不符合正則表達(dá)式,則匹配失敗,函數(shù)返回None;而re.search則匹配整個(gè)字符串(全文搜索),直到找到一個(gè)匹配為止。這里舉個(gè)例子說明:


#-*-encoding:utf-8-*-
import re

string = 'helolomypythonhistorypythonourpythonend'
patt = ".python."
r1 = re.match(patt,string)
r2 = re.search(patt,string)

print r1         #r1打印值為空
print r2         #<_sre.SRE_Match object at 0x10c56f2a0>
print r2.span()  #在起始位置匹配
print r2.group() #匹配整個(gè)表達(dá)式的字符串

運(yùn)行結(jié)果如下:


None
<_sre.SRE_Match object at 0x10c56f2a0>
(7, 15)
ypythonh

3)全局匹配函數(shù)。其上面的例子中,即使源字符串中有多個(gè)結(jié)果符號模式,也只能提取一個(gè)結(jié)果。那么,我們?nèi)绾螌⒎夏J降膬?nèi)容全部匹配出來呢?

首先,使用re.compile()對正則表達(dá)式進(jìn)行預(yù)編譯,實(shí)現(xiàn)更加有效率的匹配。編譯后,使用findall()根據(jù)正則表達(dá)式從源字符串中將匹配的結(jié)果全部找出。

代碼如下:


#-*-encoding:utf-8-*-
import re

string = 'helolomypythonhistorypythonourpythonend'
pattern = re.compile('.python.') #預(yù)編譯
result = pattern.findall(string)
print result

運(yùn)行結(jié)果如下:


['ypythonh', 'ypythono', 'rpythone']

我們再看另外一個(gè)例子,如下:


#-*-encoding:utf-8-*-
import re

string = 'helolomypythonhistorypythonourpythonend'
pattern = re.compile(".python") #預(yù)編譯
result = pattern.findall(string)
print result

運(yùn)行結(jié)果如下:


['ypython', 'ypython', 'rpython']

4)re.sub()函數(shù)。其很多時(shí)候我們需要根據(jù)正則表達(dá)式來實(shí)現(xiàn)替換某些字符串的功能,這時(shí)可以使用re.sub()函數(shù)來實(shí)現(xiàn),函數(shù)格式如下:


sub(pattern,repl,string,count)

其中第一個(gè)參數(shù)為正則表達(dá)式,第二個(gè)參數(shù)為要替換的字符串,第三個(gè)參數(shù)為源字符串,第四個(gè)參數(shù)為可選項(xiàng),代表最多可替換的次數(shù)。如果忽略不寫,那么會(huì)將符合模式的結(jié)果全部替換。

這里舉個(gè)簡單的例子說明:


#-*-coding:utf-8-*-
import re
string = 'helolomypythonhistorypythonourpythonend'
patt = "python."
r1 = re.sub(patt,'php',string)
r2 = re.sub(patt,'php',string,2)
print r1         
print r2

輸出結(jié)果如下:


helolomyphpistoryphpurphpnd
helolomyphpistoryphpurpythonend

(2)Python正則表達(dá)式的常見應(yīng)用

1)匹配電話號碼。先用前面介紹的知識點(diǎn)來整理下數(shù)字,例如,對3位數(shù)字重復(fù)兩次,可以使用下面的正則表達(dá)式:


(\d\d\d){2}

來看一個(gè)簡單的例子:


import re

patt = '(\d\d\d){2}'
num='1245987967967867789'
result = re.search(patt,num)
print result.group()

前面已設(shè)計(jì)過一個(gè)通用的正則表達(dá)式,下面使用這個(gè)通用的適合電話號碼的正則表達(dá)式進(jìn)行測試:


#coding:utf-8
import re
telre = "[\(]?\d{3}[\)-]?\d{8}|[\(]?\d{4}[\)-]?\d{7}"
te1 = "027-86912233"
te2 = "02786912233"
te3 = "(027)86912233"
te4 = "(0278)6912233"

print re.search(telre,te1).group()
print re.search(telre,te2).group()
print re.search(telre,te3).group()
print re.search(telre,te4).group()

例子中te1-te4代表的電話號碼全都能正常打印出來,結(jié)果如下:


027-86912233
02786912233
(027)86912233
(0278)6912233

2)匹配.com或.cn結(jié)尾的URL網(wǎng)址。用Python正則表達(dá)式來實(shí)現(xiàn)并不復(fù)雜,代碼如下:


#-*- encoding:utf-8 -*-
import re
pattern = "[a-zA-Z]+://[^\s]*[.com|.cn]"
string = "<a 'http://www.163.com/'網(wǎng)易首頁</a>"
result = re.search(pattern,string)
print result.group()

打印結(jié)果如下:


http://www.163.com

這里主要分析pattern的寫法,首先寫://是固定的,然后我們要以.com或.cn結(jié)尾,所以最后應(yīng)該是[.com|.cn],在://跟[.com|.cn]之間是不能出現(xiàn)空格的,這里用[^\s],也是應(yīng)該有字符串相關(guān)內(nèi)容的,所以至少是重復(fù)一次,這里用的是[^\s]*而非[^\s]+;同理,前面出現(xiàn)的[a-zA-Z]代表的是任意的字母組合,也得有內(nèi)容,所以后面得跟上+號,組合起來正則表達(dá)式就是:[a-zA-Z]+://[^\s]*[.com|.cn]。

3)匹配電子郵件。示例代碼如下:


#-*- encoding:utf-8 -*-
import re
pattern = "^[0-9a-zA-Z_-]{0,19}@[0-9a-zA-Z._-]{1,13}\.[com,cn,net]{1,3}$"
string = "yuhongchun027@163.com"
result = re.search(pattern,string)
print result.group()

打印結(jié)果如下:


yuhongchun027@163.com

這個(gè)例子也很容易理解,大家要注意這里的{0,19}及{1,13}代表的是位數(shù)。

4)騰訊云的鏡像觸發(fā)器。騰訊云的私有倉庫很多時(shí)候需要通過自定義正則規(guī)律來觸發(fā)鏡像自動(dòng)Push,所以我們需要寫一些較復(fù)雜的正則規(guī)律,在正式部署之前,需要進(jìn)行如下驗(yàn)證:


import re
pattern = "1.([0-9].){1,2}[0-9]{4}.[0-9]{6}"
string = "1.1.0730.020332"
# string = "1.0.1.0730.020332"
result = re.search(pattern,string)
print result.group()

結(jié)果可以正常顯示,如下:


1.1.0730.020332

再看另外一個(gè)例子:


import re
pattern = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{4}\.\d{6}|v\d{1,3}\.\d{1,3}-release-\d{1,3}'
string= '4.11.0.0801.102331'
result = re.search(pattern,string)

print result.group()

結(jié)果可以正常顯示,如下:


4.11.0.0801.102331

Python正則表達(dá)式在工作中應(yīng)用得非常多,我們還可以用它來寫較復(fù)雜的爬蟲需求,比如抓取某網(wǎng)站的待定格式圖片或CSS等,平時(shí)要多注意總結(jié)歸納,這樣可以加深理解,在開發(fā)工作中可以得心應(yīng)手地寫需求。

主站蜘蛛池模板: 垫江县| 江西省| 陈巴尔虎旗| 岱山县| 崇文区| 色达县| 通许县| 高阳县| 德清县| 绥阳县| 花莲市| 荆门市| 根河市| 永福县| 山阳县| 香河县| 孟州市| 格尔木市| 阿巴嘎旗| 闽清县| 扶绥县| 延川县| 中宁县| 乐平市| 九江市| 昭通市| 荥经县| 涡阳县| 丹阳市| 宁河县| 商洛市| 正阳县| 资阳市| 柘荣县| 手机| 福贡县| 裕民县| 绵阳市| 广德县| 武威市| 左云县|