欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python爬蟲入門教程--正則表達(dá)式完全指南(五)

 更新時(shí)間:2017年05月25日 11:10:32   作者:FOOFISH  
要想做爬蟲,不可避免的要用到正則表達(dá)式,如果是簡單的字符串處理,類似于split,substring等等就足夠了,可是涉及到比較復(fù)雜的匹配,當(dāng)然是正則的天下,下面這篇文章主要給大家介紹了python爬蟲之正則表達(dá)式的相關(guān)資料,需要的朋友可以參考下。

前言

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

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

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>
  """

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

字符串的表現(xiàn)形式

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

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

前者是常規(guī)字符串,后者 r 開頭的是原始字符串,兩者有什么區(qū)別?因?yàn)樵谏厦娴睦又?,它們都是由普通文本字符組成的串,在這里沒什么區(qū)別,下面可以證明

>>> foo is bar
True
>>> foo == bar
True

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

>>> foo = "\n"
>>> bar = r"\n"

>>> foo, len(foo)
('\n', 1)
>>> bar, len(bar)
('\\n', 2)
>>> foo == bar
False
>>>

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

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

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

>>> foo = "\\b"
>>> bar = r"\b"
>>> foo == bar
True

正則基本介紹

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

Python 內(nèi)置模塊 re 是專門用于處理正則表達(dá)式的模塊。

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

# 等價(jià)于
>>> re.match(r"a.d", "and")
<_sre.SRE_Match object at 0x10a15dcc8>

如果原文本字符串與正則表達(dá)式匹配,那么就會返回一個(gè) Match 對象,當(dāng)不匹配時(shí),match 方法返回的 None,通過判斷m是否為None可進(jìn)行表單驗(yàn)證。

接下來,我們需要學(xué)習(xí)更多元字符。

基本元字符

  • .:匹配除換行符以外的任意一個(gè)字符,例如:"a.c" 可以完全匹配 "abc",也可以匹配 "abcef" 中的 "abc"
  • \: 轉(zhuǎn)義字符,使特殊字符具有本來的意義,例如: 1\.2 可以匹配 1.2
  • [...]:匹配方括號中的任意一個(gè)字符,例如:a[bcd]e 可以匹配 abe、ace、ade,它還支持范圍操作,比如:a到z可表示為 "a-z",0到9可表示為 "0-9",注意,在 "[]" 中的特殊字符不再有特殊意義,就是它字面的意義,例如:[.*]就是匹配 . 或者 *
  • [^...],字符集取反,表示只要不是括號中出現(xiàn)的字符都可以匹配,例如: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)中與正則表達(dá)式相匹配的那部分子字符串(abc),提前是要匹配成功 match 方法才會返回 Match 對象,進(jìn)而才有g(shù)roup方法。

預(yù)設(shè)元字符

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

邊界匹配

邊界匹配相關(guān)的符號專門用于修飾字符。

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

重復(fù)匹配

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

  • * 重復(fù)匹配零次或者更多次
  • ? 重復(fù)匹配零次或者一次
  • + 重復(fù)匹配1次或者多次
  • {n} 重復(fù)匹配n次
  • {n,} 重復(fù)匹配至少n次
  • {n, m} 重復(fù)匹配n到m次
# 簡單匹配身份證號碼,前面17位是數(shù)字,最后一位可以是數(shù)字或者字母X
>>> re.match(r"\d{17}[\dX]", "42350119900101153X").group()
'42350119900101153X'

# 匹配5到12的QQ號碼
>>> re.match(r"\d{5,12}$", "4235011990").group()
'4235011990'

邏輯分支

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

# abc|cde 可以匹配abc 或者 cde,但優(yōu)先匹配abc
>>> re.match(r"aa(abc|cde)","aaabccde").group()
'aaabc'

0\d{2}-\d{8}|0\d{3}-\d{7} 表達(dá)式以0開頭,既可以匹配3位區(qū)號8位號碼,也可以匹配4位區(qū)號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'

分組

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

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

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

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

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

貪婪與非貪婪

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

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

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

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

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

>>> html = '<img src="/images/category.png"><img src="/images/js_framework.png">'

# 非貪婪模式就匹配的兩個(gè)img標(biāo)簽
# 你可以改成貪婪模式看看可以匹配幾個(gè)
>>> 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 函數(shù)可以接受一個(gè)函數(shù)作為替換目標(biāo)對象,函數(shù)返回值用來替換正則表達(dá)式匹配的部分,在這里,我把整個(gè)img標(biāo)簽定義為一個(gè)正則表達(dá)式 r'',group() 返回的值是 <img src="/images/category.png"> ,而 group(1) 的返回值是 /images/category.png,最后,我用 replace 方法把相對路徑替換成絕對路徑。

到此,你應(yīng)該對正則表達(dá)式有了初步的了解,現(xiàn)在我想你應(yīng)該能解決文章開篇提的問題了。

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

總結(jié)

以上就是這篇文章的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Python 實(shí)現(xiàn)OpenCV格式和PIL.Image格式互轉(zhuǎn)

    Python 實(shí)現(xiàn)OpenCV格式和PIL.Image格式互轉(zhuǎn)

    今天小編就為大家分享一篇Python 實(shí)現(xiàn)OpenCV格式和PIL.Image格式互轉(zhuǎn),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-01-01
  • Python腳本傳參數(shù)argparse模塊的使用

    Python腳本傳參數(shù)argparse模塊的使用

    這篇文章主要介紹了Python腳本傳參數(shù)argparse模塊的使用,文章圍繞argparse模塊的相關(guān)資料展開具體的使用方法,具有一的參考價(jià)值,需要的小伙伴可以參考一下
    2022-03-03
  • python中字符串變二維數(shù)組的實(shí)例講解

    python中字符串變二維數(shù)組的實(shí)例講解

    下面小編就為大家分享一篇python中字符串變二維數(shù)組的實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-04-04
  • 教你怎么用Python實(shí)現(xiàn)多路徑迷宮

    教你怎么用Python實(shí)現(xiàn)多路徑迷宮

    這篇文章主要介紹了教你怎么用Python實(shí)現(xiàn)多路徑迷宮,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)python的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 擴(kuò)展Django admin的list_filter()可使用范圍方法

    擴(kuò)展Django admin的list_filter()可使用范圍方法

    今天小編就為大家分享一篇擴(kuò)展Django admin的list_filter()可使用范圍方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-08-08
  • python編碼總結(jié)(編碼類型、格式、轉(zhuǎn)碼)

    python編碼總結(jié)(編碼類型、格式、轉(zhuǎn)碼)

    這篇文章主要介紹了python編碼操作,包括編碼類型、格式、轉(zhuǎn)碼等,并結(jié)合實(shí)例分析了相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2016-07-07
  • python爬蟲之生活常識解答機(jī)器人

    python爬蟲之生活常識解答機(jī)器人

    這篇文章主要介紹了python爬蟲之生活常識解答機(jī)器人,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)python的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • Python配置文件管理之ini和yaml文件讀取的實(shí)現(xiàn)

    Python配置文件管理之ini和yaml文件讀取的實(shí)現(xiàn)

    本文主要介紹了Python配置文件管理之ini和yaml文件讀取,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Python協(xié)程asyncio模塊的演變及高級用法

    Python協(xié)程asyncio模塊的演變及高級用法

    網(wǎng)上很多關(guān)于Python協(xié)程asyncio模塊的教程都是基于老版Python的, 本文將以對比方式展示新老Python版本下協(xié)程的寫法有什么不同并總結(jié)了asyncio的一些高級用法, 包括如何獲取協(xié)程任務(wù)執(zhí)行結(jié)果,gather和wait方法的區(qū)別以及如何給任務(wù)添加回調(diào)函數(shù)。
    2021-05-05
  • python如何導(dǎo)出微信公眾號文章方法詳解

    python如何導(dǎo)出微信公眾號文章方法詳解

    這篇文章主要介紹了python如何導(dǎo)出微信公眾號文章方法詳解,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08

最新評論