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)手地寫需求。
- Implementing Cisco UCS Solutions
- 大學(xué)計(jì)算機(jī)應(yīng)用基礎(chǔ)實(shí)踐教程(Windows 7+Office 2013)
- 網(wǎng)絡(luò)操作系統(tǒng):Windows Server 2003管理與應(yīng)用
- 無蘋果不生活 OS X Mountain Lion隨身寶典
- Arch Linux Environment Setup How-to
- Learning Android Intents
- 嵌入式Linux系統(tǒng)開發(fā):基于Yocto Project
- Windows 7案例教程
- INSTANT Migration from Windows Server 2008 and 2008 R2 to 2012 How-to
- 從零開始學(xué)安裝與重裝系統(tǒng)
- Linux內(nèi)核API完全參考手冊(第2版)
- Hadoop Operations and Cluster Management Cookbook
- Instant Getting Started with VMware Fusion
- Drupal 7 Mobile Web Development Beginner’s Guide
- Mastering Spring Cloud