目錄
正規表達式:re子產品
元字元
正規表達式如何比對任意字元:re.S
貪婪比對和非貪婪比對
1.貪婪比對
2.非貪婪比對
正規表達式的分組
貓眼電影TOP100資訊提取
1.需求分析
2.代碼分析
3.編寫程式
正規表達式:re子產品
re子產品有兩種方式實作正則比對
方式一:
lists=re.findall("he","hello world")
方式二:
pattern=re.complie("he")
lists=pattern.findall("hello world")
這兩種方法都可以正則比對字元,隻是第二種方式提前定義了正規表達式,可以複用,是以推薦使用第二種方式
元字元
正規表達式常用的元字元如下:
. | 比對任意一個字元,除了\n |
* | 比對0個或多個的字元串 |
+ | 比對1個或多個的字元串 |
? | 比對0個或1個,為非貪婪方式 |
[a, b , c] | 比對 ‘a’ 或 ‘b’ 或 ‘c’ |
\s | 比對 任何 空白字元, 相當于[\t\n\r\f] |
\S | 比對 任何 非空白字元, 相當于[^\t\n\r\f] |
正規表達式如何比對任意字元:re.S
通常來說,如果我們要比對任意字元,可以這樣寫(方式一):
pattern=re.complie("[\s\S]")
lists=pattern.findall("hello\nworld")
這樣寫可以比對到\n,看下面這種寫法,這個寫法并不會比對\n這個換行符。一個頁面有非常多的換行符,是以這麼寫時不合理的。
pattern=re.complie(".*")
lists=pattern.findall("hello\nworld")
那麼我們可以使用re.S,如下,re.S代表允許'. '比對'\n'(方式二,推薦):
pattern=re.complie(".*",re.S)
lists=pattern.findall("hello\nworld")
貪婪比對和非貪婪比對
1.貪婪比對
在整個表達式比對成功的情況下,盡可能的多比對*或 + 或 ?。
表達方式:.* 或 .+ 或 .?
2.非貪婪比對
在整個表達式比對成功的情況下,盡可能的少比對* 或 + 或 ?。
表達方式:.*? 或 .?? 或 .+?
貪婪比對和非貪婪比對到底有什麼含義能,看下面的例子:
編寫這麼一段代碼,我們預期的結果是得到幾個集合,集合裡有兩個對象,<div><p>今天天氣不錯</p></div>和<div><p>太陽很舒服</p></div>
import re
str="""
<div><p>今天天氣不錯</p></div>
<div><p>太陽很舒服</p></div>
"""
pattern=re.compile("<div><p>.*</p></div>",re.S)
lists=pattern.findall(str)
print(lists)
但是結果卻成了一個對象,這是因為在貪婪比對模式下,由于‘.’會比對任意字元(它會認為</p></div>也是任意字元),是以它會比對到最後一個以‘</p></div>’結尾的字元串。

再看下面的一段代碼,稍一看似乎沒什麼差別,眼睛尖的是能發現差別的,在.*後面加了一個?号,剛才說了這是非貪婪表達式方式,而非貪婪比對的比對模式是比對最近的以</p></div>結尾的字元串(僅在這個案例中),這樣的比對模式正好符合我們的預期結果。
import re
str="""
<div><p>今天天氣不錯</p></div>
<div><p>太陽很舒服</p></div>
"""
pattern=re.compile("<div><p>.*?</p></div>",re.S)
lists=pattern.findall(str)
print(lists)
結果:
總結:在正規表達式中,絕大數情況會使用非貪婪比對,非常好了解,我們需要的内容是一個裝滿了成功比對的對象集合,而不是一個連在一起的對象集合(而且在多數情況下傳回的結果總是會與你預想的結果有差別)。
正規表達式的分組
還是上面那個案例,我們剛才得到的結果如下圖,我們僅需要<div></div>中間的内容該如何處理,這就需要用的正規表達式分組。
正規表達式分組是指在完整的的模式中定義子模式,将每個用圓括号中的子模式作為結果提取出來
實際運用非常簡單,還是上面的代碼,我們隻需要在.*?包在括号裡即可,如下:
import re
str="""
<div><p>今天天氣不錯</p></div>
<div><p>太陽很舒服</p></div>
"""
pattern=re.compile('<div><p>(.*?)</p></div>',re.S)
lists=pattern.findall(str)
做完上面的案例後,我們在具體來聊一下正規表達式的分組,請看下面3個案例:
import re
str='A B C D'
pattern2=re.compile("\w+\s+\w+")
lists2=pattern2.findall(str)
pattern3=re.compile("(\w+)\s+\w+")
lists3=pattern3.findall(str)
pattern4=re.compile("(\w+)\s+(\w+)")
lists4=pattern4.findall(str)
print(lists2)
print(lists3)
print(lists4)
先看第一組正規表達式:re.compile("\w+\s+\w+"),我們知道\w比對任意字母和數字,+号比對一個或多個,我們定義的str='A B C D',按照比對規則,\w+将比對一串連續的字元,而這裡的字元用空格隔開了,是以隻會比對一個字元。\s+比對任何空白字元,最後的\w+比對一個字元。那麼總結下來就是這樣的比對規則:字元 空格 字元。結果也是如此。
再看第二組正規表達式:re.compile("(\w+)\s+\w+"),與第一組的差別在與第一個\w+加了括号,從結果上差別就是隻比對到了一個字元。我們在上面的案例中講了正規表達式的分組,這裡的括号就是給\w+做分組,然它成為子模式。在子模式下隻取出子模式的比對内容作為結果,這裡的\w+的比對結果是A,是以輸出A。
第三組:re.compile("(\w+)\s+(\w+)")。第三組有兩個括号,代表有兩個子模式。兩個子模式會以元組的方式輸出。
總結:
1.正規表達式的分組是通過加()來實作的
2.如果隻想取比對結果的某一段内容,為這一段内容的比對模式加上()
3.有兩個()将會以元組的方式進行輸出
貓眼電影TOP100資訊提取
1.需求分析
貓眼電影TOP100榜單URL:https://maoyan.com/board/4?offset=0
頁面詳情如下,我們要提取的資訊有電影名稱、主演、上移時間和評分。
因為是top100,每頁顯示10個,總計10頁。分析URL得到第一頁為https://maoyan.com/board/4?offset=0,第二頁為https://maoyan.com/board/4?offset=10。
是以頁碼格式為offset=(page-1)*10
2.代碼分析
提取資訊的關鍵在與寫對正規表達式,如圖所示,我們要提取的資訊有劃紅線的部分。
經過整理後得出的資訊如下,我們要以這一串内容寫一段正規表達式。首先我們将需要提取的字元打上分組符号()。要注意的是電影名有兩處顯示,我們選擇title裡的;評分是分為兩段顯示的,我們都要打上标記。切忌不要把他寫成一行,目前這一段格式包含了\n換行符,并且每一個位置都是精确的,不易改動。
<div class="movie-item-info">
<p class="name"><a href="/films/1203" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" title="霸王别姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
<p class="star">
主演:張國榮,張豐毅,鞏俐
</p>
<p class="releasetime">上映時間:1993-07-26</p> </div>
<div class="movie-item-number score-num">
<p class="score"><i class="integer">9.</i><i class="fraction">5</i>
打完之後如下,現在這一段正規表達式僅可以比對霸王别姬這一段内容,我們要想辦法讓他比對所有。
<div class="movie-item-info">
<p class="name"><a href="/films/1203" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" title="(.*?)" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
<p class="star">
(.*?)
</p>
<p class="releasetime">(.*?)</p> </div>
<div class="movie-item-number score-num">
<p class="score"><i class="integer">(.*?)</i><i class="fraction">(.*?)</i>
我們将多餘的部分用.*?代替。得到如下格式。剛才說了,因為有換行符的存在,是以在換行出也要打上.*?
<div class="movie-item-info">
.*?title="(.*?)".*?
<p class="star">
(.*?)
</p>.*?
<p class="releasetime">(.*?)</p>.*?
<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>
最終經過整理後,得到下面内容。這裡不要留白行,擠壓成一段字元串。
<div class="board-item-main">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>.*?<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>
3.編寫程式
1.定義一個類
2.初始化類,定義url,headers,正則比對表達式
3.定義方法:get_html()用于擷取頁面,注意轉碼
4.定義方法:run(),作為類的入口函數,提示輸出頁面。顯示結果需要進一步處理,去除空格符
from urllib import request
import re
class Maoyan_spider(object):
def __init__(self):
self.url="https://maoyan.com/board/4?offset={}"
self.headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"}
self.pattern=re.compile('<div class="board-item-main">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)'
'</p>.*?<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>',re.S)
#擷取頁面
def get_html(self,url):
req = request.Request(url=url, headers=self.headers)
rep = request.urlopen(req)
html = rep.read().decode("utf-8")
return html
#入口函數
def run(self):
page=(int)(input("請輸入頁碼數:"))
#計算頁面
offset=(page-1)*10
#拼接url
url=self.url.format(offset)
html=self.get_html(url)
lists=self.pattern.findall(html)
for i in lists:
print("電影名:" + i[0].strip())
print(i[1].strip())
print(i[2].strip())
print("評分:" + i[3] + i[4])
if __name__ == "__main__":
maoyan=Maoyan_spider()
maoyan.run()
最終效果如下: