1. 程式人生 > >《零基礎入門學習Python》第058講:論一隻爬蟲的自我修養6:正則表示式2

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

上一節課我們通過一個例子(匹配 ip 地址)讓大家初步瞭解到正則表示式的魔力,也讓大家充分了解到學習正則表示式是一個相對比較困難的事情。所以這一節課我們將繼續學習 正則表示式的語法。

我們依稀還記得在Python中,正則表示式是以字串的形式來描述的,正則表示式的強大之處在於特殊符號的應用,我們上節課舉了例子,例如 點號(.),在正則表示式中表示匹配除了 換行符之外的任何字元,它就是一個特殊的字元。正是這些特殊符號,使得一個正則表示式可以匹配一個複雜的規則,而不僅僅是匹配一個字串,如果你只需要匹配一個字串,那用 find() 方法就可以了。

我這裡給大家準備了一個列表,Python3 正則表示式特殊符號及用法(詳細列表)

這裡羅列了python3 所支援的所有正則表示式的特殊符號以及具體的含義,在難以理解的地方,用斜體舉了例子給大家看,方便大家理解,大家可以將這個文章收藏起來,以後用到的時候查詢就可以了,切記不要死記硬背,因為根本背不住,如果背錯了更扎心。

大家看到這裡,可能就會犯嘀咕了:“好歹我也是見過世面的人啊,為了查詢一個字串,有必要掌握這麼多規則嗎?”。

實話說,沒必要。我這裡只是給大家做一個總結,我們正常使用的情況下,只是使用這裡的一小部分,另外一大部分只是為了應對突發情況而準備的。例如某一天,你心血來潮,想為你的規則再增加複雜一點的規則,那麼這裡邊正則表示式就可以應付自如了。一定要記住的是,這裡面的東西不要去背,多做練習才是重要的啊。你掌握的東西才是你的,背下來的東西過兩天就不是你的了。

特殊符號是由兩部分組成的,一部分是 元字元。(例如我們上節課講的 點號(.),方括號([]),反斜槓(\)等)。

所有的元字元包括:.   ^    $     *     +     ?     { }     [ ]     \     |     ( )  

另一部分就是 反斜槓加上普通符號組成的特殊符號,

它擁有特殊的含義。

首先來談談元字元:

點號(.):是匹配除了換行符之外的任何字元。

| :就相當於邏輯或,學過C語言的同學們都知道,這就是按位或。A|B 表示匹配正則表示式 A或者B

例如:

>>> import re
>>> re.search(r"Python(E|F)", "PythonE")
<_sre.SRE_Match object; span=(0, 7), match='PythonE'>
>>> re.search(r"Python(E|F)", "PythonF")
<_sre.SRE_Match object; span=(0, 7), match='PythonF'>
>>> re.search(r"Python(E|F)", "PythonD")
>>> 

託字元(^):定位匹配,匹配字串的開始位置(即確定一個位置)。

例如:

>>> re.search(r"^Python", "I love Python")
>>> 
>>> re.search(r"^Python", "Python, I love")
<_sre.SRE_Match object; span=(0, 6), match='Python'>

跟託字元(^)對應的就是 美元符號($),$ 匹配輸入字串的結束位置.

>>> re.search(r"Python$", "Python, I love")
>>> 
>>> re.search(r"Python$", "I love Python")
<_sre.SRE_Match object; span=(7, 13), match='Python'>

我們剛剛提到了值組,就是用小括號括起來的,我們上節課也用過了,用小括號括起來跟數學的括號是一樣的,把一個東西當做一個整體,那麼就把它括起來。

接下來是史上最困難、最複雜的 反斜槓(\),反斜槓在正則表示式中應用是最廣泛的,它既可以將一個普通的字元變為特殊字元(這部分內容下節課繼續講解),同時也可以解除元字元的特殊功能,這在上節課已經講過,例如 \. 匹配的就不是除換行符之外的任何字元了,他匹配的就是一個點(.)了。

如果在反斜槓後面加的是數字,那麼還有兩種表示方案:

①如果跟著的數字是 1~99,就表示引用序號對應的值組所匹配的字串,其中序號所對應的值組:為 \ 前面的值組,\序號必須在對應的值組的正後面,序號為第幾個值組。

例如:

>>> re.search(r"(Python)\1", "I love Python")
>>> 
>>> re.search(r"(Python)\1", "I love PythonPython")
<_sre.SRE_Match object; span=(7, 19), match='PythonPython'>

上面的(Python)是第一個值組(序號是從1開始計算的,因為0表示一個八進位制數),所以 \1,且\1 表示Python,其實 r'(Python)\1' 就等於 'PythonPython'。

>>> re.search(r"(love)(Python)\1", "I lovelovePythonPython")  #這樣\1 是找不到 (love)的,\序號必須在對應值組的正後方
>>> 
>>> re.search(r"(love)\1(Python)", "I lovelovePythonPython")
<_sre.SRE_Match object; span=(2, 16), match='lovelovePython'>
>>> re.search(r"(love)(Python)\2", "I lovelovePythonPython")
<_sre.SRE_Match object; span=(6, 22), match='lovePythonPython'>
>>> re.search(r"(love)\1(Python)\2", "I lovelovePythonPython")
<_sre.SRE_Match object; span=(2, 22), match='lovelovePythonPython'>

並不是要求全部都要是值組,是要求 \序號 匹配的是值組:

>>> re.search(r"(I )love(Python)\2", "I lovePythonPython.com")
<_sre.SRE_Match object; span=(0, 18), match='I lovePythonPython'>

②如果跟著的數字是 0 或者 3位的數字,那麼它是一個八進位制數,表示的是這個八進位制數對應的 ASCII 碼對應的字元

例如:字元 0 對應的十進位制數為 48,對應的八進位制數為 60,這裡要三位數,就是060,所以:

>>> re.search(r"\060", '0')
<_sre.SRE_Match object; span=(0, 1), match='0'>
>>> re.search(r"I love Python\060", 'I love Python0')
<_sre.SRE_Match object; span=(0, 14), match='I love Python0'>

接下來要介紹的元字元是 中括號([ ]),這是生成一個字元類,事實上,字元類就是一個字元集合的意思,另外,值的注意的是:被中括號包含在裡面的元字元都會失去特殊功能,就像 反斜槓加上一個元字元是一樣的,舉例:

>>> re.search(r"[.]", 'I love Python.com')
<_sre.SRE_Match object; span=(13, 14), match='.'>

字元類的意思就是將它裡面的內容都當做普通的字元看待,除了幾個特殊的字元:

①小橫槓(-),我們用它表示範圍,我們上節課講過,這節課我們講一個其他的方法:re.findall()

re.findall

在字串中找到正則表示式所匹配的所有子串,並把它們作為一個列表返回。

re.findall(pattern, string, flags=0)

引數:

引數 描述
pattern 匹配的正則表示式
string 要匹配的字串。
flags 標誌位,用於控制正則表示式的匹配方式,如:是否區分大小寫,多行匹配等等。
>>> re.findall(r"[a-z]","12a32bc43jf3")
['a', 'b', 'c', 'j', 'f']

findall 和 search 相比,似乎更符合我們的需求,但是當遇到值組時,findall 也會有陷阱,我們後面會講解。

②反斜槓(\),把反斜槓放在字元類[ ]中,它也不是表示本身,這樣會報錯,

>>> re.findall(r"[\]","12a32bc43jf3")
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    re.findall(r"[\]","12a32bc43jf3")
  File "D:\ProgramFiles\Anaconda3\lib\re.py", line 213, in findall
    return _compile(pattern, flags).findall(string)
  File "D:\ProgramFiles\Anaconda3\lib\re.py", line 293, in _compile
    p = sre_compile.compile(pattern, flags)
  File "D:\ProgramFiles\Anaconda3\lib\sre_compile.py", line 536, in compile
    p = sre_parse.parse(p, flags)
  File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 829, in parse
    p = _parse_sub(source, pattern, 0)
  File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 437, in _parse_sub
    itemsappend(_parse(source, state))
  File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 545, in _parse
    source.tell() - here)
sre_constants.error: unterminated character set at position 0

反斜槓在字元類裡,表示Python 字串的轉義符。

在字串裡,我們都知道 \n 表示回車的意思,所以:

>>> re.findall(r"[\n]","12a32\nbc43jf3")
['\n']

③託字元 ^,在字元類[ ]裡,表示‘除了’(取反)的意思,但是要注意的是,這個託字元 ^ 必須放在最前面:

>>> re.findall(r"[^a-z]","12a32bc43jf3")
['1', '2', '3', '2', '4', '3', '3']

如果放在後面,就是表示匹配它本身:

>>> re.findall(r"[a-z^]","12a32bc^^43jf3")
['a', 'b', 'c', '^', '^', 'j', 'f']

最後要介紹的元字元 是用於做重複的事情,例如我們上節課講到的 大括號{ },如{M,N}(要求M,N均為非負整數,且M<=N)表示前面的內容匹配 M~N次。

>>> re.search(r'Python{3}', 'I love Pythonnn')
<_sre.SRE_Match object; span=(7, 15), match='Pythonnn'>
>>> re.search(r'(Python){3}', 'I love PythonPythonPython')
<_sre.SRE_Match object; span=(7, 25), match='PythonPythonPython'>

在正則表示式中,需要注意的是,大家在寫程式設計的時候,可能會注意美觀,可能會多加一些空格,但是在正則表示式裡面,你千萬不能加空格,例如:

>>> re.search(r'(Python){1,5}', 'I love PythonPythonPython')
<_sre.SRE_Match object; span=(7, 25), match='PythonPythonPython'>
>>> re.search(r'(Python){1, 5}', 'I love PythonPythonPython')
>>> 

因為空格會被解析為一個正則表示式。

最後,我們來談一下幾個特殊的:

①星號(*):匹配前面的子表示式零次或多次,等價於 {0,}

 

②加號(+):匹配前面的子表示式一次或多次,等價於 {1,}

③問號(?):匹配前面的子表示式零次或一次,等價於 {0,1}

在正則表示式中,如果實現條件一樣,推薦大家使用左邊的 *  +  ?這三個,不要使用 大括號{ },因為:首先,星號、加號、問號更加簡潔;其次,正則表示式內部會對這三個符號進行優化,效率會比使用大括號要高一些。

最後,我們來談一下 貪婪 和 非貪婪。

關於我們這個 重複 的操作,有一點需要注意的就是:正則表示式預設是啟用 貪婪 的模式來進行匹配的,那什麼是 貪婪 呢?貪婪就是貪心,也就是說,只要在符合的條件下,它會盡可能多的去匹配,例如前面的 re.search(r'(Python){1,5}', 'I love PythonPythonPython') 就會直接匹配到3個 Python。

我們來看一個現實中的案例。假設我們想 匹配 <html> 

>>> s = "<html><title> I love Python.com</title></html>"
>>> re.search(r"<.+>", s)
<_sre.SRE_Match object; span=(0, 46), match='<html><title> I love Python.com</title></html>'>

<.+> 表示以 < 開頭,以 > 結尾,重複 . 號 1次或多次。最後匹配了字串全部。很明顯,這不是我們想要的結果。

因為貪婪會在條件符合的情況下儘可能多的去匹配,既然是這樣,我們就必須啟用 非貪婪模式才可以,那麼非貪婪模式怎麼樣啟用呢?

很簡單,在表示重複的元字元後面再加上一個問號,這時候,問號就不代表0次或1次了,而是表示啟用非貪婪模式:

>>> re.search(r"<.+?>", s)
<_sre.SRE_Match object; span=(0, 6), match='<html>'>

好了,到這裡,正則表示式的所有元字元我麼就講解完畢了,大家課後一定要勤加練習。