正則表達式很難嗎?其實也就那樣!
- 2019 年 10 月 4 日
- 筆記
1

/ 寫在前面的話 /
Hello,元宵節過了,這個年也算是過完了,接下來就得看我們2019年的奮鬥了,2019年JAP君會一直陪著大家一起學習!今天我們來學習一下號稱最難學的正則表達式,正則表達式在我們寫爬蟲的時候確實是一個很好的幫手,因為有一些網站的數據可能並不是那麼的規整或者數據太多,我們只需要部分數據的時候,此時我們就可以通過一些表達式來進行提取,正則表達式就是其中一種進行數據篩選的表達式。
2

/ 正則表達式之「原子」 /
估計大家看到這個小標題就有點懵逼,原子?我點錯公眾號了嗎?當然你沒有點錯,這裡的「原子」,並非是物理中的原子,那是什麼?原子是正則表達式中最基本的組成單位,每一個正則表達式中至少要包含一個「原子」。
常見的原子類型有哪些?
1.普通字元作原子
2.非列印字元作原子
3.通用字元作為原子
4.原子表
1.普通字元作原子:
普通字元是編寫正則表達式時最常見的原子了,包括所有的大寫和小寫字母字元、所有數字等。例如,a—z、A—Z、0—9。
說明:search函數是re模組裡面的,第一個參數就是原子,第二個參數是需要解析的字元串。
s = "JAVAandPython" # 普通字元作為原子 pattern = "and" m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(4, 7), match='and'>
說明:re.research返回的是一個match對象
2.非列印字元作為原子:
非列印字元,是一些在字元串中的格式控制符號,例如空格、回車及製表符號等。例如下表所示列出了正則表達式中常用的非列印字元及其含義。

# 非列印字元作為原子 s = '''JAVAand Python ''' pattern = "n" m = re.search(pattern, s) print(m)
說明:這裡我們使用了三引號來進行換行書寫,s中是存在一個n的
輸出結果:
<_sre.SRE_Match object; span=(7, 8), match='n'>
3.通用字元作為原子
上面我們說的普通字元和非列印字元都是一個原子匹配一個字元,而通用字元是匹配一類字元,例如我們匹配數字時,不是匹配一個而是匹配這一類。
下面我給大家列舉出一些:
w:包含字母,數字,下劃線
W:除了字母,數字,下劃線之外的
d:十進位的數字
D:除十進位的數字
s:空白字元
S:除空白字元
# 通用字元作為原子 s = 'JAVAand666python' pattern = "dddw" m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(7, 11), match='666p'>
4.原子表:
使用原子表「[]」就可以定義一組彼此地位平等的原子,且從原子表中僅選擇一個原子進行匹配。
# 原子表 s = 'JAVAand666python' pattern = "and[1234567]" m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(4, 8), match='and6'>
其實質就是從[]中選取符合原字元串的元素來進行匹配。
3

/ 正則表達式之「元字元」/
what is 元字元?所謂的元字元就是正則表達式中一些含有特殊意義的字元,如下表:

是不是看了之後還是有點懵逼?接下來,我給大家用程式碼一一實現一下
1. 「 . 」 除了換行符外的任意一個字元
# 元字元 . 除了換行外任意一個字元 s = 'JAVAand666python' pattern = "JAVA." m = re.search(pattern, s) print(m) pattern = "JAVA..." m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(0, 5), match='JAVAa'> <_sre.SRE_Match object; span=(0, 7), match='JAVAand'>
2. ^ 匹配輸入字元串的開始位置
s = 'JAVAand666python' pattern = "^JAVA." m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(0, 5), match='JAVAa'>
3. $ 輸入字元串的結束位置
s = 'JAVAand666python' pattern = ".python$" m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(9, 16), match='6python'>
4. * 匹配前子表達式的任意次
+ 匹配前子表達式的1次或者多次
? 匹配前子表達式的0次或者1次
s = 'JAVAand666python' pattern = "JAVA.*" m = re.search(pattern, s) print(m) s = 'JAVAand666python' pattern = "JAVA.+" m = re.search(pattern, s) print(m) s = 'JAVAand666python' pattern = "JAVA.?" m = re.search(pattern, s) print(m)
輸出結果:
<_sre.SRE_Match object; span=(0, 16), match='JAVAand666python'> <_sre.SRE_Match object; span=(0, 16), match='JAVAand666python'> <_sre.SRE_Match object; span=(0, 5), match='JAVAa'>
5.{n} 匹配前子表達式恰好出現n次
{n,} 匹配前子表達式至少出現n次
{n,m} 匹配前子表達式至少出現n次,至多出現m次
s = 'JAVAanddddddd666python' pattern = "JAVAand{3}" m = re.search(pattern, s) print(m) s = 'JAVAanddddddd666python' pattern = "JAVAand{3,}" m = re.search(pattern, s) print(m) s = 'JAVAanddddddd666python' pattern = "JAVAand{3,5}" m = re.search(pattern, s) print(m)輸出結果:
<_sre.SRE_Match object; span=(0, 9), match='JAVAanddd'> <_sre.SRE_Match object; span=(0, 13), match='JAVAanddddddd'> <_sre.SRE_Match object; span=(0, 11), match='JAVAanddddd'>
OK,上面幾個是比較常見的元字元。
4

/ 正則表達式之「模式修正符」 /
雖然都說正則很難,其實基礎上也不是特別難,只是大部分的東西需要死記硬背,接下來我們來扯一下模式修正符,這個非常簡單,就幾個簡單的字母標點,但是也是需要我們記住的。
首先還是跟大家講下模式修正符是個啥,它就是通過一些特定的符號去改正正則表達式的含義,從而達到一些特定的效果而且我沒進行模式修正是不要去改變正則表達式的。
下表就是一些模式修正符:

這裡面比較重要的就是 I M S ,下面我就簡單給大家用程式碼展示一個:
import re s = "JAVAandPython666666" pattern = 'java' # 這裡的第三個參數就是調用了我們的模式修正符,其他的也是一樣的使用 m = re.search(pattern, s, re.I) print(m)
輸出結果:
<_sre.SRE_Match object; span=(0, 4), match='JAVA'>
從上面的程式碼和輸出結果可以看出我們通過re.I 忽略掉了大小寫。
再來看一個:
s = '''123java asdasd ''' pattern = '123.+' # 這裡的第三個參數就是調用了我們的模式修正符,其他的也是一樣的使用 m = re.search(pattern, s, re.S) print(m)
輸出結果:
<_sre.SRE_Match object; span=(0, 15), match='123javanasdasdn'>
從結果中,我們可以看到它把n也列印出來了。
5

/ 貪婪模式和懶惰模式 /
其實從字面意思上就可以看出,「貪婪模式」就是儘可能多的匹配,「懶惰模式」就是儘可能少的匹配。可能大家還是不知道這是個啥,還是直接上程式碼吧。
我們首先看一下貪婪模式:
s = '123JAVAandpypyPython' pattern = '123.*py' m = re.search(pattern, s, re.I) print(m)
我們來看看輸出結果:
<_sre.SRE_Match object; span=(0, 16), match='123JAVAandpypyPy'>
我們從程式碼和輸出結果分析,可以看到它直到最後一個py才停止
我們再來看一下懶惰模式:
s = '123JAVAandpypyPython' pattern = '123.*?py' m = re.search(pattern, s, re.I) print(m)
輸出結果:
<_sre.SRE_Match object; span=(0, 12), match='123JAVAandpy'>
可以看到它匹配到第一個py時就停止了
總結:其實大家都可以發現,懶惰模式和貪婪模式就是一個問號的差別,貪婪模式是「.*」,懶惰模式是「.*?」。貪婪模式就是會一直「吃」到底,懶惰模式就是「吃」到第一個就不吃了,按照這樣分析,可以發現貪婪模式所得到的結果是比較模糊的而懶惰模式得到的結果更加的精確。
6

/ 正則表達式的函數 /
接下來我們來看一下正則中的函數,這個是十分重要的。我就直接上程式碼。
import re # 以下正則分成了兩個小組,以小括弧為單位 s = r'([a-z]+) ([a-z]+) ([a-z]+) ([a-z]+)' # s.I表示忽略大小寫 pattern = re.compile(s, re.I) # match -從頭開始匹配,但是search可以從任意處匹配 m = pattern.match("Hello World Kuls yes qweqwe") # group(0)表示返回匹配成功的整個子串 s = m.group(0) print(s) # span(0)返回的是匹配成功的整個子串的跨度 a = m.span(0) print(a) # group(1)表示第一個匹配成功的子串 s = m.group(1) print(s) # span(1)表示第一個匹配成功的子串跨度 a = m.span(1) print(a) # groups()等價於group(1),group(2) s = m.groups() print(s)其實我在程式碼裡面給大家講解了每一個函數的作用,但是我還是把它扯出來吧,防止大家沒看到。
match -從頭開始匹配,但是search可以從任意處匹配
group(0)表示返回匹配成功的整個子串
span(0)返回的是匹配成功的整個子串的跨度
group(1)表示第一個匹配成功的子串
span(1)表示第一個匹配成功的子串跨度
groups()等價於group(1),group(2)
整個輸出的結果也給大家看看:
Hello World Kuls yes (0, 20) Hello (0, 5) ('Hello', 'World', 'Kuls', 'yes')另外還有兩個比較重要的函數findall和finditer,兩者相比,findall是最常用的。
import re s = r'd+' # 全局匹配 pattern = re.compile(s) m = pattern.findall("i am 18 years old and 185 high") print(m) # finditer m = pattern.finditer("i am 18 years old and 185 high") for i in m: print(i.group())輸出結果:
['18', '185'] 18 185
可以看到findall返回的是一個list列表
7

/ 一些常用的正則實例 /
這裡給大家準備了一些經常用的正則實例,大家可以收藏收藏。
Email地址:^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL:[a-zA-z]+://[^s]* 或 ^http://([w-]+.)+[w-]+(/[w-./?%&=]*)?$
手機號碼:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])d{8}$
中國電話號碼(0511-4405222、021-87888822):d{3}-d{8}|d{4}-d{7}
身份證號(15位、18位數字):^d{15}|d{18}$
IP地址:d+.d+.d+.d+ (提取IP地址時有用)
中文字元的正則表達式:[u4e00-u9fa5]
• end •