2020年9月12日星期六

58 《零基礎入門學習Python》筆記 第058講:論一隻爬蟲的自我修養6:正則表達式2

《零基礎入門學習Python》筆記   第058講:論一隻爬蟲的自我修養6:正則表達式2


上一節課我們通過一個例子(匹配ip 地址)讓大家初步了解到正則表達式的魔力,也讓大家充分了解到學習正則表達式是一個相對比較困難的事情。所以這一節課我們將繼續學習正則表達式的語法。
我們依稀還記得在Python中,正則表達式是以字符串的形式來描述的,正則表達式的強大之處在於特殊符號的應用,我們上節課舉了例子,例如點號(.),在正則表達式中表示匹配除了換行符之外的任何字符,它就是一個特殊的字符。正是這些特殊符號,使得一個正則表達式可以匹配一個複雜的規則,而不僅僅是匹配一個字符串,如果你只需要匹配一個字符串,那用find() 方法就可以了。
我這裡給大家準備了一個列表,Python3正則表達式特殊符號及用法(詳細列表)
這里羅列了python3 所支持的所有正則表達式的特殊符號以及具體的含義,在難以理解的地方,用斜體舉了例子給大家看,方便大家理解,大家可以將這個文章收藏起來,以後用到的時候查詢就可以了,切記不要死記硬背,因為根本背不住,如果背錯了更扎心。
大家看到這裡,可能就會犯嘀咕了:“好歹我也是見過世面的人啊,為了查找一個字符串,有必要掌握這麼多規則嗎?”。
實話說,沒必要。我這裡只是給大家做一個總結,我們正常使用的情況下,只是使用這裡的一小部分,另外一大部分只是為了應對突發情況而準備的。例如某一天,你心血來潮,想為你的規則再增加複雜一點的規則,那麼這裡邊正則表達式就可以應付自如了。一定要記住的是,這裡面的東西不要去背,多做練習才是重要的啊。你掌握的東西才是你的,背下來的東西過兩天就不是你的了。
特殊符號是由兩部分組成的,一部分是元字符。(例如我們上節課講的點號(.),方括號([]),反斜杠(\)等)。
所有的元字符包括:. ^ $ * + ? { } [ ] \ | ( )  
另一部分就是反斜杠加上普通符號組成的特殊符號,它擁有特殊的含義。
首先來談談元字符:
點號(.):是匹配除了換行符之外的任何字符。
| :就相當於邏輯或,學過C語言的同學們都知道,這就是按位或。A|B 表示匹配正則表達式A或者B
例如:
  1. >>> import re
  2. >>> re.search(r"Python(E|F)", "PythonE")
  3. <_sre.SRE_Match object; span=(0, 7), match='PythonE'>
  4. >>> re.search(r"Python(E|F)", "PythonF")
  5. <_sre.SRE_Match object; span=(0, 7), match='PythonF'>
  6. >>> re.search(r"Python(E|F)", "PythonD")
  7. >>>
托字符(^):定位匹配,匹配字符串的開始位置(即確定一個位置)。
例如:
  1. >>> re.search(r"^Python", "I love Python")
  2. >>>
  3. >>> re.search(r"^Python", "Python, I love")
  4. <_sre.SRE_Match object; span=(0, 6), match='Python'>
跟托字符(^)對應的就是美元符號($),$ 匹配輸入字符串的結束位置.
  1. >>> re.search(r"Python$", "Python, I love")
  2. >>>
  3. >>> re.search(r"Python$", "I love Python")
  4. <_sre.SRE_Match object; span=(7, 13), match='Python'>
我們剛剛提到了值組,就是用小括號括起來的,我們上節課也用過了,用小括號括起來跟數學的括號是一樣的,把一個東西當做一個整體,那麼就把它括起來。
接下來是史上最困難、最複雜的反斜杠(\),反斜杠在正則表達式中應用是最廣泛的,它既可以將一個普通的字符變為特殊字符(這部分內容下節課繼續講解),同時也可以解除元字符的特殊功能,這在上節課已經講過,例如\. 匹配的就不是除換行符之外的任何字符了,他匹配的就是一個點(.)了。
如果在反斜杠後面加的是數字,那麼還有兩種表示方案:
①如果跟著的數字是1~99,就表示引用序號對應的值組所匹配的字符串,其中序號所對應的值組:為\ 前面的值組,\序號必須在對應的值組的正後面,序號為第幾個值組。
例如:
  1. >>> re.search(r"(Python)\1", "I love Python")
  2. >>>
  3. >>> re.search(r"(Python)\1", "I love PythonPython")
  4. <_sre.SRE_Match object; span=(7, 19), match='PythonPython'>
上面的(Python)是第一個值組(序號是從1開始計算的,因為0表示一個八進制數),所以\1,且\1表示Python,其實r'(Python)\1'就等於' PythonPython'。
  1. >>> re.search(r"(love)(Python)\1", "I lovelovePythonPython") #这样\1 是找不到 (love)的,\序号必须在对应值组的正后方
  2. >>>
  3. >>> re.search(r"(love)\1(Python)", "I lovelovePythonPython")
  4. <_sre.SRE_Match object; span=(2, 16), match='lovelovePython'>
  5. >>> re.search(r"(love)(Python)\2", "I lovelovePythonPython")
  6. <_sre.SRE_Match object; span=(6, 22), match='lovePythonPython'>
  7. >>> re.search(r"(love)\1(Python)\2", "I lovelovePythonPython")
  8. <_sre.SRE_Match object; span=(2, 22), match='lovelovePythonPython'>
並不是要求全部都要是值組,是要求\序號匹配的是值組:
  1. >>> re.search(r"(I )love(Python)\2", "I lovePythonPython.com")
  2. <_sre.SRE_Match object; span=(0, 18), match='I lovePythonPython'>
②如果跟著的數字是0或者3位的數字,那麼它是一個八進制數,表示的是這個八進制數對應的ASCII碼對應的字符
例如:字符0 對應的十進制數為48,對應的八進制數為60,這裡要三位數,就是060,所以:
  1. >>> re.search(r"\060", '0')
  2. <_sre.SRE_Match object; span=(0, 1), match='0'>
  1. >>> re.search(r"I love Python\060", 'I love Python0')
  2. <_sre.SRE_Match object; span=(0, 14), match='I love Python0'>
接下來要介紹的元字符是中括號([ ]),這是生成一個字符類,事實上,字符類就是一個字符集合的意思,另外,值的注意的是:被中括號包含在裡面的元字符都會失去特殊功能,就像反斜杠加上一個元字符是一樣的,舉例:
  1. >>> re.search(r"[.]", 'I love Python.com')
  2. <_sre.SRE_Match object; span=(13, 14), match='.'>
字符類的意思就是將它裡面的內容都當做普通的字符看待,除了幾個特殊的字符:
①小橫槓(-),我們用它表示範圍,我們上節課講過,這節課我們講一個其他的方法:re.findall()

re.findall

在字符串中找到正則表達式所匹配的所有子串,並把它們作為一個列表返回。
re.findall(pattern, string, flags=0)
參數:
參數描述
pattern匹配的正則表達式
string要匹配的字符串。
flags標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫,多行匹配等等。
  1. >>> re.findall(r"[a-z]","12a32bc43jf3")
  2. ['a', 'b', 'c', 'j', 'f']
findall 和search 相比,似乎更符合我們的需求,但是當遇到值組時,findall 也會有陷阱,我們後面會講解。
②反斜杠(\),把反斜杠放在字符類[ ]中,它也不是表示本身,這樣會報錯,
  1. >>> re.findall(r"[\]","12a32bc43jf3")
  2. Traceback (most recent call last):
  3. File "<pyshell#47>", line 1, in <module>
  4. re.findall(r"[\]","12a32bc43jf3")
  5. File "D:\ProgramFiles\Anaconda3\lib\re.py", line 213, in findall
  6. return _compile(pattern, flags).findall(string)
  7. File "D:\ProgramFiles\Anaconda3\lib\re.py", line 293, in _compile
  8. p = sre_compile.compile(pattern, flags)
  9. File "D:\ProgramFiles\Anaconda3\lib\sre_compile.py", line 536, in compile
  10. p = sre_parse.parse(p, flags)
  11. File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 829, in parse
  12. p = _parse_sub(source, pattern, 0)
  13. File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 437, in _parse_sub
  14. itemsappend(_parse(source, state))
  15. File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 545, in _parse
  16. source.tell() - here)
  17. sre_constants.error: unterminated character set at position 0
反斜杠在字符類裡,表示Python 字符串的轉義符。
在字符串裡,我們都知道\n 表示回車的意思,所以:
  1. >>> re.findall(r"[\n]","12a32\nbc43jf3")
  2. ['\n']
③托字符^,在字符類[ ]裡,表示'除了'(取反)的意思,但是要注意的是,這個托字符^必須放在最前面:
  1. >>> re.findall(r"[^a-z]","12a32bc43jf3")
  2. ['1', '2', '3', '2', '4', '3', '3']
如果放在後面,就是表示匹配它本身:
  1. >>> re.findall(r"[a-z^]","12a32bc^^43jf3")
  2. ['a', 'b', 'c', '^', '^', 'j', 'f']
最後要介紹的元字符是用於做重複的事情,例如我們上節課講到的大括號{ },如{M,N}(要求M,N均為非負整數,且M<=N)表示前面的內容匹配M~N次。
  1. >>> re.search(r'Python{3}', 'I love Pythonnn')
  2. <_sre.SRE_Match object; span=(7, 15), match='Pythonnn'>
  1. >>> re.search(r'(Python){3}', 'I love PythonPythonPython')
  2. <_sre.SRE_Match object; span=(7, 25), match='PythonPythonPython'>
在正則表達式中,需要注意的是,大家在寫編程的時候,可能會注意美觀,可能會多加一些空格,但是在正則表達式裡面,你千萬不能加空格,例如:
  1. >>> re.search(r'(Python){1,5}', 'I love PythonPythonPython')
  2. <_sre.SRE_Match object; span=(7, 25), match='PythonPythonPython'>
  1. >>> re.search(r'(Python){1, 5}', 'I love PythonPythonPython')
  2. >>>
因為空格會被解析為一個正則表達式。
最後,我們來談一下幾個特殊的:
①星號(*):匹配前面的子表達式零次或多次,等價於{0,}

②加號(+):匹配前面的子表達式一次或多次,等價於{1,}
③問號(?):匹配前面的子表達式零次或一次,等價於{0,1}
在正則表達式中,如果實現條件一樣,推薦大家使用左邊的* + ?這三個,不要使用大括號{ },因為:首先,星號、加號、問號更加簡潔;其次,正則表達式內部會對這三個符號進行優化,效率會比使用大括號要高一些。
最後,我們來談一下貪婪和非貪婪。
關於我們這個重複的操作,有一點需要注意的就是:正則表達式默認是啟用貪婪的模式來進行匹配的,那什麼是貪婪呢?貪婪就是貪心,也就是說,只要在符合的條件下,它會盡可能多的去匹配,例如前面的re.search(r'(Python){1,5}', 'I love PythonPythonPython') 就會直接匹配到3個Python。
我們來看一個現實中的案例。假設我們想匹配<html> 
  1. >>> s = "<html><title> I love Python.com</title></html>"
  2. >>> re.search(r"<.+>", s)
  3. <_sre.SRE_Match object; span=(0, 46), match='<html><title> I love Python.com</title></html>'>
<.+> 表示以< 開頭,以> 結尾,重複. 號1次或多次。最後匹配了字符串全部。很明顯,這不是我們想要的結果。
因為貪婪會在條件符合的情況下盡可能多的去匹配,既然是這樣,我們就必須啟用非貪婪模式才可以,那麼非貪婪模式怎麼樣啟用呢?
很簡單,在表示重複的元字符後面再加上一個問號,這時候,問號就不代表0次或1次了,而是表示啟用非貪婪模式:
  1. >>> re.search(r"<.+?>", s)
  2. <_sre.SRE_Match object; span=(0, 6), match='<html>'>
好了,到這裡,正則表達式的所有元字符我麼就講解完畢了,大家課後一定要勤加練習。

0 留言:

發佈留言