前面幾篇博客主要介紹的是對於爬蟲所需要的庫以及相關知識點的介紹,這篇博客就是讓我們練習一下真正的爬蟲該怎麼寫。下面我主要講兩個實例,一個是b站新番的信息爬取,另一個是貓眼電影TOP100的相關信息爬取。
b站新番排行榜的爬取
首先我們需要request和re模塊,我採用函數結構話來寫爬蟲,你們可以看看,首先我們找到我們需要爬取的詳情頁b站新番排行榜。
我們需要爬取得信息有上面的新番排名,動漫名稱,播放量,彈幕量,追番數,以及最後的綜合得分。首先我們看一下網頁源代碼,發現信息都在源代碼中。怎麼看源代碼我就不講解了。我就直接開始講怎麼開始爬蟲吧。
1.構造請求
我採用函數的形式,設置相關參數,然後請求的時候就直接傳遞url這個參數就行啦。
import requests
from requests.exceptions import RequestException
import re
def get_page(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except RequestException:
return None
由於這個網站比較簡單,對於請求頭等參數我們不需要配置,然後我們直接調用它。
url='https://www.bilibili.com/ranking/bangumi/13/0/3'
html =get_one_page(url)
2. 正則表達式構造
這裏我就不顯示返回的結果,我們發現返回的信息就是網頁源代碼了,現在我們需要分析了,因爲每部新番在網頁展示的時候,也是一種列表形式的呈現,所以我們只要找到相似的列表單元格式,就可以用正則表達式匹配。在我看了一會源代碼發現:
<div class="num">1</div><div class="content"><div class="img"><a href="//bangumi.bilibili.com/anime/28006" target="_blank"><div class="lazy-img cover"><img alt="擅長捉弄的高木同學 第二季" src=""></div></a><!----></div><div class="info"><a href="//bangumi.bilibili.com/anime/28006" target="_blank" class="title">擅長捉弄的高木同學 第二季</a><div class="bangumi-info">全12話</div><div class="detail"><span class="data-box"><i class="b-icon play"></i>2890.3萬</span><span class="data-box"><i class="b-icon view"></i>100.0萬</span><span class="data-box"><i class="fav"></i>168.2萬</span></div><div class="pts"><div>3604904</div>綜合得分
</div></div><!----></div></li><li class="rank-item">
每一條新番的信息都是這樣呈現的,現在我們只需要將對應正則表達式寫出來就可以了,結合上篇博客,我花了一兩分寫的一個比較簡單的剛好匹配出來,你們可以看一下:
<div class="num">(\d+).*?href="(.*?)".*?<img alt="(.*?)" src.*?</i>(.*?)</span>.*?</i>(.*?)</span>.*?</i>(.*?)</span>.*?<div>(\d+)</div>
是不是也很簡單,當我們請求之後匹配正則表達式,就能得到我們想要的信息了。
pattern = re.compile('<div class="num">(\d+).*?href="(.*?)".*?<img alt="(.*?)" src.*?</i>(.*?)</span>.*?</i>(.*?)</span>.*?</i>(.*?)</span>.*?<div>(\d+)</div>', re.S)
items = re.findall(pattern, html)
items
看一下我們的運行結果:
[('1',
'//bangumi.bilibili.com/anime/28006',
'擅長捉弄的高木同學 第二季',
'2890.6萬',
'100.0萬',
'168.2萬',
'3604904'),
('2',
'//bangumi.bilibili.com/anime/26801',
'鬼滅之刃',
'2.2億',
'357.5萬',
'496.0萬',
'3518535'),
('3',
'//bangumi.bilibili.com/anime/28016',
'女高中生的虛度日常',
'3442.3萬',
'73.6萬',
'175.5萬',
'1253146'),
('4',
'//bangumi.bilibili.com/anime/26363',
'君主·埃爾梅羅二世事件簿 魔眼收集列車 Grace note',
'3025.5萬',
'41.0萬',
'197.0萬',
'598908'),
我只顯示前四條,你們想看詳細的可以自己試着運行一下。但我們發現爬取的鏈接地址是不完整的,這個時候,就需要我們對數據進行處理,我們看了一下源代碼,發現我們爬取是沒問題,只是這個網站省去了前部分公共的。https://www.bilibili.com
這個時候就需要我們處理了。
3.數據處理
舉一個例子,前綴url是這個https://www.bilibili.com
,後綴url是這個//bangumi.bilibili.com/anime/26363
,我們可以用正則表達式來處理
url1 ='//bangumi.bilibili.com/anime/28016'
url2 ='https://www.bilibili.com'
url2+re.sub('//','/',url1)
處理後的結果是:
'https://www.bilibili.com/bangumi.bilibili.com/anime/28016'
這樣就達到我們的要求了。
我們可以看看最後的完整代碼:
import requests
from requests.exceptions import RequestException
import re
def get_page(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except RequestException:
return None
url='https://www.bilibili.com/ranking/bangumi/13/0/3'
html =get_page(url)
pattern = re.compile('<div class="num">(\d+).*?href="(.*?)".*?<img alt="(.*?)" src.*?</i>(.*?)</span>.*?</i>(.*?)</span>.*?</i>(.*?)</span>.*?<div>(\d+)</div>', re.S)
items = re.findall(pattern, html)
url_1 ='https://www.bilibili.com'
for item in items:
url=url_1+re.sub('//','/',item[1])
print(item[1])
print(url)
print(item[2])
print(item[3])
print(item[4])
print(item[5])
print(item[6])
我們可以看看運行結果:
1
https://www.bilibili.com/bangumi.bilibili.com/anime/28006
擅長捉弄的高木同學 第二季
2891.4萬
100.0萬
168.2萬
3604904
2
https://www.bilibili.com/bangumi.bilibili.com/anime/26801
鬼滅之刃
2.2億
357.6萬
496.0萬
3518535
3
https://www.bilibili.com/bangumi.bilibili.com/anime/28016
女高中生的虛度日常
3443.0萬
73.6萬
175.5萬
1253146
4
https://www.bilibili.com/bangumi.bilibili.com/anime/26363
君主·埃爾梅羅二世事件簿 魔眼收集列車 Grace note
3026.0萬
41.0萬
197.0萬
598908
5
https://www.bilibili.com/bangumi.bilibili.com/anime/27993
Dr.STONE 石紀元
3327.9萬
45.1萬
169.0萬
550751
我發現達到我們的要求,一個基本爬蟲就完成了,至於保存我後面會慢慢講解,第一個案例就到這裏了(最後小編髮現自己賊傻,其實那個url不是番劇url,需要微調,後期我再修改一下)
貓眼排行的爬取
我們需要抓取的目標站點爲http://maoyan.com/board/4,打開之後便可以查看到榜單信息,如圖所示。
排名第一的電影是霸王別姬,頁面中顯示的有效信息有影片名稱、主演、上映時間、上映地區、評分、圖片等信息。
將網頁滾動到最下方,可以發現有分頁的列表,直接點擊第2頁,觀察頁面的URL和內容發生了怎樣的變化,
可以發現頁面的URL變成http://maoyan.com/board/4?offset=10,比之前的URL多了一個參數,那就是offset=10,而目前顯示的結果是排行11-20名的電影,初步推斷這是一個偏移量的參數。再點擊下一頁,發現頁面的URL變成了http://maoyan.com/board/4?offset=20,參數offset變成了20,而顯示的結果是排行21~30的電影。
由此可以總結出規律,offset代表偏移量值,如果偏移量爲n,則顯示的電影序號就是n+1到n+10,每頁顯示10個。所以,如果想獲取TOP100電影,只需要分開請求10次,而10次的offset參數分別設置爲0、10、20、…90即可,這樣獲取不同的頁面之後,再用正則表達式提取出相關信息,就可以得到TOP100的所有電影信息了。
下面我們開始編寫爬蟲。
1. 構造請求
這個和上面b站新番排行榜是一樣的,我們就不重複,直接調用,但我們需要加入一個請求頭信息,不然那可能會返回403,這樣運行之後,就可以成功獲取首頁的源代碼了。獲取源代碼後,就需要解析頁面,提取出我們想要的信息。
def get_page(url):
try:
response = requests.get(url,headers=headers)
if response.status_code == 200:
return response.text
return None
except RequestException:
return None
url = 'http://maoyan.com/board/4'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
2.正則提取
我們直接觀察源代碼分析,找到對應的規則,用正則表達式進行編寫,可以看到,一部電影信息對應的源代碼是一個dd節點,我們用正則表達式來提取這裏面的一些電影信息。首先,需要提取它的排名信息。而它的排名信息是在class爲board-index的i節點內,這裏利用非貪婪匹配來提取i節點內的信息,隨後需要提取電影的圖片。可以看到,後面有a節點,其內部有兩個img節點。經過檢查後發現,第二個img節點的data-src屬性是圖片的鏈接。這裏提取第二個img節點的data-src屬性,再往後,需要提取電影的名稱,它在後面的p節點內,class爲name。所以,可以用name做一個標誌位,然後進一步提取到其內a節點的正文內容,再提取主演、發佈時間、評分等內容時,都是同樣的原理。最後,正則表達式寫爲:
<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>
這樣就能匹配到我們想要的信息,爲了更好 的顯示我們爬取的數據,我們將格式微微調整然後打印
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:
print('index', item[0])
print('image', item[1])
print('title', item[2])
print('actor', item[3].strip()[3:])
print('time', item[4].strip()[5:])
print('score', item[5]+ item[6])
打印出來的結果:
index 1
image https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c
title 霸王別姬
actor 張國榮,張豐毅,鞏俐
time 1993-01-01
score 9.5
index 2
image https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@160w_220h_1e_1c
title 肖申克的救贖
actor 蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓
time 1994-09-10(加拿大)
score 9.5
index 3
image https://p0.meituan.net/movie/289f98ceaa8a0ae737d3dc01cd05ab052213631.jpg@160w_220h_1e_1c
title 羅馬假日
actor 格利高裏·派克,奧黛麗·赫本,埃迪·艾伯特
time 1953-09-02(美國)
score 9.1
3.整合代碼
最後,實現main()方法來調用前面實現的方法,將單頁的電影結果打印出來:
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_page(url)
parse_page(html)
到此爲止,我們就完成了單頁電影的提取,也就是首頁的10部電影。
4.分頁爬取
因爲我們需要抓取的是TOP100的電影,所以還需要遍歷一下,給這個鏈接傳入offset參數,實現其他90部電影的爬取,此時添加如下調用即可:
for i in range(10):
main(offset=i * 10)
time.sleep(1)
5.完整代碼
到此爲止,我們的貓眼電影TOP100的爬蟲就全部完成了,再稍微整理一下,完整的代碼如下:
def get_page(url):
try:
response = requests.get(url,headers=headers)
if response.status_code == 200:
return response.text
return None
except RequestException:
return None
url = 'http://maoyan.com/board/4'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
def parse_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:
print('index', item[0])
print('image', item[1])
print('title', item[2])
print('actor', item[3].strip()[3:])
print('time', item[4].strip()[5:])
print('score', item[5]+ item[6])
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_page(url)
parse_page(html)
if __name__ == '__main__':
for i in range(10):
main(offset=i * 10)
time.sleep(1)
貓眼存在輕微的反爬蟲,如果速度過快,則會無響應,所以這裏又增加了一個延時等待。最後我們看看運行結果,只顯示部分:
index 1
image https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c
title 霸王別姬
actor 張國榮,張豐毅,鞏俐
time 1993-01-01
score 9.5
index 2
image https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@160w_220h_1e_1c
title 肖申克的救贖
actor 蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓
time 1994-09-10(加拿大)
score 9.5
index 3
image https://p0.meituan.net/movie/289f98ceaa8a0ae737d3dc01cd05ab052213631.jpg@160w_220h_1e_1c
title 羅馬假日
actor 格利高裏·派克,奧黛麗·赫本,埃迪·艾伯特
time 1953-09-02(美國)
score 9.1
index 4
image https://p1.meituan.net/movie/6bea9af4524dfbd0b668eaa7e187c3df767253.jpg@160w_220h_1e_1c
title 這個殺手不太冷
actor 讓·雷諾,加里·奧德曼,娜塔莉·波特曼
time 1994-09-14(法國)
score 9.5
index 5
image https://p1.meituan.net/movie/b607fba7513e7f15eab170aac1e1400d878112.jpg@160w_220h_1e_1c
title 泰坦尼克號
actor 萊昂納多·迪卡普里奧,凱特·溫絲萊特,比利·贊恩
time 1998-04-03
score 9.5
index 6
image https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e041108.jpg@160w_220h_1e_1c
title 唐伯虎點秋香
actor 周星馳,鞏俐,鄭佩佩
time 1993-07-01(中國香港)
score 9.1
index 7
image https://p0.meituan.net/movie/46c29a8b8d8424bdda7715e6fd779c66235684.jpg@160w_220h_1e_1c
title 魂斷藍橋
actor 費雯·麗,羅伯特·泰勒,露塞爾·沃特森
time 1940-05-17(美國)
score 9.2
index 8
image https://p0.meituan.net/movie/223c3e186db3ab4ea3bb14508c709400427933.jpg@160w_220h_1e_1c
title 亂世佳人
actor 費雯·麗,克拉克·蓋博,奧利維婭·德哈維蘭
time 1939-12-15(美國)
score 9.1
index 9
image https://p1.meituan.net/movie/ba1ed511668402605ed369350ab779d6319397.jpg@160w_220h_1e_1c
title 天空之城
actor 寺田農,鷲尾真知子,龜山助清
time 1992-05-01
score 9.0
index 10
image https://p0.meituan.net/movie/b0d986a8bf89278afbb19f6abaef70f31206570.jpg@160w_220h_1e_1c
title 辛德勒的名單
actor 連姆·尼森,拉爾夫·費因斯,本·金斯利
time 1993-12-15(美國)
score 9.2
這個爬蟲是不是很簡單,通過這兩個簡單的實例我們瞭解到爬蟲的基礎流程,也大致掌握前面這些庫的用法,此外我們在做數據採集少不了保存,後面我會有一篇博客專門介紹這個。