文章目錄
建議第一節閱讀,到第二節可以對照第一節查看使用
一、re模塊
(一)關於正則表達式介紹
- 1.數據抓取,數據提取,如:抓取網站中的圖片地址
- 2.數據清洗:把不雅的評論清洗掉
- 3.數據驗證:驗證手機號的合法性
正則表達式:是一種字符串的查詢和匹配規則,按照提供的規則完成目標字符串中的數據的查詢和檢索。
50年代,神經學、精神學兩位科學家嘗試將大腦中認知事物的方式描述出來,最終沒 有成功。70 年代數學家看到了兩位科學家筆記/筆錄,聯合另外一位生物學家一起將人類大腦認知事物的方式,使用數學表達式第一次簡單的描述了出來,並沒有出現實際的應用價值。肯·湯普森(Unix 之父):學校實驗室打工,自己想玩遊戲,搭建遊戲平臺→ 研發第一代的Unix 操作系統,在操作系統中爲了能快速的檢索字符串信息,研究各種文獻發現了數學家發表的論文,將正確規則描述事物的方式-正則表達式-在 Unix操作系統中第一次實現了出來並且取得了非常好的效果。
正則表達式在發展過程中,晨露崢嶸,被很多編程語言所接納,各種編程語言在構建語法規則的過程中,紛紛表示支持正則表達式的操作語法。python:支持正則表達式語法操作的模塊:re
項目需求:錄入資料--手機號碼
要求:長度11位、都是數字、156|188開頭
比較傳統解決思路,和使用正則表達的思路,發現,正則表達代碼優化更好!
① 傳統代碼的驗證方式
phone = input("請輸入您的手機號碼:")
def common_verify(phone):
# 判斷長度
if len(phone) == 11:
# 判斷開頭
if phone.startswith("156") or phone.startswith("188"):
# 判斷數字
for x in phone:
if not x.isdigit(): # 是數字
break
else:
# 合法手機號碼
input("這是一個合法的手機號碼")
return True
# 非法手機號碼
input("這是一個非法的手機號碼")
return False
print(common_verify(phone))
輸入正確的結果圖:
錯誤的結果:
② 正則表達式的驗證方式
import re
phone = input("請輸入您的手機號碼:")
def re_verify(phone):
"""驗證手機號碼是否合法的函數"""
# 定義一個正則表達式
vp = r"(156|188)\d{8}"
return True if len(re.findall(vp, phone)) > 0 else False
re_verify(phone)
(二)Python中操作正則表達式
Python 提供的正則表達式處理模塊 re,提供了各種正則表達式的處理函數
● 使用過程 【不一定是這種結構,以最簡單說明】:
1.導入模塊 import re
2.匹配,ret = re.match("正則 表達式公式", "要匹配的字符串")
3.判斷是否成功
ret: 不爲空,表示匹配成功,返回match對象
爲空,匹配失敗
4.取匹配結果 ret。group()
注意:re.match():匹配xxx開頭的字符串
● 對應代碼說明:
# 匹配 "csdn"
import re
ret = re.match("csdn","csdn.com") # 從頭開始匹配
if ret:
print(ret.group())
print(ret)
else:
print("匹配成功")
在結果此處,可以看到有一個"CSDN",這是ret.group()直接查看結果的,而剩下哪行則是對象
(三)字符查詢匹配函數
官方 re.py 可以查看註釋,本篇文章主要講解常用的,並且本文基本基於match函數講解
對於函數簡單的做一個說明
函數 | 說明 |
---|---|
re.match(reg,info) | 用於在 開始位置[從左往右] 匹配目標字符串info,符合正則表達式reg,會返回一個match對象,匹配不成功返回None |
re.search(reg,info) ) | 掃描整個字符串info,符合正則表達式reg,返回一個match對象,否則None |
re.findall(reg,info) | 掃描整個字符串info,符合正則表達式reg字符串, 提取出來,存放到列表中 |
re.fullmatch(reg,info) | 掃描整個字符串,字符串在正則表達式reg限定範圍,返回整個字符串,否則None |
re.finditer(reg,info) | 掃描整個字符串,將匹配到的字符保存在一個可以遍歷的列表中 |
(四)字符串拆分替換函數
函數 | 說明 |
---|---|
re.split(reg,string) | 正則表達式reg,將字符串string 才分成一個字符串列表,如:re.split(r"\s+",string),表示使用一個或者多個空白字符對字符串info進行拆分,並返回一個拆分後的字符串列表 |
re.sub(reg,repl,string) | 使用指定的字符串repl來替換字符串string中匹配的正則表達式reg的字符 |
(五)正則表達式元字符
使用正則表達式時,用到了一些包含特殊含義的字符,用於表示字符串中一些特殊的位置, 非常重要,我們先簡單瞭解一下一些常用的元字符
元字符 | 描述 |
---|---|
^ | 表示匹配字符串的開頭位置 在 [ ] 中使用代表取反 |
$ | 表示匹配字符串的結束位置的字符 |
. | 表示匹配任意一個字符 |
\d | 匹配一個數字字符 |
\D | 匹配一個非數字字符 |
\s | 匹配一個空白字符 |
\S | 匹配一個非空白字符 |
\w | 匹配一個數字/字母/下劃線中任意一個字符 |
\W | 匹配一個非數字字母下劃線的任意一個字符 Python3 中文也算 |
\b | 匹配一個單詞的邊界 |
\B | 匹配不是單詞的開頭或者結束位置 |
(六)正則表達式中的量詞
量詞,用於限定字符出現數量的關鍵字
量詞 | 描述 |
---|---|
x* | 用於匹配符號*前面的字符出現0次或者多次 |
x+ | 用於匹配符號+前面的字符出現1次或者多次 |
x? | 用於匹配符號?前面的字符出現0次或者1次 |
x{n} | 用於匹配符號{n}前面的字符出現n次 |
x{m,n} | 用於匹配符號{m,n} 前面的字符出現至少m次,最多n次 |
x{n,} | 用於匹配符號{n,}前面的字符出現至少n次 |
(七)正則表達式範圍匹配
在正則表達式中,針對字符的匹配,除了快捷的元字符的匹配,還有另一種使用方括號進行 的範圍匹配方式,具體如下:
範圍 | 說明 |
---|---|
[0-9] | 用於匹配一個0~9之間的數字,等價於\d |
[^0-9] | 用於匹配一個非數字字符,等價於\D |
[3-6] | 用於匹配一個3~6之間的數字 |
[a-z] | 用於匹配一個a~z之間的字母 |
[A-Z] | 用於匹配一個A~Z之間的字母 |
[a-f] | 用於匹配一個a~f之間的字母 |
[a-zA-Z] | 用於匹配一個a-z或者A-Z之間的字母,匹配任意一個字母 |
[a-zA-Z0-9] | 用於匹配一個字母或者數字 |
[a-zA-Z0-9] | 用於匹配一個字母或者數字或者下劃線,等價於\w |
[^a-zA-Z0-9] | 用於匹配一個非字母或者數字或者喜愛劃線,等價於\w |
(八)正則表達式分組
正則表達式主要是用於進行字符串檢索匹配操作的利器,在一次完整的匹配過程中,可以將 匹配到的結果進行分組,這樣就更加的細化了我們對匹配結果的操作,正則表達式通過圓括 號()進行分組,以提取匹配結果的部分結果。常用的兩種分組:
分組 | 描述 |
---|---|
(expression) | 使用圓括號直接分組;正則表達式本身匹配的結果就是一個組,可以通過group()或者group(0)獲取;然後正則表達式中包含的圓括號就是按照順序從1開始編號的小組 |
(?Pexpression) | 使用圓括號分組,然後給當前的圓括號表示的小組命名爲name,可以通過group(name)進行數據的獲取 |
(九)貪婪模式和懶惰模式
貪婪模式:從目標字符串的兩頭開始搜索,一次儘可能多的匹配符合條件的字符串,但是有 可能會匹配到不需要的內容,正則表達式中的元字符、量詞、範圍等都模式是貪婪匹配模式, 使用的時候一定要注意分析結果:
`<div>.*</div>就是一個貪婪模式,用於匹配<div>和 </div>之間所有的字符
懶惰模式: 從目標字符串按照順序從頭到位進行檢索匹配,儘可能的檢索到最小範圍的匹配 結果,語法結構是在貪婪模式的表達式後面加上一個符號?即可,
如<div>.*?</div>就是一個 懶惰模式的正則,用於僅僅匹配最小範圍的<div>和</div>之間的內容
二、正則表達常用案例
1、正則表達基本使用
# 匹配 "csdn"
import re
ret = re.match("csdn","csdn.com") # 從頭開始匹配
if ret:
print(ret.group())
print(ret)
else:
print("匹配成功")
2、匹配單個字符
"""
常用的
"""
import re
# 1. “.” : 表示匹配任意一個字符,除了\n(換行符)以外
# 案例:看電影,輸入電影名稱:“速度與激情”時,“激”字符可以任意輸入,都算成功
# ret = re.match("速度與.情","速度與激情123123")
# print(ret)
# 2. [] : 匹配 “ []”列舉的字符
# 案例:看電影,輸入電影名稱:“速度與激情”時,“速度與激情1”時 和 “速度與激情2”都正確
# ret = re.match("速度與激情[1245678]","速度與激情1") # 成功
# ret = re.match("速度與激情[1245678]","速度與激情a") # None
# ret = re.match("速度與激情[1245678]","速度與激情10") # 需要完善
# ret = re.match("速度與激情[1-8]","速度與激情8") # 另外個寫法,成功 簡化
# “速度與激情6”不匹配
# ret = re.match("速度與激情[1-57-8]","速度與激情6") #None
# ret = re.match("速度與激情[a-z]","速度與激情a") #成功,如果是註冊也可以
# print(ret)
# 3. \d :匹配數字,0-9
# ret = re.match("速度與激情\d","速度與激情6") #成功
# 4. \D :匹配非數字
# ret = re.match("速度與激情\D","速度與激情6") #None
# ret = re.match("速度與激情\D","速度與激情a") #成功
# print(ret)
# 5. \s 匹配空格,包含空格和tab
# ret = re.match("速度與激情\s","速度與激情 ") #成功
# ret = re.match("速度與激情\s","速度與激情a") #None
# ret = re.match("速度與激情\s","速度與激情 a") #成功
# print(ret)
# 6. \S 匹配空格,只要不是空格都匹配
# ret = re.match("速度與激情\S","速度與激情a")#成功
# ret = re.match("速度與激情\S","速度與激情")#None
# print(ret)
# 7. \w 匹配單詞字符:包含(a-z,A-Z,0-9,_下劃線)
# 在python3 中,中文字符包含在單詞字符中
# ret = re.match("\w","1")#成功
# ret = re.match("\w","a")#成功
# ret = re.match("\w","_")#成功
# ret = re.match("\w","@")#None
# ret = re.match("\w","穩")#成功 !!
# print(ret)
# 8. \W 匹配非單詞字符
# ret = re.match("\W","@")#成功
# ret = re.match("\W","=")#成功
# ret = re.match("\W","張")#None
ret = re.match("\W","a")#None
print(ret)
3、匹配多個字符
import re
# 1.{m} : 匹配前一個字符出現m次
# 匹配合法手機號
# 規則 要求滿足11位,並且第一位是 1
# ret = re.match("\d{11}","12345687901")# 成功 判斷規則:數字+11位
# ret = re.match("1\d{10}","12345687901")# 成功 除了第一位,往後10位匹配
# ret = re.match("1\d{10}","42345687901")# None 開頭不爲1
# ret = re.match("1\d{10}","1234fg68791")# None 包含了字母
# ret = re.match("1\d{10}","12345687901asd")# 成功,10後 沒有參與匹配
# print(ret)
# 2.{m,n} : 匹配前一個字符出現m到n次
# 驗證電話號碼的合法性 如:010-1234567,0558-8080255
# 規則:區號3-4位數字,電話號是7-8位,中間用“ - ” 連接
# ret = re.match("\d{3,4}-\d{7,8}","010-12345678")#成功
# ret = re.match("\d{3,4}-\d{7,8}","0558-8080255")#成功
# ret = re.match("\d{3,4}-\d{7,8}","0558-8080255a")#成功
# print(ret)
# 3.?: 前一個字符出現0次或者1次,要麼出現1次,要麼出現0次
# 驗證電話號碼的合法性 如:010-1234567,0558-8080255
# 規則:區號3-4位數字,電話號是7-8位,中間用“ - ” ,“-”可有可無
# ret = re.match("\d{3,4}-\d{7,8}","0101234567")#None
# # ret = re.match("\d{3,4}-?\d{7,8}","0101234567")#成功
# ret = re.match("\d{3,4}-?\d{7,8}","010@1234567")#None
# print(ret)
# 4. * : 匹配前一個字符出現0次或多次
# 要求:把一個文本內容全部提取出來
# content = "life is short, I use Python !"
# ret = re.match(".",content) # 匹配的第一個
# ret = re.match(".*",content) # 匹配所有,只能匹配一行
# content = "life is short, I use \n Python !"
# ret = re.match(".*",content,re.S) # re.S 可以匹配多行
# ret = re.match(".*","") # 成功
# print(ret)
# 5. + :匹配前一個字符,出現1次或者多次,即至少出現1次
# ret = re.match("a+","aaaa")# 成功
ret = re.match("a+","")# None
print(ret)
4、匹配開頭和結尾
import re
# 1. ^ :匹配開頭的字符串
# ret = re.match("csdn","csdn.com") # 成功
# ret = re.match("csdn","www.csdn.com") # None
# ret = re.search("csdn","www.csdn.com") # 成功
# ret = re.search("^csdn","www.csdn.com") # None
# print(ret)
# 注意用在[]表示取反
# ret = re.match("速度與激情[^6]","速度與激情9") # 成功
# ret = re.match("速度與激情[1-57-9]","速度與激情9") # 成功
# print(ret)
# 2. $ :匹配以XXX結尾的字符串
# 匹配出合法的手機號
# 規則:11位數字 且第一位是1
# ret = re.match("1\d{10}","12345678901") # 成功
# ret = re.match("1\d{10}","12345678901abc") # 成功 後面字母沒有匹配,不符合
ret = re.match("1\d{10}$","12345678901abc") # None
print(ret)
5、案例操作[ 匹配爬取郵箱,合法名判斷]
import re
# 1、匹配出合法的變量名
# 匹配規則:字符由數字,字母,下劃線組成,首字符不能是數字
names = ["age1","1age","AGO_","_9age","age_","a9#_2","a#ge"]
for i in names:
ret = re.match("[a-zA-Z_]\w*",i) #
if ret :
print(f"{i}是合法的")
else:
print(f"{i}不合法")
# 2、匹配合法的163郵箱
# 匹配規則:每個郵箱以“@163.com”結尾,@之前4-20單詞字符
# email_list = ["[email protected]","[email protected]","[email protected]","[email protected]","liang@163ccom"]
# for a in email_list:
# ret = re.match("\w{4,20}@163\.com$",a)
# if ret:
# print(f"{a}是合法的")
# else:
# print(f"{a}不合法")
6、匹配分組
import re
# 1. | : 匹配左右任意一個表達式 管道符
# email_list = ["[email protected]","[email protected]","[email protected]","[email protected]","liang@163ccom"]
# for a in email_list:
# ret = re.match("\w{4,20}@163\.com$|\w{4,20}@126\.com|\w{4,20}@qq\.com",a)
# if ret:
# print(f"{a}是合法的")
# else:
# print(f"{a}不合法")
# 2、 (ab):分組
# A、引入
email = "[email protected]"
ret = re.match("(\w{4,20})@qq\.com",email)
print(ret.group())
print(ret.group(1))
# email_list = ["[email protected]","[email protected]","[email protected]","[email protected]","liang@163ccom"]
# for a in email_list:
# ret = re.match("\w{4,20}@(163|qq|126)\.com$",a)
# if ret:
# print(f"{a}是合法的")
# else:
# print(f"{a}不合法")
# 3、引用分組 \num: 引用分組匹配到的字符串
# 檢查html網頁的合法性
# 語法規則:標籤必須配對
# content = "<h1>hello world</h1>" # 前後配對 合法
# content = "<h1>hello world</h1>" # 前後不配對也合法
# ret = re.match("<\w+>.*</\w+>",content) # 不嚴謹的寫法
# ret = re.match("<(\w+)>.*</\\1>",content) # 嚴謹的寫法
# print(ret)
# 4、給分組起別名(?P<name>) 寫到前面
# 5、(?P=name):引用別名爲name的分組匹配到的字符串
# 檢查html網頁的合法性
# content = "<h1><body>hello world</body></h1>"
# # ret = re.match("<(\w+)><(\w+)>.*<(/\\2)></\\1>",content)
# ret = re.match("<(?P<a>\w+)><(?P<b>\w+)>.*</(?P=b)></(?P=a)>",content)
# print(ret)
7、案例操作 [ sub() search()findall()]
import re
# 1、search
# content = "閱讀次數88次,下載次數30次"
# ret = re.search("\d+",content)
# print(ret) # 只打印一個結果,並不能所有匹配
# # 2、findall(): 查找字符串中所有匹配的數據,返回的是列表
# # 匹配所有數字
content = "閱讀次數88次,下載次數30次"
ret = re.findall("\d+",content)
print(ret) # 打印列表 包含所有數字
# 3、sub() :替換,返回的是替換後的字符串
# 將所有次數歸0
# content = "閱讀次數88次,下載次數30次"
# ret = re.sub("\d+","0",content)
# print(ret) # 只打印一個結果,並不能所有匹配
8、貪婪和非貪婪
"""
Python 中默認是貪婪,總是嘗試儘可能多的匹配字符,非貪婪相反,總是儘可能少的匹配字符
"""
import re
# 1、Python 中默認是貪婪,總是嘗試儘可能多的匹配字符,非貪婪相反,總是儘可能少的匹配字符
# ret = re.match("\d{2,5}","12345")
# print(ret) # 匹配了5次
# 2、字符串前面加r“表示原生字符串”
# 3、re.complie(strPattern)
# 將字符串形式的正則表達式編譯成pattern對象
pattern = re.compile("\d{2,5}")
ret = pattern.match("12345")
print(ret)