總目錄:http://www.lxweimin.com/p/e406a9bc93a9
Python-爬蟲 - 子目錄:http://www.lxweimin.com/p/23cf57674bf1
文檔:https://docs.python.org/3/library/re.html
正則表達式語言相對小型和受限(功能有限),因此并非所有字符串處理都能用正則表達式完成。
當然也有些任務可以用正則表達式完成,不過最終表達式會變得異常復雜。碰到這些情形時,編寫Python 代碼進行處理可能反而更好;盡管 Python 代碼比一個精巧的正則表達式要慢些,但它更易理解。
常用方法
re.compile: 編譯一個正則表達式模式(pattern)
re.match: 從頭開始匹配, 使用group()方法可以獲取第一個匹配值
re.search: 用包含方式匹配,使用group()方法可以獲取第一個匹配值
re.findall: 用包含方式匹配,把所有匹配到的字符放到以列表中的元素返回多個匹配值
re.sub: 匹配字符并替換
re.split: 以匹配到的字符當做列表分隔符,返回列表
基本模式
簡單字符
沒有特殊意義的字符都是簡單字符,簡單字符就代表自身,絕大部分字符都是簡單字符,舉個例子
/abc/ //匹配 abc
/123/ //匹配 123
/-_-/ //匹配 -_-
轉義字符
第一種,是為了匹配不方便顯示的特殊字符,比如換行,tab符號等
第二種,正則中預先定義了一些代表特殊意義的字符,比如\w等
第三種,在正則中某些字符有特殊含義(比如下面說到的),轉義字符可以讓其顯示自身的含義
字符集合
有時我們需要匹配一類字符,字符集可以實現這個功能,字符集的語法用[]分隔,下面的代碼能夠匹配a或b或c
[abc]
如果要表示字符很多,可以使用-表示一個范圍內的字符,下面兩個功能相同
[0123456789]
[0-9]
在前面添加^,可表示非的意思,下面的代碼能夠匹配abc之外的任意字符
[^abc]
其實正則還內置了一些字符集,在上面的轉義字符有提到,下面給出內置字符集對應的自定義字符集
.匹配除了換行符(\n)以外的任意一個字符 = [^\n]
\w = [0-9a-Z_]
\W = [^0-9a-Z_]
\s = [ \t\n\v]
\S = [^ \t\n\v]
\d = [0-9]
\D = [^0-9]
量詞
如果我們有三個蘋果,我們可以說自己有個3個蘋果,也可以說有一個蘋果,一個蘋果,一個蘋果,每種語言都有量詞的概念
如果需要匹配多次某個字符,正則也提供了量詞的功能,正則中的量詞有多個,如?、+、*、{n}、{m,n}、{m,}
{n}匹配n次,比如a{2},匹配aa
{m, n}匹配m-n次,優先匹配n次,比如a{1,3},可以匹配aaa、aa、a
{m,}匹配m-∞次,優先匹配∞次,比如a{1,},可以匹配aaaa...
?匹配0次或1次,優先匹配1次,相當于{0,1}
+匹配1-n次,優先匹配n次,相當于{1,}
*匹配0-n次,優先匹配n次,相當于{0,}
正則默認和人心一樣是貪婪的,也就是常說的貪婪模式,凡是表示范圍的量詞,都優先匹配上限而不是下限
a{1, 3} //匹配字符串'aaa'的話,會匹配aaa而不是a
有時候這不是我們想要的結果,可以在量詞后面加上?,就可以開啟非貪婪模式
a{1, 3}? //匹配字符串'aaa'的話,會匹配a而不是aaa
字符邊界
有時我們會有邊界的匹配要求,比如已xxx開頭,已xxx結尾
^在[]外表示匹配開頭的意思
^abc //可以匹配abc,但是不能匹配aabc
$表示匹配結尾的意思
abc$ //可以匹配abc,但是不能匹配abcc
上面提到的\b表示單詞的邊界
abc\b //可以匹配 abc,但是不能匹配 abcc
選擇表達式
有時我們想匹配x或者y,如果x和y是單個字符,可以使用字符集,[abc]可以匹配a或b或c,如果x和y是多個字符,字符集就無能為力了,此時就要用到分組
正則中用|來表示分組,a|b表示匹配a或者b的意思
123|456|789 //匹配 123 或 456 或 789
分組和引用
分組是正則中非常強大的一個功能,可以讓上面提到的量詞作用于一組字符,而非單個字符,分組的語法是圓括號包裹(xxx)
(abc){2}? ? ?//匹配abcabc
分組不能放在[]中,分組中還可以使用選擇表達式
(123|456){2}? ? ?//匹配 123123、456456、123456、456123
和分組相關的概念還有一個捕獲分組和非捕獲分組,分組默認都是捕獲的,在分組的(后面添加?:可以讓分組變為非捕獲分組,非捕獲分組可以提高性能和簡化邏輯
'123'.match(/(?123)/) //返回 ['123']
'123'.match(/(123)/)? //返回 ['123', '123']
和分組相關的另一個概念是引用,比如在匹配html標簽時,通常希望后面的xxx能夠和前面保持一致
引用的語法是\數字,數字代表引用前面第幾個捕獲分組,注意非捕獲分組不能被引用
<([a-z]+)><\/\1> //可以匹配 ``或 ``等
預搜索
如果你想匹配xxx前不能是yyy,或者xxx后不能是yyy,那就要用到預搜索
js只支持先行預搜索,也就是xxx前面必須是yyy,或者xxx前面不能是yyy
(?=1)2 //可以匹配12,不能匹配22
(?!1)2 //可有匹配22,不能匹配12
修飾符
默認正則是區分大小寫,這可能并不是我們想要的,正則提供了修飾符的功能,修復的語法如下
/xxx/gi //最后面的g和i就是兩個修飾符
g正則遇到第一個匹配的字符就會結束,加上全局修復符,可以讓其匹配到結束
i正則默認是區分大小寫的,i可以忽略大小寫
m正則默認遇到換行符就結束了,不能匹配多行文本,m可以讓其匹配多行文本
主要方法
re.compile方法
compile 函數用于編譯正則表達式,生成一個正則表達式( Pattern )對象,供 match() 和 search() 這兩個函數使用。
語法:
re.compile(pattern[, flags])
pattern :一個字符串形式的正則表達式
flags :可選,表示匹配模式,比如忽略大小寫,多行模式等,具體參數為:
re.I忽略大小寫
re.L表示特殊字符集 \w, \W, \b, \B, \s, \S依賴于當前環境
re.M多行模式
re.S即為 .并且包括換行符在內的任意字符(.不包括換行符)
re.U表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S依賴于 Unicode字符屬性數據庫
re.X為了增加可讀性,忽略空格和 # 后面的注釋
上述flags re.I和re.M是非常常用的。如果要同時使用兩個flags,可以使用re.I | re.M。
例子:
我們編寫了一個電子郵箱的正則表達式,并用它來驗證用戶輸入的郵箱是否有效。
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 :一個字符串形式的正則表達式
string :要匹配的字符串
例子:
編寫了一個年份的正則表達式
import re
year_pattern = re.compile(r'\d{4}')# 四位整數,匹配年份
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是從頭匹配的,從頭沒有符合正則表達式,就返回None。
re.search方法雖然可以從字符串任何位置開始搜索匹配,但一旦找到第一個匹配對象,其就停止工作了。
如何從"Elephants are bigger than rats"里提取Elephants和bigger兩個單詞
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開始。
有的符號如", ', )本身就有特殊的含義,我們在正則表達中使用時必需先對它們進行轉義,方法就是在其符號前件反斜杠\。
下例展示了我們如何從“總共樓層(共7層)"提取共7層三個字,我們需要給括號轉義。
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中我們還使用了一對沒加反斜杠的括號,表明這是一個match group。
如果我們有”總共樓層(共7層)干擾)樓層"這樣的字符串,加了個干擾問號,那我們該如何匹配(共7層)呢?
import re
string10 ="總共樓層(共7層)干擾)問號"
pattern10 = re.compile(r'\(.*\)')# 默認貪婪模式
pattern11 = re.compile(r'\(.*?\)')# 加問號?變非貪婪模式
print(re.search(pattern10, string10).group())
print(re.search(pattern11, string10).group())
(共7層)干擾)
(共7層)
Python里正則匹配默認是貪婪的,總是嘗試匹配盡可能多的字符。
非貪婪的則相反,總是嘗試匹配盡可能少的字符。如果要使用非貪婪模式,我們需要在., *, ?號后面再加個問號?即可。
re.findall方法
試圖從一個字符串中提取所有符合正則表達式的字符串列表時需要使用re.findall方法。
findall方法使用方法有兩種,一種是pattern.findall(string) ,另一種是re.findall(pattern, string)。
re.findall方法經常用于從爬蟲爬來的文本中提取有用信息。
語法:
pattern.findall(string)
re.findall(pattern, string)
pattern : 一個字符串形式的正則表達式
string : 要匹配的字符串
例子:
提取年份列表
import re
year_pattern = re.compile(r'\d{4}')# 四位整數,匹配年份
string1 ='我愛1998和1999年'
print(year_pattern.findall(string1))
['1998', '1999']
提取百度首頁帶有鏈接的關鍵詞
import re
import requests
response = requests.get('https://www.baidu.com')
response.encoding="utf-8"
urls = re.findall(r'<a.*>(.*)</a>', response.text,)# 獲取帶鏈接的關鍵詞
for urlin urls:
print(url)
登錄
意見反饋
re.sub方法
該方法經常用于去除空格,無關字符或隱藏敏感字符。
語法:
re.sub(pattern, new_string, current_string)
pattern :一個字符串形式的正則表達式
new_string :要替換的字符串
current_string :要匹配的字符串
實例:
如何把年份替換為****
import re
year_pattern = re.compile(r'\d{4}')# 四位整數,匹配年份
string1 ='我愛1998和1999年'
replaced_str = re.sub(year_pattern, '****', string1)
print(replaced_str)
我愛****和****年
re.split方法
split方法用于分割字符串,但是并不完美
語法:
re.split(pattern, string)
pattern :一個字符串形式的正則表達式
string :要匹配的字符串
例子:
分割后的字符串成列表
import re
string1 ="1cat2dogs3cats4"
list1 = re.split(r'\d+', string1)
print(list1)
['', 'cat', 'dogs', 'cats', '']
列表首尾都多了空格,需要手動去除。