国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python正則表達式完全指南

2020-01-04 17:14:01
字體:
來源:轉載
供稿:網友

正則表達式處理文本有如疾風掃秋葉,絕大部分編程語言都內置支持正則表達式,它應用在諸如表單驗證、文本提取、替換等場景。爬蟲系統更是離不開正則表達式,用好正則表達式往往能收到事半功倍的效果。

介紹正則表達式前,先來看一個問題,下面這段文本來自豆瓣的某個網頁鏈接,我對內容進行了縮減。問:如何提取文本中所有郵箱地址呢?

html = """  <style>   .qrcode-app{    display: block;    background: url(/pics/qrcode_app4@2x.png) no-repeat;   }  </style>  <div class="reply-doc content">   <p class="">34613453@qq.com,謝謝了</p>   <p class="">30604259@qq.com麻煩樓主</p>  </div>  <p class="">490010464@163.com<br/>謝謝</p>  """

如果你還沒接觸過正則表達式,我想對此會是一籌莫展,不用正則,似乎想不到一種更好的方式來處理,不過,我們暫且放下這個問題,待學習完正則表達式之后再來考慮如何解決。

字符串的表現形式

Python 字符串有幾種表現形式,以u開頭的字符串稱為Unicode字符串,它不在本文討論范圍內,此外,你應該還看到過這兩種寫法:

>>> foo = "hello">>> bar = r"hello"

前者是常規字符串,后者 r 開頭的是原始字符串,兩者有什么區別?因為在上面的例子中,它們都是由普通文本字符組成的串,在這里沒什么區別,下面可以證明

>>> foo is barTrue>>> foo == barTrue

但是,如果字符串中包括有特殊字符,會是什么情況呢?再來看一個例子:

>>> foo = "/n">>> bar = r"/n">>> foo, len(foo)('/n', 1)>>> bar, len(bar)('//n', 2)>>> foo == barFalse>>>

"/n" 是一個轉義字符,它在 ASCII 中表示換行符。而 r"/n" 是一個原始字符串,原始字符串不對特殊字符進行轉義,它就是你看到的字面意思,由 "/" 和 "n" 兩個字符組成的字符串。

定義原始字符串可以用小寫r或者大寫R開頭,比如 r"/b" 或者 R"/b" 都是允許的。在 Python 中,正則表達式一般用原始字符串的形式來定義,為什么呢?

舉例來說,對于字符 "/b" 來說,它在 ASCII 中是有特殊意義的,表示退格鍵,而在正則表達式中,它是一個特殊的元字符,用于匹配一個單詞的邊界,為了能讓正則編譯器正確地表達它的意義就需要用原始字符串,當然也可以使用反斜杠 "/" 對常規定義的字符串進行轉義

>>> foo = "//b">>> bar = r"/b">>> foo == barTrue

正則基本介紹

正則表達式由普通文本字符和特殊字符(元字符)兩種字符組成。元字符在正則表達式中具有特殊意義,它讓正則表達式具有更豐富的表達能力。例如,正則表達式 r"a.d"中 ,字符 'a' 和 'd' 是普通字符,'.' 是元字符,. 可以指代任意字符,它能匹配 'a1d'、'a2d'、'acd' ,它的匹配流程是:

python,正則表達式

Python 內置模塊 re 是專門用于處理正則表達式的模塊。

>>> rex = r"a.d" # 正則表達式文本>>> original_str = "and" # 原始文本>>> pattern = re.compile(rex) # 正則表達式對象>>> m = pattern.match(original_str) # 匹配對象>>> m <_sre.SRE_Match object at 0x101c85b28># 等價于>>> re.match(r"a.d", "and")<_sre.SRE_Match object at 0x10a15dcc8>

如果原文本字符串與正則表達式匹配,那么就會返回一個 Match 對象,當不匹配時,match 方法返回的 None,通過判斷m是否為None可進行表單驗證。

接下來,我們需要學習更多元字符。

基本元字符

.:匹配除換行符以外的任意一個字符,例如:"a.c" 可以完全匹配 "abc",也可以匹配 "abcef" 中的 "abc"
/: 轉義字符,使特殊字符具有本來的意義,例如: 1/.2 可以匹配 1.2
[...]:匹配方括號中的任意一個字符,例如:a[bcd]e 可以匹配 abe、ace、ade,它還支持范圍操作,比如:a到z可表示為 "a-z",0到9可表示為 "0-9",注意,在 "[]" 中的特殊字符不再有特殊意義,就是它字面的意義,例如:[.*]就是匹配 . 或者 *
[^...],字符集取反,表示只要不是括號中出現的字符都可以匹配,例如:a[^bcd]e 可匹配 aee、afe等

>>> re.match(r"a.c", "abc").group()'abc'>>> re.match(r"a.c", "abcef").group()'abc'>>> re.match(r"1/.2", "1.2").group()'1.2'>>> re.match(r"a[0-9]b", "a2b").group()'a2b'>>> re.match(r"a[0-9]b", "a5b11").group()'a5b'>>> re.match(r"a[.*?]b", "a.b").group()'a.b'>>> re.match(r"abc[^/w]", "abc!123").group()'abc!

group 方法返回原字符串(abcef)中與正則表達式相匹配的那部分子字符串(abc),提前是要匹配成功 match 方法才會返回 Match 對象,進而才有group方法。

預設元字符

/w 匹配任意一個單詞字符,包括數字和下劃線,它等價于 [A-Za-z0-9_],例如 a/wc 可以匹配 abc、acc
/W 匹配任意一個非單詞字符,與 /w 操作相反,它等價于 [^A-Za-z0-9_],例如: a/Wc 可匹配 a!c
/s 匹配任意一個空白字符,空格、回車等都是空白字符,例如:a/sc 可以配 a/nc,這里的 /n表示回車
/S 匹配任意一個非空白字符
/d 匹配任意一個數字,它等價于[0-9],例如:a/dc 可匹配 a1c、a2c ...
/D 匹配任意一個非數字

邊界匹配

邊界匹配相關的符號專門用于修飾字符。

^ 匹配字符的開頭,在字符串的前面,例如:^abc 表示匹配 a開頭,后面緊隨bc的字符串,它可以匹配 abc$ 匹配字符的結尾,在字符串的末尾位置,例如: hello$>>> re.match(r"^abc","abc").group()'abc'>>> re.match(r"^abc$","abc").group()'abc'

重復匹配

前面的元字符都是針對單個字符來匹配的,如果希望匹配的字符重復出現,比如匹配身份證號碼,長度18位,那么就需要用到重復匹配的元字符

* 重復匹配零次或者更多次
? 重復匹配零次或者一次
+ 重復匹配1次或者多次
{n} 重復匹配n次
{n,} 重復匹配至少n次
{n, m} 重復匹配n到m次

# 簡單匹配身份證號碼,前面17位是數字,最后一位可以是數字或者字母X>>> re.match(r"/d{17}[/dX]", "42350119900101153X").group()'42350119900101153X'# 匹配5到12的QQ號碼>>> re.match(r"/d{5,12}$", "4235011990").group()'4235011990'

邏輯分支

匹配一個固定電話號碼,不同地區規則不一樣,有的地方區號是3位,電話是8位,有的地方區號是4位,電話為7位,區號與號碼之間用 - 隔開,如果應對這樣的需求呢?這時你需要用到邏輯分支條件字符 |,它把表達式分為左右兩部分,先嘗試匹配左邊部分,如果匹配成功就不再匹配后面部分了,這是邏輯 "或" 的關系

# abc|cde 可以匹配abc 或者 cde,但優先匹配abc>>> re.match(r"aa(abc|cde)","aaabccde").group()'aaabc'0/d{2}-/d{8}|0/d{3}-/d{7} 表達式以0開頭,既可以匹配3位區號8位號碼,也可以匹配4位區號7位號碼>>> re.match(r"0/d{2}-/d{8}|0/d{3}-/d{7}", "0755-4348767").group()'0755-4348767'>>> re.match(r"0/d{2}-/d{8}|0/d{3}-/d{7}", "010-34827637").group()'010-34827637'

分組

前面介紹的匹配規則都是針對單個字符而言的,如果想要重復匹配多個字符怎么辦,答案是,用子表達式(也叫分組)來表示,分組用小括號"()"表示,例如 (abc){2} 表示匹配abc兩次, 匹配一個IP地址時,可以使用 (/d{1,3}/.){3}/d{1,3},因為IP是由4組數組3個點組成的,所有,前面3組數字和3個點可以作為一個分組重復3次,最后一部分是一個1到3個數字組成的字符串。如:192.168.0.1。

關于分組,group 方法可用于提取匹配的字符串分組,默認它會把整個表達式的匹配結果當做第0個分組,就是不帶參數的 group() 或者是 group(0),第一組括號中的分組用group(1)獲取,以此類推

>>> m = re.match(r"(/d+)(/w+)", "123abc")#分組0,匹配整個正則表達式>>> m.group()'123abc'#等價>>> m.group(0)'123abc'# 分組1,匹配第一對括號>>> m.group(1)'123'# 分組2,匹配第二對括號>>> m.group(2)'abc'>>>

通過分組,我們可以從字符串中提取出想要的信息。另外,分組還可以通過指定名字的方式獲取。

# 第一個分組的名字是number# 第二個分組的名字是char>>> m = re.match(r"(?P<number>/d+)(?P<char>/w+)", "123abc")>>> m.group("number")'123'# 等價>>> m.group(1)'123'

貪婪與非貪婪

默認情況下,正則表達式重復匹配時,在使整個表達式能得到匹配的前提下盡可能匹配多的字符,我們稱之為貪婪模式,是一種貪得無厭的模式。例如: r"a.*b" 表示匹配 a 開頭 b 結尾,中間可以是任意多個字符的字符串,如果用它來匹配 aaabcb,那么它會匹配整個字符串。

>>> re.match(r"a.*b", "aaabcb").group()'aaabcb'

有時,我們希望盡可能少的匹配,怎么辦?只需要在量詞后面加一個問號" ?",在保證匹配的情況下盡可能少的匹配,比如剛才的例子,我們只希望匹配 aaab,那么只需要修改正則表達式為 r"a.*?b"

>>> re.match(r"a.*?b", "aaabcb").group()'aaab'>>>

非貪婪模式在爬蟲應用中使用非常頻繁。比如之前在公眾號「Python之禪」曾寫過一篇爬取網站并將其轉換為PDF文件的場景,在網頁上涉及img標簽元素是相對路徑的情況,我們需要把它替換成絕對路徑

>>> html = '<img src="/images/category.png"><img src="/images/js_framework.png">'
# 非貪婪模式就匹配的兩個img標簽# 你可以改成貪婪模式看看可以匹配幾個>>> rex = r'<img.*?src="(.*?)">'>>> re.findall(rex, html)['/images/category.png', '/images/js_framework.png']>>>>>> def fun(match):...  img_tag = match.group()...  src = match.group(1)...  full_src = "http://foofish.net" + src...  new_img_tag = img_tag.replace(src, full_src)...  return new_img_tag...>>> re.sub(rex, fun, html)<img src="http://foofish.net/images/category.png"><img src="http://foofish.net/images/js_framework.png">

sub 函數可以接受一個函數作為替換目標對象,函數返回值用來替換正則表達式匹配的部分,在這里,我把整個img標簽定義為一個正則表達式 r'<img.*?src="(.*?)">',group() 返回的值是 <img src="/images/category.png">,而 group(1) 的返回值是 /images/category.png,最后,我用 replace 方法把相對路徑替換成絕對路徑。

到此,你應該對正則表達式有了初步的了解,現在我想你應該能解決文章開篇提的問題了。

正則表達式的基本介紹也到這里告一段落,雖然代碼示例中用了re模塊中的很多方法,但我還沒正式介紹該模塊,考慮到文章篇幅,我把這部分放在下篇,下篇將對re的常用方法進行介紹。

以上所述是小編給大家介紹的Python正則表達式完全指南,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VEVB武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 澜沧| 珲春市| 老河口市| 温州市| 周宁县| 罗田县| 台中县| 筠连县| 玉溪市| 岳普湖县| 伊金霍洛旗| 苍山县| 青田县| 那坡县| 马边| 和田市| 新平| 贡觉县| 清水县| 泾源县| 永丰县| 会昌县| 江阴市| 花垣县| 夹江县| 巴南区| 博罗县| 牡丹江市| 双辽市| 罗平县| 抚顺县| 兴义市| 固镇县| 武胜县| 瑞丽市| 肃北| 盐边县| 苍溪县| 云和县| 松桃| 宜州市|