總目錄:http://www.reibang.com/p/e406a9bc93a9
Python-爬蟲 - 子目錄:http://www.reibang.com/p/23cf57674bf1
文檔:https://docs.python.org/3/library/re.html
正則表達(dá)式語言相對小型和受限(功能有限),因此并非所有字符串處理都能用正則表達(dá)式完成。
當(dāng)然也有些任務(wù)可以用正則表達(dá)式完成沛硅,不過最終表達(dá)式會(huì)變得異常復(fù)雜眼刃。碰到這些情形時(shí),編寫Python 代碼進(jìn)行處理可能反而更好摇肌;盡管 Python 代碼比一個(gè)精巧的正則表達(dá)式要慢些擂红,但它更易理解。
常用方法
re.compile: 編譯一個(gè)正則表達(dá)式模式(pattern)
re.match: 從頭開始匹配, 使用group()方法可以獲取第一個(gè)匹配值
re.search: 用包含方式匹配围小,使用group()方法可以獲取第一個(gè)匹配值
re.findall: 用包含方式匹配昵骤,把所有匹配到的字符放到以列表中的元素返回多個(gè)匹配值
re.sub: 匹配字符并替換
re.split: 以匹配到的字符當(dāng)做列表分隔符,返回列表
基本模式
簡單字符
沒有特殊意義的字符都是簡單字符肯适,簡單字符就代表自身变秦,絕大部分字符都是簡單字符,舉個(gè)例子
/abc/ //匹配 abc
/123/ //匹配 123
/-_-/ //匹配 -_-
轉(zhuǎn)義字符
第一種框舔,是為了匹配不方便顯示的特殊字符蹦玫,比如換行,tab符號等
第二種雨饺,正則中預(yù)先定義了一些代表特殊意義的字符钳垮,比如\w等
第三種,在正則中某些字符有特殊含義(比如下面說到的)额港,轉(zhuǎn)義字符可以讓其顯示自身的含義
字符集合
有時(shí)我們需要匹配一類字符饺窿,字符集可以實(shí)現(xiàn)這個(gè)功能,字符集的語法用[]分隔移斩,下面的代碼能夠匹配a或b或c
[abc]
如果要表示字符很多肚医,可以使用-表示一個(gè)范圍內(nèi)的字符,下面兩個(gè)功能相同
[0123456789]
[0-9]
在前面添加^向瓷,可表示非的意思肠套,下面的代碼能夠匹配abc之外的任意字符
[^abc]
其實(shí)正則還內(nèi)置了一些字符集,在上面的轉(zhuǎn)義字符有提到猖任,下面給出內(nèi)置字符集對應(yīng)的自定義字符集
.匹配除了換行符(\n)以外的任意一個(gè)字符 = [^\n]
\w = [0-9a-Z_]
\W = [^0-9a-Z_]
\s = [ \t\n\v]
\S = [^ \t\n\v]
\d = [0-9]
\D = [^0-9]
量詞
如果我們有三個(gè)蘋果你稚,我們可以說自己有個(gè)3個(gè)蘋果,也可以說有一個(gè)蘋果朱躺,一個(gè)蘋果刁赖,一個(gè)蘋果,每種語言都有量詞的概念
如果需要匹配多次某個(gè)字符长搀,正則也提供了量詞的功能宇弛,正則中的量詞有多個(gè),如?源请、+枪芒、*彻况、{n}、{m,n}舅踪、{m,}
{n}匹配n次纽甘,比如a{2},匹配aa
{m, n}匹配m-n次硫朦,優(yōu)先匹配n次贷腕,比如a{1,3},可以匹配aaa咬展、aa、a
{m,}匹配m-∞次瞒斩,優(yōu)先匹配∞次破婆,比如a{1,},可以匹配aaaa...
?匹配0次或1次胸囱,優(yōu)先匹配1次祷舀,相當(dāng)于{0,1}
+匹配1-n次,優(yōu)先匹配n次烹笔,相當(dāng)于{1,}
*匹配0-n次裳扯,優(yōu)先匹配n次,相當(dāng)于{0,}
正則默認(rèn)和人心一樣是貪婪的谤职,也就是常說的貪婪模式饰豺,凡是表示范圍的量詞,都優(yōu)先匹配上限而不是下限
a{1, 3} //匹配字符串'aaa'的話允蜈,會(huì)匹配aaa而不是a
有時(shí)候這不是我們想要的結(jié)果冤吨,可以在量詞后面加上?,就可以開啟非貪婪模式
a{1, 3}? //匹配字符串'aaa'的話饶套,會(huì)匹配a而不是aaa
字符邊界
有時(shí)我們會(huì)有邊界的匹配要求漩蟆,比如已xxx開頭,已xxx結(jié)尾
^在[]外表示匹配開頭的意思
^abc //可以匹配abc妓蛮,但是不能匹配aabc
$表示匹配結(jié)尾的意思
abc$ //可以匹配abc怠李,但是不能匹配abcc
上面提到的\b表示單詞的邊界
abc\b //可以匹配 abc,但是不能匹配 abcc
選擇表達(dá)式
有時(shí)我們想匹配x或者y蛤克,如果x和y是單個(gè)字符捺癞,可以使用字符集,[abc]可以匹配a或b或c咖耘,如果x和y是多個(gè)字符翘簇,字符集就無能為力了,此時(shí)就要用到分組
正則中用|來表示分組儿倒,a|b表示匹配a或者b的意思
123|456|789 //匹配 123 或 456 或 789
分組和引用
分組是正則中非常強(qiáng)大的一個(gè)功能版保,可以讓上面提到的量詞作用于一組字符呜笑,而非單個(gè)字符,分組的語法是圓括號包裹(xxx)
(abc){2}? ? ?//匹配abcabc
分組不能放在[]中彻犁,分組中還可以使用選擇表達(dá)式
(123|456){2}? ? ?//匹配 123123叫胁、456456、123456汞幢、456123
和分組相關(guān)的概念還有一個(gè)捕獲分組和非捕獲分組驼鹅,分組默認(rèn)都是捕獲的,在分組的(后面添加?:可以讓分組變?yōu)榉遣东@分組森篷,非捕獲分組可以提高性能和簡化邏輯
'123'.match(/(?123)/) //返回 ['123']
'123'.match(/(123)/)? //返回 ['123', '123']
和分組相關(guān)的另一個(gè)概念是引用输钩,比如在匹配html標(biāo)簽時(shí),通常希望后面的xxx能夠和前面保持一致
引用的語法是\數(shù)字仲智,數(shù)字代表引用前面第幾個(gè)捕獲分組买乃,注意非捕獲分組不能被引用
<([a-z]+)><\/\1> //可以匹配 ``或 ``等
預(yù)搜索
如果你想匹配xxx前不能是yyy,或者xxx后不能是yyy钓辆,那就要用到預(yù)搜索
js只支持先行預(yù)搜索剪验,也就是xxx前面必須是yyy,或者xxx前面不能是yyy
(?=1)2 //可以匹配12前联,不能匹配22
(?!1)2 //可有匹配22功戚,不能匹配12
修飾符
默認(rèn)正則是區(qū)分大小寫,這可能并不是我們想要的似嗤,正則提供了修飾符的功能啸臀,修復(fù)的語法如下
/xxx/gi //最后面的g和i就是兩個(gè)修飾符
g正則遇到第一個(gè)匹配的字符就會(huì)結(jié)束,加上全局修復(fù)符双谆,可以讓其匹配到結(jié)束
i正則默認(rèn)是區(qū)分大小寫的壳咕,i可以忽略大小寫
m正則默認(rèn)遇到換行符就結(jié)束了,不能匹配多行文本顽馋,m可以讓其匹配多行文本
主要方法
re.compile方法
compile 函數(shù)用于編譯正則表達(dá)式谓厘,生成一個(gè)正則表達(dá)式( Pattern )對象,供 match() 和 search() 這兩個(gè)函數(shù)使用寸谜。
語法:
re.compile(pattern[, flags])
pattern :一個(gè)字符串形式的正則表達(dá)式
flags :可選竟稳,表示匹配模式,比如忽略大小寫熊痴,多行模式等他爸,具體參數(shù)為:
re.I忽略大小寫
re.L表示特殊字符集 \w, \W, \b, \B, \s, \S依賴于當(dāng)前環(huán)境
re.M多行模式
re.S即為 .并且包括換行符在內(nèi)的任意字符(.不包括換行符)
re.U表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S依賴于 Unicode字符屬性數(shù)據(jù)庫
re.X為了增加可讀性,忽略空格和 # 后面的注釋
上述flags re.I和re.M是非常常用的果善。如果要同時(shí)使用兩個(gè)flags诊笤,可以使用re.I | re.M。
例子:
我們編寫了一個(gè)電子郵箱的正則表達(dá)式巾陕,并用它來驗(yàn)證用戶輸入的郵箱是否有效讨跟。
email_pattern = re.compile(r'^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}$')
print(re.match(email_pattern, 'django@pyghon.org'))
print(re.match(email_pattern, '1009070053@qq.com'))
<_sre.SRE_Match object; span=(0, 17), match='django@pyghon.org'><_sre.SRE_Match object; span=(0, 17), match='1009070053@qq.com'>
re.match和re.search方法
re.match和re.search方法類似纪他,唯一不同的是re.match從頭匹配,re.search可以從字符串中任一位置匹配晾匠。如果有匹配對象match返回茶袒,可以使用match.group()提取匹配字符串。
語法:
re.match(pattern, string)
re.search(pattern, string)
pattern :一個(gè)字符串形式的正則表達(dá)式
string :要匹配的字符串
例子:
編寫了一個(gè)年份的正則表達(dá)式
import re
year_pattern = re.compile(r'\d{4}')# 四位整數(shù)凉馆,匹配年份
string1 ='我愛1998和1999年'
match1 = re.match(year_pattern, string1)
print(match1)
match2 = re.search(year_pattern, string1)
print(match2)
print(match2.group())
None?
<_sre.SRE_Match object; span=(2, 6), match='1998'>?
1998?
你可以看到re.match沒有任何匹配薪寓,而re.search也只是匹配到1998年,而沒有匹配到1999年澜共。這是為什么呢向叉?
re.match是從頭匹配的,從頭沒有符合正則表達(dá)式咳胃,就返回None植康。
re.search方法雖然可以從字符串任何位置開始搜索匹配,但一旦找到第一個(gè)匹配對象展懈,其就停止工作了。
如何從"Elephants are bigger than rats"里提取Elephants和bigger兩個(gè)單詞
import re
string3 ="Elephants are bigger than rats";
match3 = re.search(r'(.*) are (.*?) .*', string3, re.M|re.I)
print(match3.group())
print(match3.group(1))
print(match3.group(2))
Elephants are bigger than rats
Elephants?
bigger?
match.group的編號是從1開始的供璧,而不是像列表一樣從0開始存崖。
有的符號如", ', )本身就有特殊的含義,我們在正則表達(dá)中使用時(shí)必需先對它們進(jìn)行轉(zhuǎn)義睡毒,方法就是在其符號前件反斜杠\来惧。
下例展示了我們?nèi)绾螐摹翱偣矘菍?共7層)"提取共7層三個(gè)字,我們需要給括號轉(zhuǎn)義演顾。
import re
string4 ="總共樓層(共7層)"
pattern5 = re.compile(r'\(.*\)')
match5 = re.search(pattern5, string4)
print(match5.group())
pattern6 = re.compile(r'\((.*)\)')
match6 = re.search(pattern6, string4)
print(match6.group())
print(match6.group(1))
(共7層)
(共7層)?
共7層??
我們pattern5和pattern6中都對外面雙括號都加了反斜杠\供搀,表明這是括號符號本身。在pattern6中我們還使用了一對沒加反斜杠的括號钠至,表明這是一個(gè)match group葛虐。
如果我們有”總共樓層(共7層)干擾)樓層"這樣的字符串,加了個(gè)干擾問號棉钧,那我們該如何匹配(共7層)呢屿脐?
import re
string10 ="總共樓層(共7層)干擾)問號"
pattern10 = re.compile(r'\(.*\)')# 默認(rèn)貪婪模式
pattern11 = re.compile(r'\(.*?\)')# 加問號?變非貪婪模式
print(re.search(pattern10, string10).group())
print(re.search(pattern11, string10).group())
(共7層)干擾)
(共7層)
Python里正則匹配默認(rèn)是貪婪的,總是嘗試匹配盡可能多的字符宪卿。
非貪婪的則相反的诵,總是嘗試匹配盡可能少的字符。如果要使用非貪婪模式佑钾,我們需要在., *, ?號后面再加個(gè)問號?即可西疤。
re.findall方法
試圖從一個(gè)字符串中提取所有符合正則表達(dá)式的字符串列表時(shí)需要使用re.findall方法。
findall方法使用方法有兩種休溶,一種是pattern.findall(string) 代赁,另一種是re.findall(pattern, string)扰她。
re.findall方法經(jīng)常用于從爬蟲爬來的文本中提取有用信息。
語法:
pattern.findall(string)
re.findall(pattern, string)
pattern : 一個(gè)字符串形式的正則表達(dá)式
string : 要匹配的字符串
例子:
提取年份列表
import re
year_pattern = re.compile(r'\d{4}')# 四位整數(shù)管跺,匹配年份
string1 ='我愛1998和1999年'
print(year_pattern.findall(string1))
['1998', '1999']
提取百度首頁帶有鏈接的關(guān)鍵詞
import re
import requests
response = requests.get('https://www.baidu.com')
response.encoding="utf-8"
urls = re.findall(r'<a.*>(.*)</a>', response.text,)# 獲取帶鏈接的關(guān)鍵詞
for urlin urls:
print(url)
登錄
意見反饋
re.sub方法
該方法經(jīng)常用于去除空格义黎,無關(guān)字符或隱藏敏感字符。
語法:
re.sub(pattern, new_string, current_string)
pattern :一個(gè)字符串形式的正則表達(dá)式
new_string :要替換的字符串
current_string :要匹配的字符串
實(shí)例:
如何把年份替換為****
import re
year_pattern = re.compile(r'\d{4}')# 四位整數(shù)豁跑,匹配年份
string1 ='我愛1998和1999年'
replaced_str = re.sub(year_pattern, '****', string1)
print(replaced_str)
我愛****和****年
re.split方法
split方法用于分割字符串廉涕,但是并不完美
語法:
re.split(pattern, string)
pattern :一個(gè)字符串形式的正則表達(dá)式
string :要匹配的字符串
例子:
分割后的字符串成列表
import re
string1 ="1cat2dogs3cats4"
list1 = re.split(r'\d+', string1)
print(list1)
['', 'cat', 'dogs', 'cats', '']
列表首尾都多了空格,需要手動(dòng)去除艇拍。