Python3網絡爬蟲實戰---2七、Requests與正則表達式抓取貓眼電影排行

上一篇文章: Python3網絡爬蟲實戰---2六、正則表達式
下一篇文章:

本節咱們利用 Requests 和正則表達式來抓取貓眼電影 TOP100 的相關內容,Requests 相較於 Urllib 使用更加方便,而目前咱們尚未系統學習 HTML 解析庫,因此可能對 HTML 的解析庫不是很瞭解,因此本節咱們選用正則表達式來做爲解析工具。html

1. 本節目標

本節咱們要提取出貓眼電影 TOP100 榜的電影名稱、時間、評分、圖片等信息,提取的站點 URL 爲:http://maoyan.com/board/4,提取的結果咱們以文件形式保存下來。git

2. 準備工做

在本節開始以前請確保已經正確安裝好了 Requests 庫,若是沒有安裝能夠參考第一章的安裝說明。github

3. 抓取分析

本節咱們須要抓取的目標站點爲:http://maoyan.com/board/4,打開以後即可以查看到榜單的信息,如圖 3-11 所示:正則表達式

clipboard.png

圖 3-11 榜單信息
排名第一的電影是霸王別姬,頁面中顯示的有效信息有影片名稱、主演、上映時間、上映地區、評分、圖片等信息。
網頁下滑到最下方能夠發現有分頁的列表,咱們點擊一下第二頁觀察一下頁面的URL和內容發生了怎樣的變化,如圖 3-12 所示:json

clipboard.png

圖 3-12 頁面 URL 變化
能夠發現頁面的 URL 變成了:http://maoyan.com/board/4?off...,相比以前的URL多了一個參數,那就是 offset=10,而目前顯示的結果是排行 11-20 名的電影,初步推斷這是一個偏移量的參數,咱們再點擊下一頁,發現頁面的 URL 變成了:http://maoyan.com/board/4?off...,參數 offset 變成了 20,而顯示的結果是排行 21-30 的電影。
由此咱們能夠總結出規律,offset 表明了一個偏移量值,若是偏移量爲 n,則顯示的電影序號就是 n+1 到 n+10,每頁顯示 10 個。因此咱們若是想獲取 TOP100 電影,只須要分開請求 10 次,而 10 次的 offset 參數設置爲 0,10,20,...,90 便可,這樣咱們獲取不一樣的頁面結果以後再用正則表達式提取出相關信息就能夠獲得 TOP100 的全部電影信息了。segmentfault

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 所示:

clipboard.png

圖 3-13 源代碼
注意這裏不要在 Elements 選項卡直接查看源碼,此處的源碼可能通過 JavaScript 的操做而和原始請求的不一樣,咱們須要從Network選項卡部分查看原始請求獲得的源碼。
查看其中的一個條目的源代碼如圖 3-14 所示:

clipboard.png

圖 3-14 源代碼
能夠看到一部電影信息對應的源代碼是一個 dd 節點,咱們用正則表達式來提取這裏面的一些電影信息,首先咱們須要提取它的排名信息,而它的排名信息是在 class 爲 board-index 的 i 節點內,因此因此這裏利用非貪婪匹配來提取 i 節點內的信息,正則表達式寫爲:

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

隨後咱們須要提取電影的圖片,能夠看到在後面有個 a 節點,其內部有兩個 img 節點,通過檢查後發現第二個 img 節點的 data-src屬性是圖片的連接,在這裏咱們提取第二個 img 節點的 data-src屬性,因此正則能夠改寫以下:

<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)"

再日後咱們須要提取電影的名稱,它在後面的 p 節點內,class 爲 name,因此咱們能夠用 name 作一個標誌位,而後進一步提取到其內 a 節點的正文內容,正則改寫以下:

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

隨後若是須要再提取主演、發佈時間、評分等內容的話都是一樣的原理,最後正則表達式寫爲:

<dd>.*?board-index.*?>(.*?)</i>.*?data-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>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',
        re.S)
    items = re.findall(pattern, html)
    print(items)

這樣咱們就能夠成功將一頁的 10 個電影信息都提取出來了,是一個列表形式,輸出結果以下:

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

但這樣還不夠,數據比較雜亂,咱們再將匹配結果處理一下,遍歷提取結果並生成字典,方法改寫以下:

def parse_one_page(html):
    pattern = re.compile(
        '<dd>.*?board-index.*?>(.*?)</i>.*?data-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()
        }

這樣咱們就能夠成功提取出電影的排名、圖片、標題、演員、時間、評份內容了,並把它賦值爲一個個的字典,造成結構化數據,運行結果以下:

{'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,)+'\n')

經過調用 write_to_json() 方法便可實現將字典寫入到文本文件的過程,此處的 content 參數就是一部電影的提取結果,是一個字典。

7. 整合代碼

最後實現一個 main() 方法負責調用以上實現的方法,將單頁的電影結果寫入到文件,實現以下:

def 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 部電影的爬取,添加以下調用便可:

if __name__ == '__main__':
    for i in range(10):
        main(offset=i * 10)

這裏還須要將 main() 方法修改一下,接收一個 offset 值做爲偏移量,而後構造 URL 進行爬取,實現以下:

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 的爬蟲就所有完成了,再稍微整理一下,完整的代碼以下:

import json
import requests
from requests.exceptions import RequestException
import re
import 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>.*?data-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) + '\n')

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)

如今貓眼多了反爬蟲,若是速度過快則會無響應,因此這裏又增長了一個延時等待。

  1. 運行結果

最後咱們運行一下代碼,相似的輸出結果以下:

{'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 所示:

clipboard.png

圖 3-15 運行結果
能夠看到電影信息也已所有保存到了文本文件中,大功告成!

10. 本節代碼

本節的代碼地址爲:
https://github.com/oldmarkfac...

11. 結語

本節咱們經過爬取貓眼 TOP100 的電影信息練習了 Requests 和正則表達式的用法,這是最基礎的實例,但願你們能夠經過這個實例對爬蟲的實現有一個最基本的思路,也對這兩個庫的用法有更深一步的體會。

相關文章
相關標籤/搜索