Python爬蟲(五)—正則表達式 re 的深入學習

前言

以下關於正則表達式 re 學習記錄,強烈推薦深入瞭解的查看官方文檔。

re:https://docs.python.org/zh-cn/3/library/re.html

本文借鑑官方文檔及博文:https://www.cnblogs.com/zhaof/p/6925674.html

正則表達式

  • 介紹
    正則表達式是一組由字母和符號組成的特殊文本, 它可以用來從文本中找出滿足你想要的格式的句子。

    • 大小寫敏感
    • 使用反斜槓(’\’)來表示特殊字符,或者把特殊字符轉義成普通字符。
    • 帶有 ‘r’ 前綴的字符串字面值中,反斜槓不必做任何特殊處理。 最簡單的表達式,也要推薦原始字符串:r’regex’。
    • 正則表達式可以拼接; 如果 A 和 B 都是正則表達式, 那麼 AB 也是正則表達式。 通常, 如果字符串 p 匹配 A 並且另一個字符串 q 匹配 B, 那麼 pq 可以匹配 AB。
    • 重複修飾符 (, +, ?, {m,n}, 等) 不能直接嵌套。這樣避免了非貪婪後綴 ? 修飾符,和其他實現中的修飾符產生的多義性。要應用一個內層重複嵌套,可以使用括號。 比如,表達式 (?:a{6}) 匹配6個 ‘a’ 字符重複任意次數。
    • 絕大部分正則表達式操作都提供爲模塊函數和方法,在 編譯正則表達式. 這些函數是一個捷徑,不需要先編譯一個正則對象,但是損失了一些優化參數。
  • 特殊字符

\w      匹配字母數字及下劃線
\W      匹配f非字母數字下劃線
\s      匹配任意空白字符,等價於[\t\n\r\f]
\S      匹配任意非空字符
\d      匹配任意數字
\D      匹配任意非數字
\A      匹配字符串開始
\Z      匹配字符串結束,如果存在換行,只匹配換行前的結束字符串
\z      匹配字符串結束
\G      匹配最後匹配完成的位置
\n      匹配一個換行符
\t      匹配一個製表符
^       匹配字符串的開頭
$       匹配字符串的末尾
.       匹配任意字符,除了換行符,re.DOTALL標記被指定時,則可以匹配包括換行符的任意字符
[....]  用來表示一組字符,單獨列出:[amk]匹配a,m或k
[^...]  不在[]中的字符:[^abc]匹配除了a,b,c之外的字符
*       匹配0個或多個的表達式
+       匹配1個或者多個的表達式
?       匹配0個或1個由前面的正則表達式定義的片段,非貪婪方式
{n}     精確匹配n前面的表示
{m,m}   匹配n到m次由前面的正則表達式定義片段,貪婪模式
a|b     匹配a或者b
()      匹配括號內的表達式,也表示一個組
  • 控制匹配的模式
修飾符 描述
re.I 使匹配對大小寫不敏感
re.L 做本地化識別(locale-aware)匹配
re.M 多行匹配,影響 ^ 和 $
re.S 使 . 匹配包括換行在內的所有字符
re.U 根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B.
re.X 該標誌通過給予你更靈活的格式以便你將正則表達式寫得更易於理解。

re.compile(pattern, flags=0)

將正則表達式的樣式編譯爲一個 正則表達式對象 (正則對象),可以用於匹配,通過這個對象的方法 match(), search() 以及其他如下描述。

這個表達式的行爲可以通過指定 標記 的值來改變。值可以是以下任意變量,可以通過位的OR操作來結合( | 操作符)。

prog = re.compile(pattern)
result = prog.match(string)

等價於

result = re.match(pattern, string)

如果需要多次使用這個正則表達式的話,使用 re.compile() 和保存這個正則對象以便複用,可以讓程序更加高效。

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

如果 string 開始的0或者多個字符匹配到了正則表達式樣式,就返回一個相應的 匹配對象 。 如果沒有匹配,就返回 None。
儘量使用泛匹配,使用括號得到匹配目標,儘量使用非貪婪模式,有換行符就用re.S
re.match是從字符串的起始位置匹配一個模式

  • 常規的匹配
    • result.group()獲取匹配的結果
    • result.span()獲去匹配字符串的長度範圍
import re

content = "hello 123 4567 World_This is a regex Demo"
result = re.match('^hello\s\d\d\d\s\d{4}\s\w{10}.*Demo$',content)
print(result)
print(result.group())
print(result.span())
"""
<_sre.SRE_Match object; span=(0, 41), match='hello 123 4567 World_This is a regex Demo'>
hello 123 4567 World_This is a regex Demo
(0, 41)
"""
  • 泛匹配
import re

content = "hello 123 4567 World_This is a regex Demo"
result = re.match("^hello.*Demo$",content)
print(result)
print(result.group())
print(result.span())
  • 匹配目標
    爲了匹配字符串中具體的目標,則需要通過()括起來。
    通過re.group()獲得結果後,如果正則表達式中有括號,則re.group(1)獲取的就是第一個括號中匹配的結果
import re

content = "extra things hello 123455 world_this is a Re Extra things"
result = re.search("hello.*?(\d+).*?Re",content)
print(result)
print(result.group())
print(result.group(1))
"""
<_sre.SRE_Match object; span=(13, 44), match='hello 123455 world_this is a Re'>
hello 123455 world_this is a Re
123455
"""
  • 貪婪匹配

import re

content = "hello 1234567 World_This is a regex Demo"
result = re.match('^hello.*(\d+).*Demo',content)
print(result)
print(result.group(1))  # 7

結果中可以看出只匹配到了7,並沒有匹配到1234567,出現這種情況的原因是前面的.* 給匹配掉了, .*在這裏會儘可能的匹配多的內容,也就是我們所說的貪婪匹配,

如果我們想要匹配到1234567則需要將正則表達式改爲:
result= re.match(’^he.*?(\d+).*Demo’,content)
這樣結果就可以匹配到1234567

  • 匹配模式的使用
import re

content = """hello 123456 world_this
my name is luozheng
"""

result = re.match('^he.*?(\d+).*?luozheng$',content,re.S)
print(result.group())
print(result.group(1))
"""
hello 123456 world_this
my name is luozheng
123456
"""

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

掃描整個 字符串 找到匹配樣式的第一個位置,並返回一個相應的 匹配對象。如果沒有匹配,就返回一個 None。
爲了匹配方便,我們會更多的用search,不用match,match必須匹配頭部,所以很多時候不是特別方便
關於常規的匹配、泛匹配、匹配目標、貪婪匹配、匹配模式的使用 同樣適用。

import re

content = "extra things hello 123455 world_this is a Re Extra things"

result = re.search("hello.*?(\d+).*?Re",content)
print(result.group())
print(result.group(1))
"""
hello 123455 world_this is a Re
123455
"""

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

對 string 返回一個不重複的 pattern 的匹配列表, string 從左到右進行掃描,匹配按找到的順序返回。如果樣式裏存在一到多個組,就返回一個組合列表;就是一個元組的列表(如果樣式裏有超過一個組合的話)。空匹配也會包含在結果裏。

在 3.7 版更改: 非空匹配現在可以在前一個空匹配之後出現了。
import re

html = '''<div id="songs-list">
    <h2 class="title">經典老歌</h2>
    <p class="introduction">
        經典老歌列表
    </p>
    <ul id="list" class="list-group">
        <li data-view="2">一路上有你</li>
        <li data-view="7">
            <a href="/2.mp3" singer="任賢齊">滄海一聲笑</a>
        </li>
        <li data-view="4" class="active">
            <a href="/3.mp3" singer="齊秦">往事隨風</a>
        </li>
        <li data-view="6"><a href="/4.mp3" singer="beyond">光輝歲月</a></li>
        <li data-view="5"><a href="/5.mp3" singer="陳慧琳">記事本</a></li>
        <li data-view="5">
            <a href="/6.mp3" singer="鄧麗君">但願人長久</a>
        </li>
    </ul>
</div>'''

results = re.findall('<li.*?>\s*?(<a.*?>)?(\w+)(</a>)?\s*?</li>',html,re.S)
print(results)
for result in results:
    print(result[1])
"""
一路上有你
滄海一聲笑
往事隨風
光輝歲月
記事本
但願人長久
"""

注意點

  • \s*? 這種用法其實就是爲了解決有的有換行,有的沒有換行的問題
  • (<a.*?>)? 這種用法是因爲html中有的有a標籤,有的沒有的,?表示匹配一個或0個,正好可以用於匹配
  • 匹配對象總是有一個布爾值 True。如果沒有匹配的話 match() 和 search() 返回 None 所以你可以簡單的用 if 語句來判斷是否匹配
match = re.search(pattern, string)
if match:
    process(match)
  • 正則表達式對象 (正則對象),同樣支持search、 match、findall、split、sub等這些方法。
pattern = re.compile("d")
pattern.search("dog")     # Match at index 0<re.Match object; span=(0, 1), match='d'>

pattern.search("dog", 1)  # No match; search doesn't include the "d"

其他函數

  • re.split(pattern, string, maxsplit=0, flags=0)
    用 pattern 分開 string 。 如果在 pattern 中捕獲到括號,那麼所有的組裏的文字也會包含在列表裏。如果 maxsplit 非零, 最多進行 maxsplit 次分隔, 剩下的字符全部返回到列表的最後一個元素。
import re

s1 = re.split(r'\W+', 'Words, words, words.')
s2 = re.split(r'(\W+)', 'Words, words, words.')
s3 = re.split(r'\W+', 'Words, words, words.', 1)
s4 = re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)

print(s1)
print(s2)
print(s3)
print(s4)
"""
['Words', 'words', 'words', '']
['Words', ', ', 'words', ', ', 'words', '.', '']
['Words', 'words, words.']
['0', '3', '9']
"""
  • re.sub(pattern, repl, string, count=0, flags=0)
    re.sub(正則表達式,替換成的字符串,原字符串)
    例如:
    content = re.sub(’\d+’,’’,content)
    會將所有數字刪除。
  • re.purge()
    清除正則表達式緩存

個人博客:Loak 正 - 關注人工智能及互聯網的個人博客
文章地址:Python爬蟲(五)—正則表達式 re 的深入學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章