1. 程式人生 > >【筆記】5、初學python3網路爬蟲——正則表示式的基本使用

【筆記】5、初學python3網路爬蟲——正則表示式的基本使用

python3網路爬蟲——正則表示式的基本使用

學習指引:視訊教程《python3網路爬蟲實戰》

為了避免學習後短時間內遺忘,讓自己隨時可以查閱前方自己學過的知識,特意註冊csdn部落格,方便自己學習時做筆記,也方便隨時回顧。也希望自己的學習過程能給同樣初學python爬蟲的你帶來一點指引!由於自己是新手,只有一點點的python基礎,所以筆記中難免會有很多紕漏,還望各路大神多多包涵,多多指教,如有不正確的地方,歡迎指出。

正則表示式:

正則表示式是一個特殊的字元序列,它能幫助你方便的檢查一個字串是否與某種模式匹配。
Python 自1.5版本起增加了re 模組,它提供 Perl 風格的正則表示式模式。
re 模組使 Python 語言擁有全部的正則表示式功能。
compile 函式根據一個模式字串和可選的標誌引數生成一個正則表示式物件。該物件擁有一系列方法用於正則表示式匹配和替換。
re 模組也提供了與這些方法功能完全一致的函式,這些函式使用一個模式字串做為它們的第一個引數。

簡單來說,什麼是正則表示式呢?正則表示式就是使用單個字串來描述和匹配一系列或者匹配某個句法規則的字串。它用非常精煉的語法,就可以檢索文字中我們所需要的字串。在網頁解析的時候,我們通常得到一個網頁的所有文字資訊,這些資訊包括html的各種標籤,我們檢索標籤內文字的方法有很多,一種是標籤匹配,比如文字<html><body>hello</body></html>這樣的標籤,想要提取hello,那我們可以直接匹配<body>裡的hello文字,但是假如有多個標籤怎麼辦,比如

<html>
    <body>
<p>hello</p> <p>goodbye</p> </body> </html>

那這個時候我們要匹配hello就沒有一開始那麼容易了吧?這裡有兩個<p>標籤,而且兩個<p>標籤裡面的文字不同,這個時候,如果使用正則表示式則可以幫上大忙。上面這個例子算是一個引子吧,下面開始學習正則表示式:

先放上一些其他部落格的教程:

我下面的學習筆記將會擺一些我在練習時候用到的完整程式碼,大家複製貼上到idle中可以直接執行,主要是方便各位初學者能夠找到例項,方便更好的理解,也避免我在學習之後的以往。

元字元 描述
\ 將下一個字元標記符、或一個向後引用、或一個八進位制轉義符。例如,“\\n”匹配\n。“\n”匹配換行符。序列“\\”匹配“\”而“\(”則匹配“(”。即相當於多種程式語言中都有的“轉義字元”的概念。
^ 匹配輸入字串的開始位置。如果設定了RegExp物件的Multiline屬性,^也匹配“\n”或“\r”之後的位置。
$ 匹配輸入字串的結束位置。如果設定了RegExp物件的Multiline屬性,$也匹配“\n”或“\r”之前的位置。
* 匹配前面的子表示式任意次。例如,zo*能匹配“z”,也能匹配“zo”以及“zoo”。*等價於o{0,}
+ 匹配前面的子表示式一次或多次(大於等於1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價於{1,}。
? 匹配前面的子表示式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等價於{0,1}。
{n} n是一個非負整數。匹配確定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的兩個o。
{n,} n是一個非負整數。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等價於“o+”。“o{0,}”則等價於“o*”。
{n,m} m和n均為非負整數,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”將匹配“fooooood”中的前三個o為一組,後三個o為一組。“o{0,1}”等價於“o?”。請注意在逗號和兩個數之間不能有空格。
? 當該字元緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})後面時,匹配模式是非貪婪的。非貪婪模式儘可能少的匹配所搜尋的字串,而預設的貪婪模式則儘可能多的匹配所搜尋的字串。例如,對於字串“oooo”,“o+”將盡可能多的匹配“o”,得到結果[“oooo”],而“o+?”將盡可能少的匹配“o”,得到結果 [‘o’, ‘o’, ‘o’, ‘o’]
.點 匹配除“\r\n”之外的任何單個字元。要匹配包括“\r\n”在內的任何字元,請使用像“[\s\S]”的模式。
x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”(此處請謹慎)。“[zf]ood”則匹配“zood”或“food”。
[xyz] 字元集合。匹配所包含的任意一個字元。例如,“[abc]”可以匹配“plain”中的“a”。
[^xyz] 負值字元集合。匹配未包含的任意字元。例如,“[^abc]”可以匹配“plain”中的“plin”。
[a-z] 字元範圍。匹配指定範圍內的任意字元。例如,“[a-z]”可以匹配“a”到“z”範圍內的任意小寫字母字元。 注意:只有連字元在字元組內部時,並且出現在兩個字元之間時,才能表示字元的範圍; 如果出字元組的開頭,則只能表示連字元本身.
[^a-z] 負值字元範圍。匹配任何不在指定範圍內的任意字元。例如,“[^a-z]”可以匹配任何不在“a”到“z”範圍內的任意字元。
\b 匹配一個單詞邊界,也就是指單詞和空格間的位置(即正則表示式的“匹配”有兩種概念,一種是匹配字元,一種是匹配位置,這裡的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B 匹配非單詞邊界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx 匹配由x指明的控制字元。例如,\cM匹配一個Control-M或回車符。x的值必須為A-Z或a-z之一。否則,將c視為一個原義的“c”字元。
\d 匹配一個數字字元。等價於[0-9]。grep 要加上-P,perl正則支援
\D 匹配一個非數字字元。等價於[^0-9]。grep要加上-P,perl正則支援
\f 匹配一個換頁符。等價於\x0c和\cL。
\n 匹配一個換行符。等價於\x0a和\cJ。
\r 匹配一個回車符。等價於\x0d和\cM。
\s 匹配任何不可見字元,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。
\S 匹配任何可見字元。等價於[^ \f\n\r\t\v]。
\t 匹配一個製表符。等價於\x09和\cI。
\v 匹配一個垂直製表符。等價於\x0b和\cK。
\w 匹配包括下劃線的任何單詞字元。類似但不等價於“[A-Za-z0-9_]”,這裡的”單詞”字元使用Unicode字符集。
\W 匹配任何非單詞字元。等價於“[^A-Za-z0-9_]”。
\xn 匹配n,其中n為十六進位制轉義值。十六進位制轉義值必須為確定的兩個數字長。例如,“\x41”匹配“A”。“\x041”則等價於“\x04&1”。正則表示式中可以使用ASCII編碼。
\num 匹配num,其中num是一個正整數。對所獲取的匹配的引用。例如,“(.)\1”匹配兩個連續的相同字元。
\n 標識一個八進位制轉義值或一個向後引用。如果\n之前至少n個獲取的子表示式,則n為向後引用。否則,如果n為八進位制數字(0-7),則n為一個八進位制轉義值。
\nm 標識一個八進位制轉義值或一個向後引用。如果\nm之前至少有nm個獲得子表示式,則nm為向後引用。如果\nm之前至少有n個獲取,則n為一個後跟文字m的向後引用。如果前面的條件都不滿足,若n和m均為八進位制數字(0-7),則\nm將匹配八進位制轉義值nm。
\nml 如果n為八進位制數字(0-7),且m和l均為八進位制數字(0-7),則匹配八進位制轉義值nml。
\un 匹配n,其中n是一個用四個十六進位制數字表示的Unicode字元。例如,\u00A9匹配版權符號(&copy;)。
\< \> 匹配詞(word)的開始(\<)和結束(\>)。例如正則表示式\<the\>能夠匹配字串”for the wise”中的”the”,但是不能匹配字串”otherwise”中的”the”。注意:這個元字元不是所有的軟體都支援的。
( )將( 和 ) 之間的表示式定義為“組”(group),並且將匹配這個表示式的字元儲存到一個臨時區域(一個正則表示式中最多可以儲存9個),它們可以用 \1 到\9 的符號來引用。
|將兩個匹配條件進行邏輯“或”(Or)運算。例如正則表示式(him|her) 匹配”it belongs to him”和”it belongs to her”,但是不能匹配”it belongs to them.”。注意:這個元字元不是所有的軟體都支援的。


廢話不多說,我們開始碼程式碼吧!

首先要正則表示式的模組是re模組,我們就要呼叫re模組,用import re語句就可以呼叫了。

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

re.match嘗試從字串的起始位置匹配一個模式,如果不是起始位置匹配成功的話,match()就返回none

最常規的匹配方法

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}\s\w\w\s\w\s\w{5}\sDemo$',content)#這裡的句子可以說是完全與上面content的字串匹配了,具體每一個字元的用途在上面表格中已經給出,這裡注意的是,開頭的^是表示開頭要匹配這個字元,末尾的$是表明字串末尾要匹配這一段,中間的\w{10}是表明匹配任何非單詞字元,而且一次性連續匹配十個,那我們就不用輸入10個\w了。
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())

執行結果:

<_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 1234567 World_This is a Regex Demo'#注意1234567之間沒有空格了
result = re.match('^Hello\s(\d+)\sWorld.*Demo$',content)#注意小括號,有了小括號,那麼就會給它分組,想要把1234567提取出來,我們將它分個組,提取group(1)就可以了
print(result)
print(result.group())
print(result.group(1))
print(result.span())

執行結果:

<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
Hello 1234567 World_This is a Regex Demo
1234567
(0, 40)

貪婪匹配

import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello.*(\d+)\sWorld.*Demo$',content)
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7
(0, 40)

這裡的.*會把所有的符合單個字元的先匹配完再輪到\d執行,相當於貪婪地匹配。

非貪婪匹配

import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello.*?(\d+)\sWorld.*Demo$',content)
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567
(0, 40)

這裡的.?中的?會匹配最少的.,符合非貪婪匹配。會最少的符合.*的單個字元就輪到\d執行,相當於非貪婪匹配。

匹配模式

如果不指定匹配模式的話,它是不會匹配換行符的

import re
content = '''Hello 1234567 World_This 
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*?Demo$',content,re.S)#指定了匹配模式
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 41), match='Hello 1234567 World_This \nis a Regex Demo'>
1234567
(0, 41)

re.search

re.search掃描整個字串並返回第一個成功的匹配

import re
content = 'I am clever Hello 1234567 World_This is a Regex Demo I am clever'
result = re.search('He.*?(\d+).*?Demo',content)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(12, 52), match='Hello 1234567 World_This is a Regex Demo'>
1234567

我們來做一個練習

import re
html = '''
    <div class="v-hd2">
        <a href="/discover/playlist/" class="tit f-ff2 f-tdn">熱門推薦</a>
        <div class="tab">
            <a href="/discover/playlist/?cat=%E5%8D%8E%E8%AF%AD" class="s-fc3">華語</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E6%B5%81%E8%A1%8C" class="s-fc3">流行</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E6%91%87%E6%BB%9A" class="s-fc3">搖滾</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E6%B0%91%E8%B0%A3" class="s-fc3">民謠</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E7%94%B5%E5%AD%90" class="s-fc3">電子</a>
        </div>
        <span class="more"><a href="/discover/playlist/" class="s-fc3">更多</a><i class="cor s-bg s-bg-6">&nbsp;</i></span>
    </div>
'''


results = re.findall('<a.*?playlist.*?cat.*?class=\"(.*?)\"\>(.*?)</a>',html,re.S)
print(results)
print(type(results))
for result in results:
    print(result)

輸出結果為

[('s-fc3', '華語'), ('s-fc3', '流行'), ('s-fc3', '搖滾'), ('s-fc3', '民謠'), ('s-fc3', '電子')]
<class 'list'>
('s-fc3', '華語')
('s-fc3', '流行')
('s-fc3', '搖滾')
('s-fc3', '民謠')
('s-fc3', '電子')

re.sub

替換字串中每一個匹配的子串後返回替換後的字串
re.sub(pattern, repl, string, count=0, flags=0)
直接上例子:

import re
html = '''
    <div class="v-hd2">
        <a href="/discover/playlist/" class="tit f-ff2 f-tdn">熱門推薦</a>
        <div class="tab">
            <a href="/discover/playlist/?cat=%E5%8D%8E%E8%AF%AD" class="s-fc3">華語</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E6%B5%81%E8%A1%8C" class="s-fc3">流行</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E6%91%87%E6%BB%9A" class="s-fc3">搖滾</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E6%B0%91%E8%B0%A3" class="s-fc3">民謠</a>
            <span class="line">|</span>
            <a href="/discover/playlist/?cat=%E7%94%B5%E5%AD%90" class="s-fc3">電子</a>
        </div>
        <span class="more"><a href="/discover/playlist/" class="s-fc3">更多</a><i class="cor s-bg s-bg-6">&nbsp;</i></span>
    </div>
'''

content = re.sub('<a.*?>|</a>','',html)#將所有的a標籤去掉再提取,就方便很多
print(content)
result = re.findall('</span>(.*?)<',content,re.S)#提取內容,注意有換行符,要加上re.S
print(result)
for r in result:
    print(r.strip())#這個strip函式可以去掉空白字元輸出,打印出的內容就沒有換行符和空格啦
<div class="v-hd2">
        熱門推薦
        <div class="tab">
            華語
            <span class="line">|</span>
            流行
            <span class="line">|</span>
            搖滾
            <span class="line">|</span>
            民謠
            <span class="line">|</span>
            電子
        </div>
        <span class="more">更多<i class="cor s-bg s-bg-6">&nbsp;</i></span>
    </div>

['\n            流行\n            ', '\n            搖滾\n            ', '\n            民謠\n            ', '\n            電子\n        ', '\n    ']
流行
搖滾
民謠
電子

re.compile

re.compile(pattern, flags=0)
將正則表示式串編譯成正則物件,以便於複用該匹配模式

import re
content = '''Hello 1234567 World_This 
is a Regex Demo
'''
pattern = re.compile('^He.*?(\d+).*?Demo$',re.S)#將整個匹配模式的方式編譯成一個物件,存到pattern變數中,下次隨時可以呼叫
result = re.match(pattern,content)
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 41), match='Hello 1234567 World_This \nis a Regex Demo'>
1234567
(0, 41)

大概知識點就是這麼多啦,大家學習愉快