python正則表達式與re模塊-02
- 2019 年 10 月 7 日
- 筆記
正則表達式
正則表達式與python的關係
# 正則表達式不是Python獨有的,它是一門獨立的技術,所有的編程語言都可以使用正則 # 但要在python中使用正則表達式,就必須依賴於python內置的re 模塊
驗證手機號是否合法的小案例
while True: phone_number = input('please input your phone number : ') if len(phone_number) == 11 and phone_number.isdigit() and (phone_number.startswith('13') or phone_number.startswith('14') or phone_number.startswith('15') or phone_number.startswith('18') or phone_number.startswith('19')): print('是合法的手機號碼') else: print('不是合法的手機號碼')
import re phone_number = input('please input your phone number : ') if re.match('^(13|14|15|18|19)[0-9]{9}$', phone_number): print('是合法的手機號碼') else: print('不是合法的手機號碼')
從上面兩段代碼中很容易就可以看得出來,使用正則表達式來校驗手機號明顯要比純python 代碼來的精簡得多。
下面就正式介紹一下正則表達式的一些基本知識
正則表達式
正則表達式: # 一種匹配字符串的規則
官方定義: # 正則表達式是對字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個「規則字符串」,這個「規則字符串」用來表達對字符串的一種過濾邏輯。
常見的應用場景: # 爬蟲 , # 數據分析
如果你想系統的學習,可以去了解一下《正則指引》這本書
在開始講正則語法之前,先推薦大家一個驗證正則的網站,可以在線測試你的正則表達式能否滿足你的預期效果:正則表達式在線測試 ,大家可以邊學習邊在該網站上面驗證

你改變正則表達式或者下方的待處理字符串他會自動重新匹配
字符組
# 在同一個位置可能出現的各種字符組成了一個字符組,在正則表達式中用[]表示
常見的字符組(一個字符組中的數據都是 '或' 的關係)

注意: # 字符組可以用 '-' 表示範圍要按照ASCII碼錶的順序書寫,可以 0-9 不可以 9-0,9的ASCII值比0大
字符
常見元字符(推薦按不同顏色來分組記)

注意: # ^ 與 & 會精準限制匹配的內容,兩者中間寫什麼,待匹配的字符串就必須是什麼,多一個少一個都不行 , # 運用 | (「或」)的時候一定要把長的寫在前面,否則匹配到短的就匹配完成了,會有很多被截斷的
分組(): # 當多個正則符號需要重複多次的時候,或者當做一個整體,進行其他操作,那麼可以用分組的形式,分組在正則的語法中就是一個小括號 '(表達式)' ,裏面放表達式即可
轉義字符
觀察上面的元字符可以發現其中有 n t 等代表特殊含義的元字符,如果就是要匹配一個字符串 'n',則會與元字符衝突,故需要在 '' 前面再加上一個 '',來防止轉義,即要表示字符串 'n' 正則中需要寫成 '\n'
量詞
只能跟在元字符/字符組/正則組後面,限制其左邊緊挨着的那個正則表達式(元字符/字符組/正則組),不可兩個量詞連在一起(除了 '?',解除正則的貪婪模式)
*、+、? 推薦如圖所示的畫坐標系的方式記憶

貪婪匹配與非貪婪匹配(惰性匹配)
貪婪匹配: # 在滿足匹配時,匹配儘可能長的字符串
非貪婪匹配: # 在滿足匹配時,匹配儘可能短的字符串
python的匹配模式默認為貪婪匹配,在量詞後面加上 ? 可以將其匹配模式改為非貪婪模式,會匹配盡量少的字符串(僅量詞作用的那個對象會受影響)
貪婪匹配原理個人理解: # 先匹配到目標字符串 '<',然後直接讀取到後面所有的字符串,從倒數第一個字符開始往回找,找到 '>',則將前面的那個位置至這個位置之間的數據返回 ,注意這裡是 .* 任意字符 無限多次!!!

非貪婪原理個人理解: # 先匹配到目標字符串 '<',從該位置開始往後尋找字符 '>',找到則這對數據為一個匹配返回結果 ,注意這裡是 .* 任意字符 無限多次!!!

案例練習
推薦案例 正則表達式練習題 ,可以根據案例鞏固知識
如果你看了本文覺得描述不清,可以參考 re模塊 這篇博客,亦或是下圖的出處的博客 python正則表達式指南 (前者的博客頁面排版看着美觀一些,但大部分數據均來源於後者)

python 中的re模塊
上面只是介紹了正則表達式的一些基礎知識,它是一門獨立的技術,要想在python中使用正則表達式,自然就需要通過學習python內置的re模塊了(也可以通過其他的一些函數方法等方式使用正則)
要使用 re模塊,請不要忘記先導模塊(import re)
本文此部分的參考性可能不高,內容也不是很全面,僅作為個人小結,如果有興趣可以參考 re模塊 裏面有更詳細的講解以及豐富的案例(另:本模塊知識很少用到,所以僅做筆記翻閱)
常見重點函數 findall 找出字符串所有符合內容成一個列表 search 查找匹配結果 match 從開頭開始比對
import re ''' findall 所有符合正則表達式的所有內容 search 有沒有符合正則表達式的內容 match 是不是正則表達式對應的開頭 ''' # findall 找出字符串中符合正則表達式的所有內容,並且返回一個列表,列表中的元素就是正則表達式匹配到的結果 res = re.findall('[a-z]+', 'nice toZmeet you') print(res) # ['nice', 'to', 'meet', 'you'] # search 不會直接返回匹配到的結果,而是給你返回一個對象,這個對象需要調用,通過調用group()方法得到匹配的字符串,如果字符串沒有匹配,則返回None。 # 注意:1.search 只會依據正則查找一次,只要查到了結果,就不會往後查找了 # 2.當查找的結果不存在的情況下,調用group會直接報錯 res = re.search('ou', 'nice toZmeet you') res2 = res.group() # 必須調用group 才能看到匹配到的結果 print(res, res2) # <_sre.SRE_Match object; span=(14, 16), match='ou'> ou res1 = re.search(r'.', 'nice toZmeet you') print(res1) # None # print(res1.group()) # 直接報錯,if空避免 # match # 注意:1.match 只會匹配字符串的開頭部分 # 2.當字符串的開頭與正則表達式不符合匹配規則的情況下,返回的也是None,調用 .group 也會報錯 res = re.match('s', 'sda e rf a f') print(res) # <_sre.SRE_Match object; span=(0, 1), match='s'> print(res.group()) # s ''' 正則表達式,返回類型為表達式對象的,如:<_sre.SRE_Match object; span=(6, 7), match='a'>,返回對象時,需要用正則方法取字符串,方法有: group() # 獲取匹配到的所有結果,不管有沒有分組將匹配到的全部拿出來,有參取匹配到的第幾個如2 groups() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果 groupdict() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果 '''
不常用函數 split 切割 sub 替換 compile 將正則編譯成一個對象,可對象. 調用上述方法 finditer 返回一個放返回結果的迭代器
import re # split 類似於字符串的切割split,返回一個列表(他會把被替換掉的字符變成空格) res = re.split('[ab]', 'sabcasbdsafafabfas') print(res) # ['s', '', 'c', 's', 'ds', 'f', 'f', '', 'f', 's'] # sub 類似於字符串的replace 方法,返回替換完成的字符串,可指定替換次數 # sub('正則表達式', '新的內容', '帶匹配的字符串', [要替換的次數]) # 先按正則表達式查找所有符合該表達式的內容,統一替換成'新的內容',還可以通過n來控制替換的個數 ret = re.sub('d', 'H', 'eva3egon4yuan4', 1) # 將數字替換成'H',參數1表示只替換1個 print(ret) # evaHegon4yuan4 # subn() 字符串replace的加強版,返回替換完成的字符串與總共替換的次數(封成了一個元組) # 返回的是一個元組,元組的第一個元素是替換完成後的結果,第二個元素代表的是替換的個數 ret = re.subn('d', 'H', 'eva3egon4yuan4') # 將數字替換成'H',返回元組(替換的結果,替換了多少次) print(ret) # ('evaHegonHyuanH', 3) # compile 將正則編譯成一個對象,後期可以直接用它來調用 findall、search 等方法 obj = re.compile('d{3}') # 將正則表達式編譯成為一個 正則表達式對象,規則要匹配的是3個數字 ret = obj.search('abc123eeee') # 正則表達式對象調用search,參數為待匹配的字符串 print(ret.group()) # 123 # finditer 返回一個存放匹配結果的迭代器,可以使用前面學習到的 迭代器對象.__next__() 方法調用 ---> 簡寫 next(迭代器對象) ret = re.finditer('d', 'ds3sy4784a') print(ret) # <callable_iterator object at 0x000001AE62617160> print(next(ret).group()) # 查看第一個結果 # 3 print(next(ret).group()) # 查看第二個結果 # 4 print([i.group() for i in ret]) # 查看剩餘的左右結果 # ['7', '8', '4']
擴展
各方法分組的區別
import re res = re.search('^[1-9]d{14}(d{2}[0-9x])?$', '110105199812067023') print(res.group()) # 110105199812067023 print(res.group(1)) # 獲取正則表達式括號闊起來分組的內容 # 023 # print(res.group(2)) # 報錯,取不到 search與match均支持獲取分組內容的操作 跟正則無關是python機制 # 而針對findall它沒有group取值的方法,所以它默認就是分組優先獲取的結果 ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com') print(ret) # 這是因為findall會優先把匹配結果組裡內容返回,如果想要匹配結果,取消權限即可 # ['oldboy'] ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') # ?:取消分組優先 print(ret) # ['www.oldboy.com']
import re ret = re.search("<(?P<tag_name>w+)>w+</(?P=tag_name)>", "<h1>hello</h1>") # 還可以在分組中利用?<name>的形式給分組起名字 # 獲取的匹配結果可以直接用group('名字')拿到對應的值 print(ret.group('tag_name')) # h1 print(ret.group()) # <h1>hello</h1> """ 注意 ?P=tag_name 相當於引用之前正則表達式,並且匹配到的值必須和前面的正則表達式一模一樣 """ # 匹配整數 ret = re.findall(r"d+", "1-2*(60+(-40.35/5)-(-4*3))") print(ret) # ['1', '2', '60', '40', '35', '5', '4', '3'] ret = re.findall(r"d+.d*|(d+)", "1-2*(60+(-40.35/5)-(-4*3))") print(ret) # ['1', '2', '60', '', '5', '4', '3'] ret.remove("") print(ret) # ['1', '2', '60', '5', '4', '3']
爬蟲小案例
''' 本爬蟲案例思路: 分析 https://movie.douban.com/top250?start=%0&filter= 頁面得知,每頁有25條數據 通過分次請求該地址,將返回的HTML代碼通過正則匹配,解析成想要的字符串格式, 分頁分條存入文件中去(用到了 分組 和 取別名 的知識點) ''' import re from urllib.request import urlopen def getPage(url): response = urlopen(url) return response.read().decode('utf-8') def parsePage(s): com = re.compile( '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)評價</span>', re.S) ret = com.finditer(s) for i in ret: yield { "id": i.group("id"), "title": i.group("title"), "rating_num": i.group("rating_num"), "comment_num": i.group("comment_num"), } def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num response_html = getPage(url) ret = parsePage(response_html) print(ret) f = open("move_info7.txt", "a", encoding="utf8") for obj in ret: print(obj) data = str(obj) f.write(data + "n") count = 0 for i in range(10): main(count) count += 25
數據小樣

上面僅僅只是一個簡單的爬蟲案例,如果你想成為一名爬蟲工程師,那麼你必須的能夠熟練地寫出正則表達式,言下之意就是除了本文的內容,你還需要好好去學學正則表達式的內容