日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Python3之正則表達(dá)式詳解

 

正則表達(dá)式

本節(jié)我們看一下正則表達(dá)式的相關(guān)用法,正則表達(dá)式是處理字符串的強(qiáng)大的工具,它有自己特定的語法結(jié)構(gòu),有了它,實(shí)現(xiàn)字符串的檢索、替換、匹配驗(yàn)證都不在話下。

當(dāng)然對于爬蟲來說,有了它,我們從HTML里面提取我們想要的信息就非常方便了。

實(shí)例引入

說了這么多,可能我們對它到底是個(gè)什么還是比較模糊,下面我們就用幾個(gè)實(shí)例來感受一下正則表達(dá)式的用法。

我們打開開源中國提供的正則表達(dá)式測試工具h(yuǎn)ttp://tool.oschina.net/regex/,打開之后我們可以輸入待匹配的文本,然后選擇常用的正則表達(dá)式,就可以從我們輸入的文本中得出相應(yīng)的匹配結(jié)果了。

例如我們在這里輸入待匹配的文本如下:

Hello, my phone number is 010-86432100 and email is cqc@cuiqingcai.com, and my website is http://cuiqingcai.com.

這段字符串中包含了一個(gè)電話號碼和一個(gè)電子郵件,接下來我們就嘗試用正則表達(dá)式提取出來。

我們在網(wǎng)頁中選擇匹配Email地址,就可以看到在下方出現(xiàn)了文本中的Email。如果我們選擇了匹配網(wǎng)址URL,就可以看到在下方出現(xiàn)了文本中的URL。是不是非常神奇?

其實(shí),在這里就是用了正則表達(dá)式匹配,也就是用了一定的規(guī)則將特定的文本提取出來。比如電子郵件它開頭是一段字符串,然后是一個(gè)@符號,然后就是某個(gè)域名,這是有特定的組成格式的。另外對于URL,開頭是協(xié)議類型,然后是冒號加雙斜線,然后是域名加路徑。

對于URL來說,我們就可以用下面的正則表達(dá)式匹配:

[a-zA-z]+://1*

如果我們用這個(gè)正則表達(dá)式去匹配一個(gè)字符串,如果這個(gè)字符串中包含類似URL的文本,那就會被提取出來。

這個(gè)正則表達(dá)式看上去是亂糟糟的一團(tuán),其實(shí)不然,這里面都是有特定的語法規(guī)則的。比如a-z代表匹配任意的小寫字母,s表示匹配任意的空白字符,*就代表匹配前面的字符任意多個(gè),這一長串的正則表達(dá)式就是這么多匹配規(guī)則的組合,最后實(shí)現(xiàn)特定的匹配功能。

寫好正則表達(dá)式后,我們就可以拿它去一個(gè)長字符串里匹配查找了,不論這個(gè)字符串里面有什么,只要符合我們寫的規(guī)則,統(tǒng)統(tǒng)可以找出來。那么對于網(wǎng)頁來說,如果我們想找出網(wǎng)頁源代碼里有多少URL,就可以用匹配URL的正則表達(dá)式去匹配,就可以得到源碼中的URL了。

在上面我們說了幾個(gè)匹配規(guī)則,那么正則表達(dá)式的規(guī)則到底有多少?那么在這里把常用的匹配規(guī)則總結(jié)一下:

模式描述

w匹配字母數(shù)字及下劃線

W匹配非字母數(shù)字及下劃線

s匹配任意空白字符,等價(jià)于 [tnrf].

S匹配任意非空字符

d匹配任意數(shù)字,等價(jià)于 [0-9]

D匹配任意非數(shù)字

A匹配字符串開始

Z匹配字符串結(jié)束,如果是存在換行,只匹配到換行前的結(jié)束字符串

z匹配字符串結(jié)束

G匹配最后匹配完成的位置

n匹配一個(gè)換行符

t匹配一個(gè)制表符

^匹配字符串的開頭

$匹配字符串的末尾。

.匹配任意字符,除了換行符,當(dāng)re.DOTALL標(biāo)記被指定時(shí),則可以匹配包括換行符的任意字符。

[...]用來表示一組字符,單獨(dú)列出:[amk] 匹配 'a','m'或'k'

2不在[]中的字符:3 匹配除了a,b,c之外的字符。

*匹配0個(gè)或多個(gè)的表達(dá)式。

+匹配1個(gè)或多個(gè)的表達(dá)式。

?匹配0個(gè)或1個(gè)由前面的正則表達(dá)式定義的片段,非貪婪方式

{n}精確匹配n個(gè)前面表達(dá)式。

{n, m}匹配 n 到 m 次由前面的正則表達(dá)式定義的片段,貪婪方式

a|b匹配a或b

( )匹配括號內(nèi)的表達(dá)式,也表示一個(gè)組

可能完了之后就有點(diǎn)暈暈的了把,不用擔(dān)心,下面我們會詳細(xì)講解下一些常見的規(guī)則的用法。怎么用它來從網(wǎng)頁中提取我們想要的信息。

Python中使用

其實(shí)正則表達(dá)式不是Python獨(dú)有的,它在其他編程語言中也可以使用,但是Python的re庫提供了整個(gè)正則表達(dá)式的實(shí)現(xiàn),利用re庫我們就可以在Python中使用正則表達(dá)式來,在Python中寫正則表達(dá)式幾乎都是用的這個(gè)庫。

下面我們就來了解下它的用法。

match()

在這里首先介紹第一個(gè)常用的匹配方法,match()方法,我們向這個(gè)方法傳入要匹配的字符串以及正則表達(dá)式,就可以來檢測這個(gè)正則表達(dá)式是否匹配字符串了。

match()方法會嘗試從字符串的起始位置匹配正則表達(dá)式,如果匹配,就返回匹配成功的結(jié)果,如果不匹配,那就返回None。

我們用一個(gè)實(shí)例來感受一下: 

 
 
 
 
  1. import re  
  2. content = 'Hello 123 4567 World_This is a Regex Demo'  
  3. print(len(content))  
  4. reresult = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}', content)  
  5. print(result)  
  6. print(result.group())  
  7. print(result.span()) 

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

 
 
 
 
  1. 41  
  2. <_sre.SRE_Match object; span=(0, 25), match='Hello 123 4567 World_This'>  
  3. Hello 123 4567 World_This  
  4. (0, 25) 

在這里我們首先聲明了一個(gè)字符串,包含英文字母、空白字符、數(shù)字等等內(nèi)容,接下來我們寫了一個(gè)正則表達(dá)式^Hellosdddsd{4}sw{10}來匹配這個(gè)長字符串。

開頭的^是匹配字符串的開頭,也就是以Hello開頭,然后s匹配空白字符,用來匹配目標(biāo)字符串的空格,d匹配數(shù)字,三個(gè)d匹配123,然后再寫一個(gè)s匹配空格,后面還有4567,我們其實(shí)可以依然用四個(gè)d來匹配,但是這么寫起來比較繁瑣,所以在后面可以跟{4}代表匹配前面的字符四次,也就是匹配四個(gè)數(shù)字,這樣也可以完成匹配,然后后面再緊接一個(gè)空白字符,然后w{10}匹配10個(gè)字母及下劃線,正則表達(dá)式到此為止就結(jié)束了,我們注意到其實(shí)并沒有把目標(biāo)字符串匹配完,不過這樣依然可以進(jìn)行匹配,只不過匹配結(jié)果短一點(diǎn)而已。

我們調(diào)用match()方法,第一個(gè)參數(shù)傳入了正則表達(dá)式,第二個(gè)參數(shù)傳入了要匹配的字符串。

打印輸出一下結(jié)果,可以看到結(jié)果是SRE_Match對象,證明成功匹配,它有兩個(gè)方法,group()方法可以輸出匹配到的內(nèi)容,結(jié)果是Hello 123 4567 World_This,這恰好是我們正則表達(dá)式規(guī)則所匹配的內(nèi)容,span()方法可以輸出匹配的范圍,結(jié)果是(0, 25),這個(gè)就是匹配到的結(jié)果字符串在原字符串中的位置范圍。

通過上面的例子我們可以基本了解怎樣在Python中怎樣使用正則表達(dá)式來匹配一段文字。

匹配目標(biāo)

剛才我們用了match()方法可以得到匹配到的字符串內(nèi)容,但是如果我們想從字符串中提取一部分內(nèi)容怎么辦呢?就像最前面的實(shí)例一樣,從一段文本中提取出郵件或電話號等內(nèi)容。

在這里可以使用()括號來將我們想提取的子字符串括起來,()實(shí)際上就是標(biāo)記了一個(gè)子表達(dá)式的開始和結(jié)束位置,被標(biāo)記的每個(gè)子表達(dá)式會依次對應(yīng)每一個(gè)分組,我們可以調(diào)用group()方法傳入分組的索引即可獲取提取的結(jié)果。

下面我們用一個(gè)實(shí)例感受一下: 

 
 
 
 
  1. import re  
  2. content = 'Hello 1234567 World_This is a Regex Demo'  
  3. reresult = re.match('^Hello\s(\d+)\sWorld', content)  
  4. print(result)  
  5. print(result.group())  
  6. print(result.group(1))  
  7. print(result.span()) 

依然是前面的字符串,在這里我們想匹配這個(gè)字符串并且把其中的1234567提取出來,在這里我們將數(shù)字部分的正則表達(dá)式用()括起來,然后接下來調(diào)用了group(1)獲取匹配結(jié)果。

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

 
 
 
 
  1. <_sre.SRE_Match object; span=(0, 19), match='Hello 1234567 World'>  
  2. Hello 1234567 World  
  3. 1234567  
  4. (0, 19) 

可以看到在結(jié)果中成功得到了1234567,我們獲取用的是group(1),與group()有所不同,group()會輸出完整的匹配結(jié)果,而group(1)會輸出第一個(gè)被()包圍的匹配結(jié)果,假如正則表達(dá)式后面還有()包括的內(nèi)容,那么我們可以依次用group(2)、group(3)等來依次獲取。

通用匹配

剛才我們寫的正則表達(dá)式其實(shí)比較復(fù)雜,出現(xiàn)空白字符我們就寫s匹配空白字符,出現(xiàn)數(shù)字我們就寫d匹配數(shù)字,工作量非常大,其實(shí)完全沒必要這么做,還有一個(gè)萬能匹配可以用,也就是.,.可以匹配任意字符(除換行符),又代表匹配前面的字符無限次,所以它們組合在一起就可以匹配任意的字符了,有了它我們就不用挨個(gè)字符地匹配了。

所以接著上面的例子,我們可以改寫一下正則表達(dá)式。 

 
 
 
 
  1. import re  
  2. content = 'Hello 123 4567 World_This is a Regex Demo'  
  3. reresult = re.match('^Hello.*Demo$', content)  
  4. print(result)  
  5. print(result.group())  
  6. print(result.span()) 

在這里我們將中間的部分直接省略,全部用.*來代替,最后加一個(gè)結(jié)尾字符串就好了,運(yùn)行結(jié)果如下: 

 
 
 
 
  1. <_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>  
  2. Hello 123 4567 World_This is a Regex Demo  
  3. (0, 41) 

可以看到group()方法輸出了匹配的全部字符串,也就是說我們寫的正則表達(dá)式匹配到了目標(biāo)字符串的全部內(nèi)容,span()方法輸出(0, 41),是整個(gè)字符串的長度。

因此,我們可以在使用.*來簡化正則表達(dá)式的書寫。

貪婪匹配與非貪婪匹配

在使用上面的通用匹配.*的時(shí)候可能我們有時(shí)候匹配到的并不是想要的結(jié)果,我們看下面的例子: 

 
 
 
 
  1. import re  
  2. content = 'Hello 1234567 World_This is a Regex Demo'  
  3. reresult = re.match('^He.*(\d+).*Demo$', content)  
  4. print(result)  
  5. print(result.group(1)) 

在這里我們依然是想獲取中間的數(shù)字,所以中間我們依然寫的是(d+),數(shù)字兩側(cè)由于內(nèi)容比較雜亂,所以兩側(cè)我們想省略來寫,都寫.,最后組成^He.(d+).*Demo$,看樣子并沒有什么問題,我們看下運(yùn)行結(jié)果:

<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>

7

奇怪的事情發(fā)生了,我們只得到了7這個(gè)數(shù)字,這是怎么回事?

這里就涉及一個(gè)貪婪匹配與非貪婪匹配的原因了,貪婪匹配下,.會匹配盡可能多的字符,我們的正則表達(dá)式中.后面是d+,也就是至少一個(gè)數(shù)字,并沒有指定具體多少個(gè)數(shù)字,所以.*就盡可能匹配多的字符,所以它把123456也匹配了,給d+留下一個(gè)可滿足條件的數(shù)字7,所以d+得到的內(nèi)容就只有數(shù)字7了。

但這樣很明顯會給我們的匹配帶來很大的不便,有時(shí)候匹配結(jié)果會莫名其妙少了一部分內(nèi)容。其實(shí)這里我們只需要使用非貪婪匹配匹配就好了,非貪婪匹配的寫法是.*?,多了一個(gè)?,那么它可以達(dá)到怎樣的效果?我們再用一個(gè)實(shí)例感受一下: 

 
 
 
 
  1. import re  
  2. content = 'Hello 1234567 World_This is a Regex Demo'  
  3. reresult = re.match('^He.*?(\d+).*Demo$', content)  
  4. print(result)  
  5. print(result.group(1)) 

在這里我們只是將第一個(gè).改成了.?,轉(zhuǎn)變?yōu)榉秦澙菲ヅ淦ヅ洹=Y(jié)果如下: 

 
 
 
 
  1. <_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>  
  2. 1234567 

很好,這下我們就可以成功獲取1234567了。原因可想而知,貪婪匹配是盡可能匹配多的字符,非貪婪匹配就是盡可能匹配少的字符,.?之后是d+用來匹配數(shù)字,當(dāng).?匹配到Hello后面的空白字符的時(shí)候,再往后的字符就是數(shù)字了,而d+恰好可以匹配,那么這里.?就不再進(jìn)行匹配,交給d+去匹配后面的數(shù)字。所以這樣,.?匹配了盡可能少的字符,d+的結(jié)果就是1234567了。

所以說,在做匹配的時(shí)候,字符串中間我們可以盡量使用非貪婪匹配來匹配,也就是用.?來代替.,以免出現(xiàn)匹配結(jié)果缺失的情況。

但這里注意,如果匹配的結(jié)果在字符串結(jié)尾,.*?就有可能匹配不到任何內(nèi)容了,因?yàn)樗鼤ヅ浔M可能少的字符,例如: 

 
 
 
 
  1. import re  
  2. content = 'http://weibo.com/comment/kEraCN'  
  3. reresult1 = re.match('http.*?comment/(.*?)', content)  
  4. reresult2 = re.match('http.*?comment/(.*)', content)  
  5. print('result1', result1.group(1))  
  6. print('result2', result2.group(1)) 

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

result1

result2 kEraCN

觀察到.?沒有匹配到任何結(jié)果,而.則盡量匹配多的內(nèi)容,成功得到了匹配結(jié)果。

所以在這里好好體會一下貪婪匹配和非貪婪匹配的原理,對后面寫正則表達(dá)式非常有幫助。

修飾符

正則表達(dá)式可以包含一些可選標(biāo)志修飾符來控制匹配的模式。修飾符被指定為一個(gè)可選的標(biāo)志。

我們用一個(gè)實(shí)例先來感受一下: 

 
 
 
 
  1. import re  
  2. content = '''Hello 1234567 World_This  
  3. is a Regex Demo  
  4. '''  
  5. reresult = re.match('^He.*?(\d+).*?Demo$', content)  
  6. print(result.group(1)) 

和上面的例子相仿,我們在字符串中加了個(gè)換行符,正則表達(dá)式也是一樣的來匹配其中的數(shù)字,看一下運(yùn)行結(jié)果: 

 
 
 
 
  1. AttributeError Traceback (most recent call last)  
  2.  in ()  
  3.       5 '''  
  4.       6 reresult = re.match('^He.*?(\d+).*?Demo$', content)  
  5. ----> 7 print(result.group(1))  
  6. AttributeError: 'NoneType' object has no attribute 'group' 

運(yùn)行直接報(bào)錯(cuò),也就是說正則表達(dá)式?jīng)]有匹配到這個(gè)字符串,返回結(jié)果為None,而我們又調(diào)用了group()方法所以導(dǎo)致AttributeError。

那我們加了一個(gè)換行符為什么就匹配不到了呢?是因?yàn)?匹配的是除換行符之外的任意字符,當(dāng)遇到換行符時(shí),.*?就不能匹配了,所以導(dǎo)致匹配失敗。

那么在這里我們只需要加一個(gè)修飾符re.S,即可修正這個(gè)錯(cuò)誤。 

 
 
 
 
  1. reresult = re.match('^He.*?(\d+).*?Demo$', content, re.S) 

在match()方法的第三個(gè)參數(shù)傳入re.S,它的作用是使.匹配包括換行符在內(nèi)的所有字符。

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

 
 
 
 
  1. 1234567 

這個(gè)re.S在網(wǎng)頁匹配中會經(jīng)常用到,因?yàn)镠TML節(jié)點(diǎn)經(jīng)常會有換行,加上它我們就可以匹配節(jié)點(diǎn)與節(jié)點(diǎn)之間的換行了。

另外還有一些修飾符,在必要的情況下也可以使用:

修飾符描述

re.I使匹配對大小寫不敏感

re.L做本地化識別(locale-aware)匹配

re.M多行匹配,影響 ^ 和 $

re.S使 . 匹配包括換行在內(nèi)的所有字符

re.U根據(jù)Unicode字符集解析字符。這個(gè)標(biāo)志影響 w, W, b, B.

re.X該標(biāo)志通過給予你更靈活的格式以便你將正則表達(dá)式寫得更易于理解。

在網(wǎng)頁匹配中較為常用的為re.S、re.I。

轉(zhuǎn)義匹配

我們知道正則表達(dá)式定義了許多匹配模式,如.匹配除換行符以外的任意字符,但是如果目標(biāo)字符串里面它就包含.我們改怎么匹配?

那么這里就需要用到轉(zhuǎn)義匹配了,我們用一個(gè)實(shí)例來感受一下: 

 
 
 
 
  1. import re  
  2. content = '(百度)www.baidu.com'  
  3. reresult = re.match('\(百度\)www\.baidu\.com', content)  
  4. print(result) 

當(dāng)遇到用于正則匹配模式的特殊字符時(shí),我們在前面加反斜線來轉(zhuǎn)義一下就可以匹配了。例如.我們就可以用.來匹配,運(yùn)行結(jié)果: 

 
 
 
 
  1. <_sre.SRE_Match object; span=(0, 17), match='(百度)www.baidu.com'> 

可以看到成功匹配到了原字符串。

以上是寫正則表達(dá)式常用的幾個(gè)知識點(diǎn),熟練掌握上面的知識點(diǎn)對后面我們寫正則表達(dá)式匹配非常有幫助。

search()

我們在前面提到過match()方法是從字符串的開頭開始匹配,一旦開頭不匹配,那么整個(gè)匹配就失敗了。

我們看下面的例子: 

 
 
 
 
  1. import re  
  2. content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'  
  3. reresult = re.match('Hello.*?(\d+).*?Demo', content)  
  4. print(result) 

在這里我們有一個(gè)字符串,它是以Extra開頭的,但是正則表達(dá)式我們是以Hello開頭的,整個(gè)正則表達(dá)式是字符串的一部分,但是這樣匹配是失敗的,也就是說只要第一個(gè)字符不匹配整個(gè)匹配就不能成功,運(yùn)行結(jié)果如下:

None

所以match()方法在我們在使用的時(shí)候需要考慮到開頭的內(nèi)容,所以在做匹配的時(shí)候并不那么方便,它適合來檢測某個(gè)字符串是否符合某個(gè)正則表達(dá)式的規(guī)則。

所以在這里就有另外一個(gè)方法search(),它在匹配時(shí)會掃描整個(gè)字符串,然后返回第一個(gè)成功匹配的結(jié)果,也就是說,正則表達(dá)式可以是字符串的一部分,在匹配時(shí),search()方法會依次掃描字符串,直到找到第一個(gè)符合規(guī)則的字符串,然后返回匹配內(nèi)容,如果搜索完了還沒有找到,那就返回None。

我們把上面的代碼中的match()方法修改成search(),再看下運(yùn)行結(jié)果: 

 
 
 
 
  1. <_sre.SRE_Match object; span=(13, 53), match='Hello 1234567 World_This is a Regex Demo'>  
  2. 1234567 

這樣就得到了匹配結(jié)果。

所以說,為了匹配方便,我們可以盡量使用search()方法。

下面我們再用幾個(gè)實(shí)例來感受一下search()方法的用法。

首先這里有一段待匹配的HTML文本,我們接下來寫幾個(gè)正則表達(dá)式實(shí)例來實(shí)現(xiàn)相應(yīng)信息的提取。 

 
 
 
 
  1. html = '''  
  2.     經(jīng)典老歌  
  3.       
  4.         經(jīng)典老歌列表  
  5.     

      
  6.       
  7.         一路上有你
  8.   
  9.           
  10.             滄海一聲笑  
  11.         
  12.   
  13.           
  14.             往事隨風(fēng)  
  15.         
  16.   
  17.         光輝歲月
  18.   
  19.         記事本
  20.   
  21.           
  22.             但愿人長久  
  23.         
  24.   
  25.       
''' 

觀察到

    節(jié)點(diǎn)里面有許多
  • 節(jié)點(diǎn),其中
  • 節(jié)點(diǎn)有的包含節(jié)點(diǎn),有的不包含節(jié)點(diǎn),節(jié)點(diǎn)還有一些相應(yīng)的屬性,超鏈接和歌手名。

    首先我們嘗試提取class為active的

  • 節(jié)點(diǎn)內(nèi)部的超鏈接包含的歌手名和歌名。

    所以我們需要提取第三個(gè)

  • 節(jié)點(diǎn)下的節(jié)點(diǎn)的singer屬性和文本。

    所以正則表達(dá)式可以以

  • 開頭,然后接下來尋找一個(gè)標(biāo)志符active,中間的部分可以用.?來匹配,然后接下來我們要提取singer這個(gè)屬性值,所以還需要寫入singer="(.?)",我們需要提取的部分用小括號括起來,以便于用group()方法提取出來,它的兩側(cè)邊界是雙引號,然后接下來還需要匹配節(jié)點(diǎn)的文本,那么它的左邊界是>,右邊界是,所以我們指定一下左右邊界,然后目標(biāo)內(nèi)容依然用(.?)來匹配,所以最后的正則表達(dá)式就變成了(.*?)',然后我們再調(diào)用search()方法,它便會搜索整個(gè)HTML文本,找到符合正則表達(dá)式的第一個(gè)內(nèi)容返回。

    另外由于代碼有換行,所以這里第三個(gè)參數(shù)需要傳入re.S

    所以整個(gè)匹配代碼如下: 

     
     
     
     
    1. reresult = re.search('(.*?)', html, re.S)  
    2. if result:  
    3.     print(result.group(1), result.group(2)) 

    由于我們需要獲取的歌手和歌名都已經(jīng)用了小括號包圍,所以可以用group()方法獲取,序號依次對應(yīng)group()的參數(shù)。

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

    齊秦 往事隨風(fēng)

    可以看到這個(gè)正是我們想提取的class為active的

  • 節(jié)點(diǎn)內(nèi)部的超鏈接包含的歌手名和歌名。

    那么正則表達(dá)式不加active會怎樣呢?也就是匹配不帶class為active的節(jié)點(diǎn)內(nèi)容,我們將正則表達(dá)式中的active去掉,代碼改寫如下: 

     
     
     
     
    1. reresult = re.search('(.*?)', html, re.S)  
    2. if result:  
    3.     print(result.group(1), result.group(2)) 

    由于search()方法會返回第一個(gè)符合條件的匹配目標(biāo),那在這里結(jié)果就變了。

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

    任賢齊 滄海一聲笑

    因?yàn)槲覀儼補(bǔ)ctive標(biāo)簽去掉之后,從字符串開頭開始搜索,符合條件的節(jié)點(diǎn)就變成了第二個(gè)

  • 節(jié)點(diǎn),后面的就不再進(jìn)行匹配,所以運(yùn)行結(jié)果自然就變成了第二個(gè)
  • 節(jié)點(diǎn)中的內(nèi)容。

    注意在上面兩次匹配中,search()方法的第三個(gè)參數(shù)我們都加了re.S,使得.*?可以匹配換行,所以含有換行的

  • 節(jié)點(diǎn)被匹配到了,如果我們將其去掉,結(jié)果會是什么? 

     
     
     
     
    1. reresult = re.search('(.*?)', html)  
    2. if result:  
    3.     print(result.group(1), result.group(2))  
    4. 運(yùn)行結(jié)果:  
    5. beyond 光輝歲月 

    可以看到結(jié)果就變成了第四個(gè)

  • 節(jié)點(diǎn)的內(nèi)容,這是因?yàn)榈诙€(gè)和第三個(gè)
  • 標(biāo)簽都包含了換行符,去掉re.S之后,.*?已經(jīng)不能匹配換行符,所以正則表達(dá)式不會匹配到第二個(gè)和第三個(gè)
  • 節(jié)點(diǎn),而第四個(gè)
  • 節(jié)點(diǎn)中不包含換行符,所以成功匹配。

    由于絕大部分的HTML文本都包含了換行符,所以通過上面的例子,我們盡量都需要加上re.S修飾符,以免出現(xiàn)匹配不到的問題。

    findall()

    在前面我們說了search()方法的用法,它可以返回匹配正則表達(dá)式的第一個(gè)內(nèi)容,但是如果我們想要獲取匹配正則表達(dá)式的所有內(nèi)容的話怎么辦?這時(shí)就需要借助于findall()方法了。

    findall()方法會搜索整個(gè)字符串然后返回匹配正則表達(dá)式的所有內(nèi)容。

    還是上面的HTML文本,如果我們想獲取所有節(jié)點(diǎn)的超鏈接、歌手和歌名,就可以將search()方法換成findall()方法。如果有返回結(jié)果的話就是list類型,所以我們需要遍歷一下list來獲依次獲取每組內(nèi)容。 

     
     
     
     
    1. reresults = re.findall('
    2. print(results)  
    3. print(type(results))  
    4. for result in results:  
    5.     print(result)  
    6.     print(result[0], result[1], result[2])  
    7. 運(yùn)行結(jié)果:  
    8. [('/2.mp3', '任賢齊', '滄海一聲笑'), ('/3.mp3', '齊秦', '往事隨風(fēng)'), ('/4.mp3', 'beyond', '光輝歲月'), ('/5.mp3', '陳慧琳', '記事本'), ('/6.mp3', '鄧麗君', '但愿人長久')]  
    9.   
    10. ('/2.mp3', '任賢齊', '滄海一聲笑')  
    11. /2.mp3 任賢齊 滄海一聲笑  
    12. ('/3.mp3', '齊秦', '往事隨風(fēng)')  
    13. /3.mp3 齊秦 往事隨風(fēng)  
    14. ('/4.mp3', 'beyond', '光輝歲月')  
    15. /4.mp3 beyond 光輝歲月  
    16. ('/5.mp3', '陳慧琳', '記事本')  
    17. /5.mp3 陳慧琳 記事本  
    18. ('/6.mp3', '鄧麗君', '但愿人長久')  
    19. /6.mp3 鄧麗君 但愿人長久 

    可以看到,返回的list的每個(gè)元素都是tuple類型,我們用對應(yīng)的索引依次取出即可。

    所以,如果只是獲取第一個(gè)內(nèi)容,可以用search()方法,當(dāng)需要提取多個(gè)內(nèi)容時(shí),就可以用findall()方法。

    sub()

    正則表達(dá)式除了提取信息,我們有時(shí)候還需要借助于它來修改文本,比如我們想要把一串文本中的所有數(shù)字都去掉,如果我們只用字符串的replace()方法那就太繁瑣了,在這里我們就可以借助于sub()方法。

    我們用一個(gè)實(shí)例來感受一下: 

     
     
     
     
    1. import re  
    2. content = '54aK54yr5oiR54ix5L2g'  
    3. content = re.sub('\d+', '', content)  
    4. print(content)  
    5. 運(yùn)行結(jié)果:  
    6. aKyroiRixLg 

    在這里我們只需要在第一個(gè)參數(shù)傳入d+來匹配所有的數(shù)字,然后第二個(gè)參數(shù)是替換成的字符串,要去掉的話就可以賦值為空,第三個(gè)參數(shù)就是原字符串。

    得到的結(jié)果就是替換修改之后的內(nèi)容。

    那么在上面的HTML文本中,如果我們想正則獲取所有

  • 節(jié)點(diǎn)的歌名,如果直接用正則表達(dá)式來提取可能比較繁瑣,比如可以寫成這樣子: 

     
     
     
     
    1. reresults = re.findall('\s*?()?(\w+)()?\s*?
    2. ', html, re.S)  
    3. for result in results:  
    4.     print(result[1])  
    5. 運(yùn)行結(jié)果:  
    6. 一路上有你  
    7. 滄海一聲笑  
    8. 往事隨風(fēng)  
    9. 光輝歲月  
    10. 記事本  
    11. 但愿人長久 

    但如果我們借助于sub()函數(shù)就比較簡單了,我們可以先用sub()函數(shù)將節(jié)點(diǎn)去掉,只留下文本,然后再利用findall()提取就好了。 

     
     
     
     
    1. html = re.sub('|', '', html)  
    2. print(html)  
    3. reresults = re.findall('(.*?)
    4. ', html, re.S)  
    5. for result in results:  
    6.     print(result.strip())  
    7. 運(yùn)行結(jié)果:  
    8.   
    9.     經(jīng)典老歌  
    10.       
    11.         經(jīng)典老歌列表  
    12.     

        
    13.       
    14.         一路上有你
    15.   
    16.           
    17.             滄海一聲笑  
    18.         
    19.   
    20.           
    21.             往事隨風(fēng)  
    22.         
    23.   
    24.         光輝歲月
    25.   
    26.         記事本
    27.   
    28.           
    29.             但愿人長久  
    30.         
    31.   
    32.     
  
  •   
  • 一路上有你  
  • 滄海一聲笑  
  • 往事隨風(fēng)  
  • 光輝歲月  
  • 記事本  
  • 但愿人長久 
  • 可以到標(biāo)簽在經(jīng)過sub()函數(shù)處理后都沒有了,然后再findall()直接提取即可。所以在適當(dāng)?shù)臅r(shí)候我們可以借助于sub()方法做一些相應(yīng)處理可以事半功倍。

    compile()

    前面我們所講的方法都是用來處理字符串的方法,最后再介紹一個(gè)compile()方法,這個(gè)方法可以講正則字符串編譯成正則表達(dá)式對象,以便于在后面的匹配中復(fù)用。 

     
     
     
     
    1. import re  
    2. content1 = '2016-12-15 12:00'  
    3. content2 = '2016-12-17 12:55'  
    4. content3 = '2016-12-22 13:21'  
    5. pattern = re.compile('\d{2}:\d{2}')  
    6. reresult1 = re.sub(pattern, '', content1)  
    7. reresult2 = re.sub(pattern, '', content2)  
    8. reresult3 = re.sub(pattern, '', content3)  
    9. print(result1, result2, result3) 

    例如這里有三個(gè)日期,我們想分別將三個(gè)日期中的時(shí)間去掉,所以在這里我們可以借助于sub()方法,sub()方法的第一個(gè)參數(shù)是正則表達(dá)式,但是這里我們沒有必要重復(fù)寫三個(gè)同樣的正則表達(dá)式,所以可以借助于compile()函數(shù)將正則表達(dá)式編譯成一個(gè)正則表達(dá)式對象,以便復(fù)用。

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

     
     
     
     
    1. 2016-12-15  2016-12-17  2016-12-22  

    另外compile()還可以傳入修飾符,例如re.S等修飾符,這樣在search()、findall()等方法中就不需要額外傳了。所以compile()方法可以說是給正則表達(dá)式做了一層封裝,以便于我們更好地復(fù)用。

    到此為止,正則表達(dá)式的基本用法就介紹完畢了,后面我們會有實(shí)戰(zhàn)來講解正則表達(dá)式的使用。

     


    網(wǎng)站標(biāo)題:Python3之正則表達(dá)式詳解
    URL網(wǎng)址:http://www.dlmjj.cn/article/dpggoge.html