本節(jié)我們利用 Requests 和正則表達(dá)式來抓取貓眼電影 TOP100 的相關(guān)內(nèi)容,Requests 相較于 Urllib 使用更加方便,而目前我們還沒有系統(tǒng)學(xué)習(xí) HTML 解析庫,所以可能對 HTML 的解析庫不是很了解,所以本節(jié)我們選用正則表達(dá)式來作為解析工具。
站在用戶的角度思考問題,與客戶深入溝通,找到烏蘇網(wǎng)站設(shè)計與烏蘇網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、域名與空間、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋烏蘇地區(qū)。
本節(jié)我們要提取出貓眼電影 TOP100 榜的電影名稱、時間、評分、圖片等信息,提取的站點 URL 為:http://maoyan.com/board/4,提取的結(jié)果我們以文件形式保存下來。
在本節(jié)開始之前請確保已經(jīng)正確安裝好了 Requests 庫,如果沒有安裝可以參考第一章的安裝說明。
本節(jié)我們需要抓取的目標(biāo)站點為:http://maoyan.com/board/4,打開之后便可以查看到榜單的信息,如圖 3-11 所示:
圖 3-11 榜單信息
排名第一的電影是霸王別姬,頁面中顯示的有效信息有影片名稱、主演、上映時間、上映地區(qū)、評分、圖片等信息。
網(wǎng)頁下滑到最下方可以發(fā)現(xiàn)有分頁的列表,我們點擊一下第二頁觀察一下頁面的URL和內(nèi)容發(fā)生了怎樣的變化,如圖 3-12 所示:
圖 3-12 頁面 URL 變化
可以發(fā)現(xiàn)頁面的 URL 變成了:http://maoyan.com/board/4?off...,相比之前的URL多了一個參數(shù),那就是 offset=10,而目前顯示的結(jié)果是排行 11-20 名的電影,初步推斷這是一個偏移量的參數(shù),我們再點擊下一頁,發(fā)現(xiàn)頁面的 URL 變成了:http://maoyan.com/board/4?off...,參數(shù) offset 變成了 20,而顯示的結(jié)果是排行 21-30 的電影。
由此我們可以總結(jié)出規(guī)律,offset 代表了一個偏移量值,如果偏移量為 n,則顯示的電影序號就是 n+1 到 n+10,每頁顯示 10 個。所以我們?nèi)绻氆@取 TOP100 電影,只需要分開請求 10 次,而 10 次的 offset 參數(shù)設(shè)置為 0,10,20,...,90 即可,這樣我們獲取不同的頁面結(jié)果之后再用正則表達(dá)式提取出相關(guān)信息就可以得到 TOP100 的所有電影信息了。
接下來我們用代碼實現(xiàn)這個過程,首先抓取第一頁的內(nèi)容,我們實現(xiàn)一個 get_one_page() 方法,傳入 url 參數(shù),然后將抓取的頁面結(jié)果返回,然后再實現(xiàn)一個 main() 方法調(diào)用一下,初步代碼實現(xiàn)如下:
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](http://maoyan.com/board/4)'
??? html = get_one_page(url)
??? print(html)
main()
Python資源分享qun 784758214 ,內(nèi)有安裝包,PDF,學(xué)習(xí)視頻,這里是Python學(xué)習(xí)者的聚集地,零基礎(chǔ),進(jìn)階,都?xì)g迎
這樣運行之后我們就可以成功獲取首頁的源代碼了,獲取源代碼之后我們就需要對頁面進(jìn)行解析,提取出我們想要的信息。
接下來我們回到網(wǎng)頁看一下頁面的真實源碼,在開發(fā)者工具中 Network 監(jiān)聽,然后查看一下源代碼,如圖 3-13 所示:
圖 3-13 源代碼
注意這里不要在 Elements 選項卡直接查看源碼,此處的源碼可能經(jīng)過 JavaScript 的操作而和原始請求的不同,我們需要從Network選項卡部分查看原始請求得到的源碼。
查看其中的一個條目的源代碼如圖 3-14 所示:
圖 3-14 源代碼
可以看到一部電影信息對應(yīng)的源代碼是一個 dd 節(jié)點,我們用正則表達(dá)式來提取這里面的一些電影信息,首先我們需要提取它的排名信息,而它的排名信息是在 class 為 board-index 的 i 節(jié)點內(nèi),所以所以這里利用非貪婪匹配來提取 i 節(jié)點內(nèi)的信息,正則表達(dá)式寫為:
.*?board-index.*?>(.*?)
隨后我們需要提取電影的圖片,可以看到在后面有個 a 節(jié)點,其內(nèi)部有兩個 img 節(jié)點,經(jīng)過檢查后發(fā)現(xiàn)第二個 img 節(jié)點的 data-src屬性是圖片的鏈接,在這里我們提取第二個 img 節(jié)點的 data-src屬性,所以正則可以改寫如下:
.*?board-index.*?>(.*?).*?data-src="(.*?)"
再往后我們需要提取電影的名稱,它在后面的 p 節(jié)點內(nèi),class 為 name,所以我們可以用 name 做一個標(biāo)志位,然后進(jìn)一步提取到其內(nèi) a 節(jié)點的正文內(nèi)容,正則改寫如下:
.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?)
隨后如果需要再提取主演、發(fā)布時間、評分等內(nèi)容的話都是同樣的原理,最后正則表達(dá)式寫為:
.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?).*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*?
這樣我們一個正則表達(dá)式可以匹配一個電影的結(jié)果,里面匹配了 7 個信息,接下來我們通過調(diào)用 findall() 方法提取出所有的內(nèi)容,實現(xiàn)一個 parse_one_page() 方法如下:
def parse_one_page(html):
pattern = re.compile(
'.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?).*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*? ',
re.S)
items = re.findall(pattern, html)
print(items)
這樣我們就可以成功將一頁的 10 個電影信息都提取出來了,是一個列表形式,輸出結(jié)果如下:
[('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')]
但這樣還不夠,數(shù)據(jù)比較雜亂,我們再將匹配結(jié)果處理一下,遍歷提取結(jié)果并生成字典,方法改寫如下:
def parse_one_page(html):
pattern = re.compile(
'.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?).*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*? ',
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()
}
這樣我們就可以成功提取出電影的排名、圖片、標(biāo)題、演員、時間、評分內(nèi)容了,并把它賦值為一個個的字典,形成結(jié)構(gòu)化數(shù)據(jù),運行結(jié)果如下:
{'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(日本)'}
Python資源分享qun 784758214 ,內(nèi)有安裝包,PDF,學(xué)習(xí)視頻,這里是Python學(xué)習(xí)者的聚集地,零基礎(chǔ),進(jìn)階,都?xì)g迎
到此為止我們就成功提取了單頁的電影信息。
隨后我們將提取的結(jié)果寫入文件,在這里直接寫入到一個文本文件中,通過 json 庫的 dumps() 方法實現(xiàn)字典的序列化,并指定 ensure_ascii 參數(shù)為 False,這樣可以保證輸出的結(jié)果是中文形式而不是 Unicode 編碼,代碼實現(xiàn)如下:
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')
通過調(diào)用 write_to_json() 方法即可實現(xiàn)將字典寫入到文本文件的過程,此處的 content 參數(shù)就是一部電影的提取結(jié)果,是一個字典。
最后實現(xiàn)一個 main() 方法負(fù)責(zé)調(diào)用以上實現(xiàn)的方法,將單頁的電影結(jié)果寫入到文件,實現(xiàn)如下:
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 部電影就可以成功提取并保存到文本文件中了。
但我們需要抓取的是 TOP100 的電影,所以我們還需要遍歷一下給這個鏈接傳入一個 offset 參數(shù),實現(xiàn)其他 90 部電影的爬取,添加如下調(diào)用即可:
if __name__ == '__main__':
??? for i in range(10):
??????? main(offset=i * 10)
這里還需要將 main() 方法修改一下,接收一個 offset 值作為偏移量,然后構(gòu)造 URL 進(jìn)行爬取,實現(xià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)
到此為止,我們的貓眼電影 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('.*?board-index.*?>(\d+).*?data-src="(.*?)".*?name">(.*?).*?star">(.*?).*?releasetime">(.*?)'
+ '.*?integer">(.*?).*?fraction">(.*?).*? ', 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)
現(xiàn)在貓眼多了反爬蟲,如果速度過快則會無響應(yīng),所以這里又增加了一個延時等待。
最后我們運行一下代碼,類似的輸出結(jié)果如下:
{'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': '笠智眾,原節(jié)子,杉村春子', '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'}
Python資源分享qun 784758214 ,內(nèi)有安裝包,PDF,學(xué)習(xí)視頻,這里是Python學(xué)習(xí)者的聚集地,零基礎(chǔ),進(jìn)階,都?xì)g迎
中間的部分輸出結(jié)果已省略,可以看到這樣就成功把 TOP100 的電影信息爬取下來了。
這時我們再看下文本文件,結(jié)果如圖 3-15 所示:
圖 3-15 運行結(jié)果
可以看到電影信息也已全部保存到了文本文件中,大功告成!
本節(jié)我們通過爬取貓眼 TOP100 的電影信息練習(xí)了 Requests 和正則表達(dá)式的用法,這是最基礎(chǔ)的實例,希望大家可以通過這個實例對爬蟲的實現(xiàn)有一個最基本的思路,也對這兩個庫的用法有更深一步的體會。