【Python3網路爬蟲開發實戰】3-基本庫的使用-4抓取貓眼電影排行

崔慶才丨靜覓發表於2019-02-13

本節中,我們利用requests庫和正規表示式來抓取貓眼電影TOP100的相關內容。requests比urllib使用更加方便,而且目前我們還沒有系統學習HTML解析庫,所以這裡就選用正規表示式來作為解析工具。

1. 本節目標

本節中,我們要提取出貓眼電影TOP100的電影名稱、時間、評分、圖片等資訊,提取的站點URL為maoyan.com/board/4,提取的結果會以檔案形式儲存下來。

2. 準備工作

在本節開始之前,請確保已經正確安裝好了requests庫。如果沒有安裝,可以參考第1章的安裝說明。

3. 抓取分析

我們需要抓取的目標站點為maoyan.com/board/4,開啟之後便可以檢視到榜單資訊,如圖3-11所示。圖3-11 榜單資訊

排名第一的電影是霸王別姬,頁面中顯示的有效資訊有影片名稱、主演、上映時間、上映地區、評分、圖片等資訊。

將網頁滾動到最下方,可以發現有分頁的列表,直接點選第2頁,觀察頁面的URL和內容發生了怎樣的變化,如圖3-12所示。

圖3-12 頁面URL變化

可以發現頁面的URL變成maoyan.com/board/4?off…,比之前的URL多了一個引數,那就是offset=10,而目前顯示的結果是排行11~20名的電影,初步推斷這是一個偏移量的引數。再點選下一頁,發現頁面的URL變成了maoyan.com/board/4?off…,引數offset變成了20,而顯示的結果是排行21~30的電影。

由此可以總結出規律,offset代表偏移量值,如果偏移量為n,則顯示的電影序號就是n+1n+10,每頁顯示10個。所以,如果想獲取TOP100電影,只需要分開請求10次,而10次的offset引數分別設定為0、10、20、…90即可,這樣獲取不同的頁面之後,再用正規表示式提取出相關資訊,就可以得到TOP100的所有電影資訊了。

4. 抓取首頁

接下來用程式碼實現這個過程。首先抓取第一頁的內容。我們實現了get_one_page()方法,並給它傳入url引數。然後將抓取的頁面結果返回,再通過main()方法呼叫。初步程式碼實現如下:

import requests

def get_one_page(url):
    response = requests.get(url)
    if response.status_code == 200:
        return response.text
    return None

def main():
    url = `http://maoyan.com/board/4`
    html = get_one_page(url)
    print(html)

main()複製程式碼

這樣執行之後,就可以成功獲取首頁的原始碼了。獲取原始碼後,就需要解析頁面,提取出我們想要的資訊。

5. 正則提取

接下來,回到網頁看一下頁面的真實原始碼。在開發者模式下的Network監聽元件中檢視原始碼,如圖3-13所示。

圖3-13 原始碼

注意,這裡不要在Elements選項卡中直接檢視原始碼,因為那裡的原始碼可能經過JavaScript操作而與原始請求不同,而是需要從Network選項卡部分檢視原始請求得到的原始碼。

檢視其中一個條目的原始碼,如圖3-14所示。

圖3-14 原始碼

可以看到,一部電影資訊對應的原始碼是一個dd節點,我們用正規表示式來提取這裡面的一些電影資訊。首先,需要提取它的排名資訊。而它的排名資訊是在classboard-indexi節點內,這裡利用非貪婪匹配來提取i節點內的資訊,正規表示式寫為:

1
<dd>.*?board-index.*?>(.*?)</i>

隨後需要提取電影的圖片。可以看到,後面有a節點,其內部有兩個img節點。經過檢查後發現,第二個img節點的data-src屬性是圖片的連結。這裡提取第二個img節點的data-src屬性,正規表示式可以改寫如下:

1
<dd>.*?board-index.*?>(.*?)</i>.*?src=”(.*?)”

再往後,需要提取電影的名稱,它在後面的p節點內,classname。所以,可以用name做一個標誌位,然後進一步提取到其內a節點的正文內容,此時正規表示式改寫如下:

1
<dd>.*?board-index.*?>(.*?)</i>.*?src=”(.*?)”.*?name.*?a.*?>(.*?)</a>

再提取主演、釋出時間、評分等內容時,都是同樣的原理。最後,正規表示式寫為:

1
<dd>.*?board-index.*?>(.*?)</i>.*?src=”(.*?)”.*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>

這樣一個正規表示式可以匹配一個電影的結果,裡面匹配了7個資訊。接下來,通過呼叫findall()方法提取出所有的內容。

接下來,我們再定義解析頁面的方法parse_one_page(),主要是通過正規表示式來從結果中提取出我們想要的內容,實現程式碼如下:

def parse_one_page(html):    pattern = re.compile(        `<dd>.*?board-index.*?>(.*?)</i>.*?src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>`,        re.S)    items = re.findall(pattern, html)    print(items)複製程式碼

這樣就可以成功地將一頁的10個電影資訊都提取出來,這是一個列表形式,輸出結果如下:

1
[(`1`, `http://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c`, `霸王別姬`, `
主演:張國榮,張豐毅,鞏俐
`, `上映時間:1993-01-01(中國香港)`, `9.`, `6`), (`2`, `http://p0.meituan.net/movie/__40191813__4767047.jpg@160w_220h_1e_1c`, `肖申克的救贖`, `
主演:蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓
`, `上映時間:1994-10-14(美國)`, `9.`, `5`), (`3`, `http://p0.meituan.net/movie/fc9d78dd2ce84d20e53b6d1ae2eea4fb1515304.jpg@160w_220h_1e_1c`, `這個殺手不太冷`, `
主演:讓·雷諾,加里·奧德曼,娜塔莉·波特曼
`, `上映時間:1994-09-14(法國)`, `9.`, `5`), (`4`, `http://p0.meituan.net/movie/23/6009725.jpg@160w_220h_1e_1c`, `羅馬假日`, `
主演:格利高利·派克,奧黛麗·赫本,埃迪·艾伯特
`, `上映時間:1953-09-02(美國)`, `9.`, `1`), (`5`, `http://p0.meituan.net/movie/53/1541925.jpg@160w_220h_1e_1c`, `阿甘正傳`, `
主演:湯姆·漢克斯,羅賓·懷特,加里·西尼斯
`, `上映時間:1994-07-06(美國)`, `9.`, `4`), (`6`, `http://p0.meituan.net/movie/11/324629.jpg@160w_220h_1e_1c`, `泰坦尼克號`, `
主演:萊昂納多·迪卡普里奧,凱特·溫絲萊特,比利·贊恩
`, `上映時間:1998-04-03`, `9.`, `5`), (`7`, `http://p0.meituan.net/movie/99/678407.jpg@160w_220h_1e_1c`, `龍貓`, `
主演:日高法子,阪本千夏,糸井重裡
`, `上映時間:1988-04-16(日本)`, `9.`, `2`), (`8`, `http://p0.meituan.net/movie/92/8212889.jpg@160w_220h_1e_1c`, `教父`, `
主演:馬龍·白蘭度,阿爾·帕西諾,詹姆斯·凱恩
`, `上映時間:1972-03-24(美國)`, `9.`, `3`), (`9`, `http://p0.meituan.net/movie/62/109878.jpg@160w_220h_1e_1c`, `唐伯虎點秋香`, `
主演:周星馳,鞏俐,鄭佩佩
`, `上映時間:1993-07-01(中國香港)`, `9.`, `2`), (`10`, `http://p0.meituan.net/movie/9bf7d7b81001a9cf8adbac5a7cf7d766132425.jpg@160w_220h_1e_1c`, `千與千尋`, `
主演:柊瑠美,入野自由,夏木真理
`, `上映時間:2001-07-20(日本)`, `9.`, `3`)]

但這樣還不夠,資料比較雜亂,我們再將匹配結果處理一下,遍歷提取結果並生成字典,此時方法改寫如下:

1234567891011121314def parse_one_page(html):    pattern = re.compile(        `<dd>.*?board-index.*?>(.*?)</i>.*?src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>`,        re.S)    items = re.findall(pattern, html)    for item in items:        yield {            `index`: item[0],            `image`: item[1],            `title`: item[2].strip(),            `actor`: item[3].strip()[3:] if len(item[3]) > 3 else ``,            `time`: item[4].strip()[5:] if len(item[4]) > 5 else ``,            `score`: item[5].strip() + item[6].strip()        }複製程式碼

這樣就可以成功提取出電影的排名、圖片、標題、演員、時間、評分等內容了,並把它賦值為一個個的字典,形成結構化資料。執行結果如下:

1
2
3
4
5
6
7
8
9
10
{`image`: `http://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c`, `actor`: `張國榮,張豐毅,鞏俐`, `score`: `9.6`, `index`: `1`, `title`: `霸王別姬`, `time`: `1993-01-01(中國香港)`}
{`image`: `http://p0.meituan.net/movie/__40191813__4767047.jpg@160w_220h_1e_1c`, `actor`: `蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓`, `score`: `9.5`, `index`: `2`, `title`: `肖申克的救贖`, `time`: `1994-10-14(美國)`}
{`image`: `http://p0.meituan.net/movie/fc9d78dd2ce84d20e53b6d1ae2eea4fb1515304.jpg@160w_220h_1e_1c`, `actor`: `讓·雷諾,加里·奧德曼,娜塔莉·波特曼`, `score`: `9.5`, `index`: `3`, `title`: `這個殺手不太冷`, `time`: `1994-09-14(法國)`}
{`image`: `http://p0.meituan.net/movie/23/6009725.jpg@160w_220h_1e_1c`, `actor`: `格利高利·派克,奧黛麗·赫本,埃迪·艾伯特`, `score`: `9.1`, `index`: `4`, `title`: `羅馬假日`, `time`: `1953-09-02(美國)`}
{`image`: `http://p0.meituan.net/movie/53/1541925.jpg@160w_220h_1e_1c`, `actor`: `湯姆·漢克斯,羅賓·懷特,加里·西尼斯`, `score`: `9.4`, `index`: `5`, `title`: `阿甘正傳`, `time`: `1994-07-06(美國)`}
{`image`: `http://p0.meituan.net/movie/11/324629.jpg@160w_220h_1e_1c`, `actor`: `萊昂納多·迪卡普里奧,凱特·溫絲萊特,比利·贊恩`, `score`: `9.5`, `index`: `6`, `title`: `泰坦尼克號`, `time`: `1998-04-03`}
{`image`: `http://p0.meituan.net/movie/99/678407.jpg@160w_220h_1e_1c`, `actor`: `日高法子,阪本千夏,糸井重裡`, `score`: `9.2`, `index`: `7`, `title`: `龍貓`, `time`: `1988-04-16(日本)`}
{`image`: `http://p0.meituan.net/movie/92/8212889.jpg@160w_220h_1e_1c`, `actor`: `馬龍·白蘭度,阿爾·帕西諾,詹姆斯·凱恩`, `score`: `9.3`, `index`: `8`, `title`: `教父`, `time`: `1972-03-24(美國)`}
{`image`: `http://p0.meituan.net/movie/62/109878.jpg@160w_220h_1e_1c`, `actor`: `周星馳,鞏俐,鄭佩佩`, `score`: `9.2`, `index`: `9`, `title`: `唐伯虎點秋香`, `time`: `1993-07-01(中國香港)`}
{`image`: `http://p0.meituan.net/movie/9bf7d7b81001a9cf8adbac5a7cf7d766132425.jpg@160w_220h_1e_1c`, `actor`: `柊瑠美,入野自由,夏木真理`, `score`: `9.3`, `index`: `10`, `title`: `千與千尋`, `time`: `2001-07-20(日本)`}

到此為止,我們就成功提取了單頁的電影資訊。

6. 寫入檔案

隨後,我們將提取的結果寫入檔案,這裡直接寫入到一個文字檔案中。這裡通過JSON庫的dumps()方法實現字典的序列化,並指定ensure_ascii引數為False,這樣可以保證輸出結果是中文形式而不是Unicode編碼。程式碼如下:

def write_to_json(content):
    with open(`result.txt`, `a`) as f:
        print(type(json.dumps(content)))
        f.write(json.dumps(content, ensure_ascii=False,).encode(`utf-8`))
複製程式碼

通過呼叫write_to_json()方法即可實現將字典寫入到文字檔案的過程,此處的content引數就是一部電影的提取結果,是一個字典。

7. 整合程式碼

最後,實現main()方法來呼叫前面實現的方法,將單頁的電影結果寫入到檔案。相關程式碼如下:

12345def main():    url = `http://maoyan.com/board/4`    html = get_one_page(url)    for item in parse_one_page(html):        write_to_json(item)複製程式碼

到此為止,我們就完成了單頁電影的提取,也就是首頁的10部電影可以成功提取並儲存到文字檔案中了。

8. 分頁爬取

因為我們需要抓取的是TOP100的電影,所以還需要遍歷一下,給這個連結傳入offset引數,實現其他90部電影的爬取,此時新增如下呼叫即可:

1
2
3
if __name__ == `__main__`:
for i in range(10):
main(offset=i * 10)

這裡還需要將main()方法修改一下,接收一個offset值作為偏移量,然後構造URL進行爬取。實現程式碼如下:

1
2
3
4
5
6
def main(offset):
url = `http://maoyan.com/board/4?offset=` + str(offset)
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)

到此為止,我們的貓眼電影TOP100的爬蟲就全部完成了,再稍微整理一下,完整的程式碼如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445import jsonimport requestsfrom requests.exceptions import RequestExceptionimport reimport time def get_one_page(url):    try:        response = requests.get(url)        if response.status_code == 200:            return response.text        return None    except RequestException:        return None def parse_one_page(html):    pattern = re.compile(`<dd>.*?board-index.*?>(d+)</i>.*?src="(.*?)".*?name"><a`                         + `.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>`                         + `.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>`, re.S)    items = re.findall(pattern, html)    for item in items:        yield {            `index`: item[0],            `image`: item[1],            `title`: item[2],            `actor`: item[3].strip()[3:],            `time`: item[4].strip()[5:],            `score`: item[5] + item[6]        } def write_to_file(content):    with open(`result.txt`, `a`, encoding=`utf-8`) as f:        f.write(json.dumps(content, ensure_ascii=False) + `
`) def main(offset):    url = `http://maoyan.com/board/4?offset=` + str(offset)    html = get_one_page(url)    for item in parse_one_page(html):        print(item)        write_to_file(item) if __name__ == `__main__`:    for i in range(10):        main(offset=i * 10)        time.sleep(1)複製程式碼

現在貓眼多了反爬蟲,如果速度過快,則會無響應,所以這裡又增加了一個延時等待。

9. 執行結果

最後,我們執行一下程式碼,輸出結果類似如下:

1
2
3
4
5
6
{`index`: `1`, `image`: `http://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c`, `title`: `霸王別姬`, `actor`: `張國榮,張豐毅,鞏俐`, `time`: `1993-01-01(中國香港)`, `score`: `9.6`}
{`index`: `2`, `image`: `http://p0.meituan.net/movie/__40191813__4767047.jpg@160w_220h_1e_1c`, `title`: `肖申克的救贖`, `actor`: `蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓`, `time`: `1994-10-14(美國)`, `score`: `9.5`}
{`index`: `98`, `image`: `http://p0.meituan.net/movie/76/7073389.jpg@160w_220h_1e_1c`, `title`: `東京物語`, `actor`: `笠智眾,原節子,杉村春子`, `time`: `1953-11-03(日本)`, `score`: `9.1`}
{`index`: `99`, `image`: `http://p0.meituan.net/movie/52/3420293.jpg@160w_220h_1e_1c`, `title`: `我愛你`, `actor`: `宋在河,李彩恩,吉海延`, `time`: `2011-02-17(韓國)`, `score`: `9.0`}
{`index`: `100`, `image`: `http://p1.meituan.net/movie/__44335138__8470779.jpg@160w_220h_1e_1c`, `title`: `遷徙的鳥`, `actor`: `雅克·貝漢,菲利普·拉波洛,Philippe Labro`, `time`: `2001-12-12(法國)`, `score`: `9.1`}

這裡省略了中間的部分輸出結果。可以看到,這樣就成功地把TOP100的電影資訊爬取下來了。

這時我們再看下文字檔案,結果如圖3-15所示。

圖3-15 執行結果

可以看到,電影資訊也已全部儲存到了文字檔案中了,大功告成!

10. 本節程式碼

本節的程式碼地址為github.com/Python3WebS…

本節中,我們通過爬取貓眼TOP100的電影資訊練習了requests和正規表示式的用法。這是一個最基礎的例項,希望大家可以通過這個例項對爬蟲的實現有一個最基本的思路,也對這兩個庫的用法有更深一步的瞭解。

本資源首發於崔慶才的個人部落格靜覓: Python3網路爬蟲開發實戰教程 | 靜覓

如想了解更多爬蟲資訊,請關注我的個人微信公眾號:進擊的Coder

weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)

相關文章