一起學爬蟲——通過爬取豆瓣電影top250學習requests庫的使用
學習一門技術最快的方式是做專案,在做專案的過程中對相關的技術查漏補缺。
本文通過爬取豆瓣top250電影學習python requests的使用。
1、準備工作
在pycharm中安裝request庫
請看上圖,在pycharm中依次點選:File->Settings。然後會彈出下圖的介面:
點選2中左上角的“+”按鈕,彈出下圖的介面:
在右上角的查詢框輸入requests,然後點選“Install Package”按鈕安裝requests外掛。
2、目標
抓取每部電影的名字、主演、評分、圖片等資訊, 並儲存在txt文字檔案中。
3、分析豆瓣top250電影首頁
1、分析豆瓣電影top250的網頁https://movie.douban.com/top250,在瀏覽器中開啟該網頁:
可以看到一頁顯示25部電影。250部電影分10顯示:
要抓取250部電影需要抓取10次。下面是每一頁的網頁連結:
https://movie.douban.com/top250?start=0&filter= 首頁,等效於https://movie.douban.com/top250 https://movie.douban.com/top250?start=25&filter= #第2頁 https://movie.douban.com/top250?start=50&filter= #第3頁 ... ... ... https://movie.douban.com/top250?start=225&filter= #第10頁
通過分析上面的連結可以得出一個規律:start=0顯示1到25部電影,start=25顯示26到50部電影,以此類推,在抓取每一頁的電影時,是需要改變start的值即可。
4、抓取豆瓣電影top250首頁
程式碼如下:
import requests import re import json headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"} url = 'https://movie.douban.com/top250?start=0&filter=' proxies = { "http": "http://123.207.96.189:80" } response = requests.get(url, proxies = proxies,headers=headers) text = response.text print(text)
通過上述的程式碼我們可以學習到requests類庫中如何使用headers資訊和設定代理。
headers頭資訊和proxies代理資訊都是定義為dict詞典變數,然後在呼叫requests.get()方法時傳輸這個兩個引數:
response = requests.get(url, proxies = proxies,headers=headers)
很多網站都有反爬蟲的措施,對於沒有headers頭資訊的請求一律認為是爬蟲,禁止該請求的訪問。因此在每次爬取網頁時都需要加上headers頭資訊。
對於訪問過於頻繁的請求,客戶端的IP會被禁止訪問,因此設定代理可以將請求偽裝成來自不同的IP,前提是要保證代理的IP地址是有效的。
5、抓取每一部電影的資訊
檢視網頁原始碼有兩種方式:
通過滑鼠右鍵點選檢視“檢視原始檔”選項,或者通過瀏覽器的開發者工具選項,然後點選Network檢視原始碼:
下圖是一部電影在HTML網頁中的顯示,我們要做的就是從HTML中提取出電影名稱、主演、圖片等資訊。
提取電影的資訊需要用到正則表示式。
通過上圖可以看到一部電影的資訊對應的原始碼是<div class="item">
節點,紅色框所示。我們先用正則表示式提取到每部電影的所有資訊:
regix = '<div class="item">'
class為pic的div節點包含電影的排名和電影圖片資訊,提取電影排名和電影圖片資訊的正則表:
regix = '<div class="item">.*?<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">'
class為info的div標籤中包含了電影的名字、導演和演員等資訊,電影名字是在class為hd的div的節點內,<span class="title">
節點內包含的是電影的名字,<span class="other">
節點內包含的是電影的別名,上圖中的褐色框部分,因此提取電影名字的正則表示式為:
regix = '<div class="item">.*?<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">.*?div
class="info.*?class="hd".*?class="title">(.*?)</span>.*?class="other">(.*?)</span>'
class為bd的節點內包含的是電影的導演和主演資訊,其中class為“”的p節點內包含的是電影的導演和演員資訊,其中還包含了<br>
標籤,上圖中的紫色框部分,為了提取電影導演和演員的資訊,正則表示式改寫為:
regix = '<div class="item">.*?<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">.*?div
class="info.*?class="hd".*?class="title">(.*?)</span>.*?class="other">(.*?)</span>.*?<div class="bd">.*?<p class="">(.*?)<br>(.*?)</p>'
class為start的div標籤中包含的是電影的星級和評分,上圖黑色框部分。提取星級和評分的規則和提取電影排名、圖片等資訊類似,最後提取整個電影資訊的正則表示式為:
regix = '<div class="item">.*?<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">.*?div class="info.*?class="hd".*?class="title">(.*?)</span>.*?class="other">(.*?)</span>.*?<div class="bd">.*?<p class="">(.*?)<br>(.*?)</p>.*?class="star.*?<span class="(.*?)"></span>.*?span class="rating_num".*?average">(.*?)</span>'
提取一頁中所有電影資訊的程式碼如下:
import requests
import re
import json
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
url = 'https://movie.douban.com/top250?start=0&filter='
proxies = {
"http": "http://123.207.96.189:80"
}
response = requests.get(url, proxies = proxies,headers=headers)
text = response.text
regix = '<div class="item">.*?<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">.*?div class="info.*?class="hd".*?class="title">(.*?)</span>.*?class="other">(.*?)</span>.*?<div class="bd">.*?<p class="">(.*?)<br>(.*?)</p>.*?class="star.*?<span class="(.*?)"></span>.*?span class="rating_num".*?average">(.*?)</span>'
results = re.findall(regix,text,re.S)
for item in results:
print(item)
結果為:
('1', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg', '肖申克的救贖', ' / 月黑高飛(港) / 刺激1995(臺)', '\n 導演: 弗蘭克·德拉邦特 Frank Darabont 主演: 蒂姆·羅賓斯 Tim Robbins /...', '\n 1994 / 美國 / 犯罪 劇情\n ', 'rating5-t', '9.6')
('2', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1910813120.jpg', '霸王別姬', ' / 再見,我的妾 / Farewell My Concubine', '\n 導演: 陳凱歌 Kaige Chen 主演: 張國榮 Leslie Cheung / 張豐毅 Fengyi Zha...', '\n 1993 / 中國大陸 香港 / 劇情 愛情 同性\n ', 'rating5-t', '9.6')
('3', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p511118051.jpg', '這個殺手不太冷', ' / 殺手萊昂 / 終極追殺令(臺)', '\n 導演: 呂克·貝鬆 Luc Besson 主演: 讓·雷諾 Jean Reno / 娜塔莉·波特曼 ...', '\n 1994 / 法國 / 劇情 動作 犯罪\n ', 'rating45-t', '9.4')
('4', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p510876377.jpg', '阿甘正傳', ' / 福雷斯特·岡普', '\n 導演: Robert Zemeckis 主演: Tom Hanks / Robin Wright Penn / Gary Sinise', '\n 1994 / 美國 / 劇情 愛情\n ', 'rating45-t', '9.4')
('5', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p510861873.jpg', '美麗人生', ' / 一個快樂的傳說(港) / Life Is Beautiful', '\n 導演: 羅伯託·貝尼尼 Roberto Benigni 主演: 羅伯託·貝尼尼 Roberto Beni...', '\n 1997 / 義大利 / 劇情 喜劇 愛情 戰爭\n ', 'rating5-t', '9.5')
('6', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p457760035.jpg', '泰坦尼克號', ' / 鐵達尼號(港 / 臺)', '\n 導演: 詹姆斯·卡梅隆 James Cameron 主演: 萊昂納多·迪卡普里奧 Leonardo...', '\n 1997 / 美國 / 劇情 愛情 災難\n ', 'rating45-t', '9.3')
('7', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1606727862.jpg', '千與千尋', ' / 神隱少女(臺) / Spirited Away', '\n 導演: 宮崎駿 Hayao Miyazaki 主演: 柊瑠美 Rumi Hîragi / 入野自由 Miy...', '\n 2001 / 日本 / 劇情 動畫 奇幻\n ', 'rating45-t', '9.3')
('8', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p492406163.jpg', '辛德勒的名單', ' / 舒特拉的名單(港) / 辛德勒名單', '\n 導演: 史蒂文·斯皮爾伯格 Steven Spielberg 主演: 連姆·尼森 Liam Neeson...', '\n 1993 / 美國 / 劇情 歷史 戰爭\n ', 'rating5-t', '9.5')
('9', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p513344864.jpg', '盜夢空間', ' / 潛行凶間(港) / 全面啟動(臺)', '\n 導演: 克里斯托弗·諾蘭 Christopher Nolan 主演: 萊昂納多·迪卡普里奧 Le...', '\n 2010 / 美國 英國 / 劇情 科幻 懸疑 冒險\n ', 'rating45-t', '9.3')
('10', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1461851991.jpg', '機器人總動員', ' / 瓦力(臺) / 太空奇兵·威E(港)', '\n 導演: 安德魯·斯坦頓 Andrew Stanton 主演: 本·貝爾特 Ben Burtt / 艾麗...', '\n 2008 / 美國 / 愛情 科幻 動畫 冒險\n ', 'rating45-t', '9.3')
('11', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p524964016.jpg', '忠犬八公的故事', ' / 忠犬小八(臺) / 秋田犬八千(港)', '\n 導演: 萊塞·霍爾斯道姆 Lasse Hallström 主演: 理查·基爾 Richard Ger...', '\n 2009 / 美國 英國 / 劇情\n ', 'rating45-t', '9.3')
('12', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p579729551.jpg', '三傻大鬧寶萊塢', ' / 三個傻瓜(臺) / 作死不離3兄弟(港)', '\n 導演: 拉庫馬·希拉尼 Rajkumar Hirani 主演: 阿米爾·汗 Aamir Khan / 卡...', '\n 2009 / 印度 / 劇情 喜劇 愛情 歌舞\n ', 'rating45-t', '9.2')
('13', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p511146807.jpg', '海上鋼琴師', ' / 聲光伴我飛(港) / 一九零零的傳奇', '\n 導演: 朱塞佩·託納多雷 Giuseppe Tornatore 主演: 蒂姆·羅斯 Tim Roth / ...', '\n 1998 / 義大利 / 劇情 音樂\n ', 'rating45-t', '9.2')
('14', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1910824951.jpg', '放牛班的春天', ' / 歌聲伴我心(港) / 唱詩班男孩', '\n 導演: 克里斯托夫·巴拉蒂 Christophe Barratier 主演: 傑拉爾·朱諾 Géra...', '\n 2004 / 法國 瑞士 德國 / 劇情 音樂\n ', 'rating45-t', '9.2')
('15', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2455050536.jpg', '大話西遊之大聖娶親', ' / 西遊記完結篇仙履奇緣 / 齊天大聖西遊記', '\n 導演: 劉鎮偉 Jeffrey Lau 主演: 周星馳 Stephen Chow / 吳孟達 Man Tat Ng...', '\n 1995 / 香港 中國大陸 / 喜劇 愛情 奇幻 冒險\n ', 'rating45-t', '9.2')
('16', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p479682972.jpg', '楚門的世界', ' / 真人Show(港) / 真人戲', '\n 導演: 彼得·威爾 Peter Weir 主演: 金·凱瑞 Jim Carrey / 勞拉·琳妮 Lau...', '\n 1998 / 美國 / 劇情 科幻\n ', 'rating45-t', '9.2')
('17', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2190556185.jpg', '教父', ' / Mario Puzo's The Godfather', '\n 導演: 弗朗西斯·福特·科波拉 Francis Ford Coppola 主演: 馬龍·白蘭度 M...', '\n 1972 / 美國 / 劇情 犯罪\n ', 'rating45-t', '9.2')
('18', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2539252713.jpg', '龍貓', ' / 鄰居託託羅 / 鄰家的豆豆龍', '\n 導演: 宮崎駿 Hayao Miyazaki 主演: 日高法子 Noriko Hidaka / 阪本千夏 Ch...', '\n 1988 / 日本 / 動畫 奇幻 冒險\n ', 'rating45-t', '9.1')
('19', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2206088801.jpg', '星際穿越', ' / 星際啟示錄(港) / 星際效應(臺)', '\n 導演: 克里斯托弗·諾蘭 Christopher Nolan 主演: 馬修·麥康納 Matthew Mc...', '\n 2014 / 美國 英國 加拿大 冰島 / 劇情 科幻 冒險\n ', 'rating45-t', '9.2')
('20', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1363250216.jpg', '熔爐', ' / 無聲吶喊(港) / 漩渦', '\n 導演: 黃東赫 Dong-hyuk Hwang 主演: 孔侑 Yoo Gong / 鄭有美 Yu-mi Jeong ...', '\n 2011 / 韓國 / 劇情\n ', 'rating45-t', '9.2')
('21', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2233971046.jpg', '無間道', ' / Infernal Affairs / Mou gaan dou', '\n 導演: 劉偉強 / 麥兆輝 主演: 劉德華 / 梁朝偉 / 黃秋生', '\n 2002 / 香港 / 劇情 犯罪 懸疑\n ', 'rating45-t', '9.1')
('22', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1312700744.jpg', '當幸福來敲門', ' / 尋找快樂的故事(港) / 追求快樂', '\n 導演: 加布里爾·穆奇諾 Gabriele Muccino 主演: 威爾·史密斯 Will Smith ...', '\n 2006 / 美國 / 劇情 傳記 家庭\n ', 'rating45-t', '9.0')
('23', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1454261925.jpg', '觸不可及', ' / 閃亮人生(港) / 逆轉人生(臺)', '\n 導演: 奧利維·那卡什 Olivier Nakache / 艾力克·託蘭達 Eric Toledano 主...', '\n 2011 / 法國 / 劇情 喜劇\n ', 'rating45-t', '9.2')
('24', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p663036666.jpg', '怦然心動', ' / 萌動青春 / 青春萌動', '\n 導演: 羅伯·萊納 Rob Reiner 主演: 瑪德琳·卡羅爾 Madeline Carroll / 卡...', '\n 2010 / 美國 / 劇情 喜劇 愛情\n ', 'rating45-t', '9.0')
('25', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1963126880.jpg', '亂世佳人', ' / 飄', '\n 導演: 維克多·弗萊明 Victor Fleming / 喬治·庫克 George Cukor 主演: 費...', '\n 1939 / 美國 / 劇情 歷史 愛情 戰爭\n ', 'rating45-t', '9.2')
從上面的結果看到電影的星級是rating5-t、rating45-t、rating4-t這樣的字元代表五星、四星半、四星等。轉換程式碼為:
def star_transfor(str):
if str == 'rating5-t':
return '五星'
elif str == 'rating45-t' :
return '四星半'
elif str == 'rating4-t':
return '四星'
elif str == 'rating35-t' :
return '三星半'
elif str == 'rating3-t':
return '三星'
elif str == 'rating25-t':
return '兩星半'
elif str == 'rating2-t':
return '兩星'
elif str == 'rating15-t':
return '一星半'
elif str == 'rating1-t':
return '一星'
else:
return '無星'
在爬取網頁時,會將圖片也爬取下來,下面的程式碼是將每部電影的圖片儲存到本地:
def down_image(url,headers):
r = requests.get(url,headers = headers)
filename = re.search('/public/(.*?)$',url,re.S).group(1)
with open(filename,'wb') as f:
f.write(r.content)
爬取到網頁的資訊後,一般會將爬取的資訊儲存到資料庫或文字檔案中,下面的程式碼是將爬取到的電影資訊儲存到文字檔案中:
def write_movies_file(str):
with open('douban_film.txt','a',encoding='utf-8') as f:
f.write(json.dumps(str,ensure_ascii=False) + '\n')
寫入文字檔案用到了json庫的dumps方法,該方法實現了字典的序列化,並且要指定ensure_ascii引數為False,保證中文不亂碼。
write_movies_file方法傳入的是一個字典的引數,因此在爬取到一部電影的資訊時,需要將電影資訊格式化為一個字典,程式碼為:
results = re.findall(regix, text, re.S)
for item in results:
yield {
'電影名稱' : item[2] + ' ' + re.sub(' ','',item[3]),
'導演和演員' : re.sub(' ','',item[4].strip()),
'評分': star_transfor(item[6].strip()) + '/' + item[7] + '分',
'排名' : item[0]
}
到目前為止,我們已經實現了爬取一頁電影的資訊,一頁包含有25部電影,250部電影有10頁,之前已經提到過,改變start的值即可抓取不同頁的電影,下面的程式碼是構造10頁電影url的程式碼:
for offset in range(0, 250, 25):
url = 'https://movie.douban.com/top250?start=' + str(offset) +'&filter='
爬取豆瓣電影top250的完整程式碼如下:
import requests
import re
import json
def parse_html(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
response = requests.get(url, headers=headers)
text = response.text
regix = '<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">.*?div class="info.*?class="hd".*?class="title">(.*?)</span>.*?class="other">' \
'(.*?)</span>.*?<div class="bd">.*?<p class="">(.*?)<br>(.*?)</p>.*?class="star.*?<span class="(.*?)"></span>.*?' \
'span class="rating_num".*?average">(.*?)</span>'
results = re.findall(regix, text, re.S)
for item in results:
down_image(item[1],headers = headers)
yield {
'電影名稱' : item[2] + ' ' + re.sub(' ','',item[3]),
'導演和演員' : re.sub(' ','',item[4].strip()),
'評分': star_transfor(item[6].strip()) + '/' + item[7] + '分',
'排名' : item[0]
}
def main():
for offset in range(0, 250, 25):
url = 'https://movie.douban.com/top250?start=' + str(offset) +'&filter='
for item in parse_html(url):
print(item)
write_movies_file(item)
def write_movies_file(str):
with open('douban_film.txt','a',encoding='utf-8') as f:
f.write(json.dumps(str,ensure_ascii=False) + '\n')
def down_image(url,headers):
r = requests.get(url,headers = headers)
filename = re.search('/public/(.*?)$',url,re.S).group(1)
with open(filename,'wb') as f:
f.write(r.content)
def star_transfor(str):
if str == 'rating5-t':
return '五星'
elif str == 'rating45-t' :
return '四星半'
elif str == 'rating4-t':
return '四星'
elif str == 'rating35-t' :
return '三星半'
elif str == 'rating3-t':
return '三星'
elif str == 'rating25-t':
return '兩星半'
elif str == 'rating2-t':
return '兩星'
elif str == 'rating15-t':
return '一星半'
elif str == 'rating1-t':
return '一星'
else:
return '無星'
if __name__ == '__main__':
main()
總結
使用requests爬取一個網頁的基本步驟:
- 設定頭資訊。
- 設定代理
- 使用get方法爬取網頁
- 使用正則表示式提取相應的資訊
- 下載圖片
- 將爬取到的資料儲存到文字檔案或資料庫中。
通過本文可以學習到如何使用requests庫爬取網頁,使用正則表示式提取網頁的資訊,下載圖片,儲存資訊到文字檔案中。