什么是正則表達(dá)式童叠?
正則表達(dá)式是對(duì)字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合齿坷,組成一個(gè)“規(guī)則字符串”,這個(gè)“規(guī)則字符串”用來(lái)表達(dá)對(duì)字符串的一種過(guò)濾邏輯数焊。
正則表達(dá)式不是Python獨(dú)有的永淌,目前主流的開(kāi)發(fā)語(yǔ)言都支持正則表達(dá)式,在Python中是使用re模塊來(lái)實(shí)現(xiàn)正則表達(dá)式佩耳。
正則表達(dá)式常用符號(hào)
符號(hào) | 含義 |
---|---|
* |
匹配0個(gè)或多個(gè)表達(dá)式 |
+ |
匹配1個(gè)或多個(gè)表達(dá)式 |
^ |
匹配字符串的開(kāi)頭 |
$ |
匹配字符串的結(jié)尾 |
. |
匹配任意單個(gè)字符遂蛀,換行符除外 |
\ |
轉(zhuǎn)義字符,把有特殊含義的字符轉(zhuǎn)換為字面形式 |
? |
匹配0或多個(gè)表達(dá)式干厚,非貪婪模式 |
a|b |
匹配a或b |
() |
匹配括號(hào)內(nèi)的表達(dá)式李滴, 也表示一個(gè)組 |
[] |
匹配其中的任意一個(gè)字符 |
[^] |
匹配任意一個(gè)不在中括號(hào)里的字符 |
\n |
匹配換行符 |
\t |
匹配制表符 |
\w |
匹配任意字母數(shù)字及下劃線 |
\s |
匹配任意空白字符 |
\d |
匹配任意數(shù)字 |
match函數(shù)
re.match會(huì)嘗試從字符串的起始位置匹配一個(gè)字符串表達(dá)式,如果匹配成功就返回匹配結(jié)果蛮瞄,如果匹配失敗就返回None.
比如這里有一個(gè)字符串:
content = 'open the command palette 123456 Hello world!'
我想要匹配出其中的數(shù)字所坯,可以這樣寫(xiě):
import re
content = 'open the command palette 123456 Hello world!'
# 在match方法中,第一個(gè)參數(shù)為正則表達(dá)式挂捅,第二個(gè)參數(shù)為待匹配的字符串
result = re.match('^open.*?(\d+).*?world!$', content)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(0, 44), match='open the command palette 123456 Hello world!'>
123456
這里^open
指定了字符串的開(kāi)頭包竹,.*?
表示以非貪婪模式匹配0或多個(gè)字符,(\d+)
表示我們想要提取的是1到多個(gè)數(shù)字,需要提取的內(nèi)容一定要放在小括號(hào)里周瞎,后面接著.*?
表示以非貪婪模式匹配0或多個(gè)字符苗缩,最后的world!$
指定了字符串的結(jié)尾。
這里group(1)
表示提取第一個(gè)小括號(hào)里的內(nèi)容声诸,如果有多個(gè)小括號(hào)酱讶,可分別使用group(2)
,group(3)
來(lái)提取。
貪婪匹配與非貪婪匹配的區(qū)別
在上面的例子中彼乌,我們使用了.*?
表示以非貪婪模式匹配0或多個(gè)字符泻肯,這里如果將?
去掉,就變成了貪婪模式慰照,它們之間的具體區(qū)別是什么呢灶挟?看下面這個(gè)例子:
import re
content = 'open the command palette 123456 Hello world!'
# 這里將(\d+)前面的?去掉毒租,其他不變稚铣。
result = re.match('^open.*(\d+).*?world!$', content)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(0, 44), match='open the command palette 123456 Hello world!'>
6
如上,匹配結(jié)果變成了一個(gè)6
,而前面的12345
不見(jiàn)了墅垮,這是因?yàn)?code>.*是以貪婪模式匹配惕医,它會(huì)匹配盡可能多的字符,緊接著的\d+
也就只匹配到了一個(gè)數(shù)字6
關(guān)于包含換行符的匹配
在實(shí)際的正則匹配過(guò)程中算色,我們需要匹配的字符串常是很多行抬伺,這其中就必定包含了換行符,這個(gè)時(shí)候就要使用修飾符re.S
來(lái)匹配包含了換行符的字符串灾梦。如下例:
import re
# 有換行的字符串要用三引號(hào)''''''引起來(lái)
content = '''open the command palette
123456 Hello world!'''
# 這里加入修飾符re.S
result = re.match('^open.*?(\d+).*?world!$', content, re.S)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(0, 57), match='open the command palette \n 123456 Hel>
123456
在網(wǎng)頁(yè)匹配中峡钓,常用的修飾符有兩種:
- re.S :匹配包含換行符在內(nèi)的所有字符
- re.I :使匹配對(duì)大小寫(xiě)不敏感
關(guān)于轉(zhuǎn)義字符的匹配
上面我們羅列的一堆正則表達(dá)式的常用字符,這些字符可以稱(chēng)為特殊字符若河,如果待匹配的字符串中本身就包含了這些特殊字符椒楣,如果不加處理,就會(huì)對(duì)我們的正則匹配造成干擾牡肉,這個(gè)時(shí)候就需要用反斜杠\
來(lái)進(jìn)行轉(zhuǎn)義:
import re
content = 'price is $10.00'
result = re.match('^price.*?\$10\.00$', content)
print(result)
<_sre.SRE_Match object; span=(0, 15), match='price is $10.00'>
這里使用\$
和\.
對(duì)字符$
和.
分別進(jìn)行轉(zhuǎn)義
search函數(shù)
上面講到的match
函數(shù)是從字符串的開(kāi)頭進(jìn)行逐個(gè)匹配,如果開(kāi)頭不匹配淆九,則匹配失敗统锤,它的執(zhí)行效率有點(diǎn)類(lèi)似與單鏈表的查詢操作,得從頭開(kāi)始挨著一個(gè)一個(gè)找炭庙,而search
函數(shù)是首先是掃描整個(gè)字符串饲窿,然后返回第一個(gè)成功匹配的結(jié)果。
在很大一堆的字符串中焕蹄,如果我們只需要其中的一小段字符串逾雄,就可以使用search函數(shù)進(jìn)行匹配:
import re
content = 'open the command palette 123456 Hello world!'
result = re.search('command.*?(\d+).*?world!', content)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(9, 44), match='command palette 123456 Hello world!'>
123456
下面來(lái)看個(gè)例子,這是我在豆瓣讀書(shū)的網(wǎng)頁(yè)上復(fù)制的一段HTML代碼:
HTML = """<div class="pl2">
<a onclick=""moreurl(this,{i:'0'})"" title="追風(fēng)箏的人">
追風(fēng)箏的人
</a>
<img src="https://img3.doubanio.com/pics/read.gif" alt="可試讀" title="可試讀">
<br>
<span style="font-size:12px;">The Kite Runner</span>
</div>
<p class="pl">[美] 卡勒德·胡賽尼 / 李繼宏 / 上海人民出版社 / 2006-5 / 29.00元</p>
<div class="star clearfix">
<span class="allstar45"></span>
<span class="rating_nums">8.9</span>
<span class="pl">(
315272人評(píng)價(jià)
)</span>
</div>"""
這里我想用正則表達(dá)式匹配這本《追風(fēng)箏的人》圖書(shū)的書(shū)名,作者鸦泳,評(píng)分和評(píng)價(jià)人數(shù)银锻,我們可以這樣匹配:
import re
result = re.search('<a.*?>(.*?)</a>.*?<p.*?>(.*?)</p>.*?rating_nums.*?>(.*?)</span>.*?>\((.*?)\)</span>', HTML, re.S)
print(result)
print(result.group(1))
print(result.group(2))
print(result.group(3))
print(result.group(4))
<_sre.SRE_Match object; span=(34, 703), match='<a href="https://book.douban.com/subject/1770782/>
追風(fēng)箏的人
[美] 卡勒德·胡賽尼 / 李繼宏 / 上海人民出版社 / 2006-5 / 29.00元
8.9
315272人評(píng)價(jià)
我解釋一下上面這句正則表達(dá)式,<a.*?>(.*?)</a>
用來(lái)匹配書(shū)名做鹰,<p.*?>(.*?)</p>
用來(lái)匹配作者信息击纬, rating_nums.*?>(.*?)</span>
用來(lái)匹配評(píng)分,>\((.*?)\)</span>
用來(lái)匹配評(píng)分人數(shù)钾麸,這里要將每一個(gè)需要提取的信息放在小括號(hào)里更振,以待下一步的輸出,然后不同有用信息的正則表達(dá)式之間用.*?
來(lái)連接饭尝,最后指定修飾符re.S
進(jìn)行換行匹配
然后調(diào)用result.group(1)
來(lái)輸出第一個(gè)小括號(hào)里的內(nèi)容肯腕,調(diào)用result.group(2)
來(lái)輸出第二個(gè)小括號(hào)里的內(nèi)容,以此類(lèi)推钥平,但是從輸出結(jié)果我們可以看到有多余的空白行和我不想要的信息实撒,比如出版社和圖書(shū)價(jià)格,這里將輸出稍微整理一下:
import re
result = re.search('<a.*?>(.*?)</a>.*?<p.*?>(.*?)</p>.*?rating_nums.*?>(.*?)</span>.*?>\((.*?)\)</span>', HTML, re.S)
print(result)
print(result.group(1).strip())
print(result.group(2).strip()[:12])
print(result.group(3).strip())
print(result.group(4).strip())
<_sre.SRE_Match object; span=(34, 703), match='<a href="https://book.douban.com/subject/1770782/>
追風(fēng)箏的人
[美] 卡勒德·胡賽尼
8.9
315272人評(píng)價(jià)
如上帖池,得到了我想要的信息奈惑。
希望這個(gè)示例對(duì)你有所啟發(fā)。
findall()函數(shù)
在整個(gè)網(wǎng)頁(yè)的正則匹配中睡汹,我們想要的信息往往處在一組同名的標(biāo)簽下類(lèi)似的位置肴甸,只是屬性值有所不同,想要提取這樣的信息囚巴,就要使用findall()
函數(shù)原在,該方法會(huì)搜索整個(gè)字符串,然后返回匹配正則表達(dá)式的所有內(nèi)容彤叉。相當(dāng)于search()
函數(shù)的拓展庶柿。
具體示例省略
compile()函數(shù)
這個(gè)方法可以將正則字符串編譯成正則表達(dá)式對(duì)象,以便在后面的匹配中方便調(diào)用秽浇。例如:
import re
pattern = re.compile('<a.*?>(.*?)</a>.*?<p.*?>(.*?)</p>.*?rating_nums.*?>(.*?)</span>.*?>\((.*?)\)</span>', re.S)
result = re.search(pattern, HTML)
print(result)
print(result.group(1).strip())
print(result.group(2).strip()[:12])
print(result.group(3).strip())
print(result.group(4).strip())
<_sre.SRE_Match object; span=(34, 703), match='<a href="https://book.douban.com/subject/1770782/>
追風(fēng)箏的人
[美] 卡勒德·胡賽尼
8.9
315272人評(píng)價(jià)
正則表達(dá)式的基本用法大致就是這些浮庐,下節(jié)我們嘗試用正則表達(dá)式寫(xiě)一個(gè)小爬蟲(chóng)。
每天學(xué)習(xí)一點(diǎn)點(diǎn)柬焕,每天進(jìn)步一點(diǎn)點(diǎn)审残。