[Python從零到壹] 六.網絡爬蟲之BeautifulSoup爬取豆瓣TOP250電影詳解 | 【生長吧!Python】(python從0到1)
https://blog.csdn.net/Eastmount
https://github.com/eastmountyxz/Python-zero2one
文章目錄
一.分析網頁DOM樹結構
1.分析網頁結構及簡單爬取
2.定位節點及網頁翻頁分析
二.爬取豆瓣電影信息
三.鏈接跳轉分析及詳情頁面爬取
四.總結
前文賞析:
[Python從零到壹] 一.為什么我們要學Python及基礎語法詳解
[Python從零到壹] 二.語法基礎之條件語句、循環語句和函數
[Python從零到壹] 三.語法基礎之文件操作、CSV文件讀寫及面向對象
[Python從零到壹] 四.網絡爬蟲之入門基礎及正則表達式抓取博客案例
[Python從零到壹] 五.網絡爬蟲之BeautifulSoup基礎語法萬字詳解
[Python從零到壹] 六.網絡爬蟲之BeautifulSoup爬取豆瓣TOP250電影詳解
分析網頁DOM樹結構
爬取豆瓣電影信息列表
鏈接跳轉分析
爬取每部電影對應的詳細信息
本文從實戰出發,讓讀者初步了解分析網頁結構方法并調用BeautifulSoup技術爬取網絡數據,后面章節將進一步深入講解。
一.分析網頁DOM樹結構
1.分析網頁結構及簡單爬取
豆瓣(Douban)是一個社區網站,創立于2005年3月6日。該網站以書影音起家,提供關于書籍、電影、音樂等作品的信息,其作品描述和評論都是由用戶提供(User-Generated Content,簡稱UGC),是Web 2.0網站中具有特色的一個網站。該網站提供了書影音推薦、線下同城活動、小組話題交流等多種服務功能,致力于幫助都市人群發現生活中有用的事物。
本文主要介紹BeautifulSoup技術爬取豆瓣電影排名前250名的電影信息。第一部分將介紹分析網頁DOM樹結構。爬取豆瓣的地址為:
https://movie.douban.com/top250?format=text
上圖中顯示了豆瓣熱門的250部電影的信息,包括電影中文名稱、英文名稱、導演、主演、評分、評價數等信息,接下來需要對其進行DOM樹結構分析。HTML網頁是以標簽對的形式出現,如< html >< /html >、< div >< /div >等,這種標簽對呈樹形結構顯示,通常稱為DOM樹結構。
在得到一個網頁之后,我們需要結合瀏覽器對其進行元素分析。比如豆瓣電影網站,選中第一部電影《肖申克的救贖》,右鍵鼠標“檢查”(Chrome瀏覽器稱為“檢查”,其他瀏覽器可能稱為“審查元素”等),如下圖2所示。
顯示結果如圖3所示,可以發現它是在< div class=”article” >< /div >路徑下,由很多個< li >< /li >組成,每一個< li >< /li >分別對應一部電影的信息。其中,電影《肖申克的救贖》HTML中對應內容為:
通過class值為“item”可以定位電影的信息。調用BeautifulSoup擴展包的find_all(attrs={“class”:“item”}) 函數可以獲取其信息。
對應的HTML部分代碼如下:
下面通過Python3代碼可以獲取電影的信息,調用BeautifulSoup中的find_all()函數獲取< div class=’item’ >的信息,其結果如圖4所示。
test01.py
# -*- coding:utf-8 -*- # By:Eastmount CSDN import urllib.request import re from bs4 import BeautifulSoup # 爬蟲函數 def crawl(url, headers): page = urllib.request.Request(url, headers=headers) page = urllib.request.urlopen(page) contents = page.read() #print(contents) soup = BeautifulSoup(contents, "html.parser") print('豆瓣電影250: 序號 \t影片名\t 評分 \t評價人數') for tag in soup.find_all(attrs={"class":"item"}): content = tag.get_text() content = content.replace('\n','') #刪除多余換行 print(content, '\n') # 主函數 if __name__ == '__main__': url = 'http://movie.douban.com/top250?format=text' headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'} crawl(url, headers)
運行結構如圖4所示,爬取了豆瓣Top250的第一頁電影的信息,包括序號、影片名、導演及主演信息、評分、評價人數等。
注意:urllib.error.HTTPError: HTTP Error 418
如果直接使用urllib.request.urlopen(url)會提示該錯誤,這是因為爬蟲被攔截,需要模擬瀏覽器訪問,這里可以打開瀏覽器按下F12,找到對應Headers內容,然后在Python代碼中設置User-Agent即可模擬該瀏覽器請求。
urlopen()函數:
Python2:urllib2.urlopen(url)
Python3:urllib.request.urlopen(url)
urlopen()函數用于創建一個表示遠程url的類文件對象,然后像操作本地文件一樣操作這個類文件對象來獲取遠程數據
read()函數:
調用read()讀取網頁內容并賦值給contents變量
BeautifulSoup函數:
soup = BeautifulSoup(contents, “html.parser”)
調用BeautifulSoup解析所抓取網頁源碼的DOM樹結構
find_all()函數:
調用BeautifulSoup的find_all()函數獲取屬性class為“item”的所有值,并調用代碼content.replace(’\n’,’’)將換行符替換為空值,從而實現刪除多余換行,最后循環輸出結果。
2.定位節點及網頁翻頁分析
通過前一部分我們獲取了電影的簡介信息,但是這些信息是融合在一起的,而在數據分析時,通常需要將某些具有使用價值的信息提取出來,并存儲至數組、列表或數據庫中,比如電影名稱、演員信息、電影評分等特征。
(1) 文本分析。從獲取的電影簡介文本信息中提取某些特定的值,通常采用字符串處理方法進行提取。
(2) 節點定位。在寫爬蟲過程中定位相關節點,然后進行爬取所需節點操作,最后賦值給變量或存儲到數據庫中。
本部分將結合BeautifulSoup技術,采用節點定位方法獲取具體的值。點開HTML網頁,檢查對應的< li >< /li >節點,可以看到該電影的構成情況,再定位節點內容,如< span class=“title” >節點可以獲取標題,< div class=“star” >節點可以獲取電影評分和評價人數。
獲取節點的核心代碼如下,定位class屬性為“item”的div布局后,再調用find_all()函數查找class屬性為title的標簽,并獲取第一個值輸出,即title[0]。接著調用find()函數爬取評分信息,通過get_text()函數獲取內容。
for tag in soup.find_all(attrs={"class":"item"}): title = tag.find_all(attrs={"class":"title"}) #電影名稱 info = tag.find(attrs={"class":"star"}).get_text() #爬取評分和評論數 print(title[0]) print(info.replace('\n','')) # 肖申克的救贖 # 9.72279813人評價
講到這里,我們第一頁的25部電影信息就爬取成功了,而該網頁共10頁,每頁顯示25部電影,如何獲取這250部完整的電影信息呢?這就涉及到鏈接跳轉和網站的翻頁分析。網站的翻頁分析通常有四種方法:
點擊下一頁分析url網址,分析它們之間的規律,這種方法的網站通常采用GET方法進行傳值,而有的網站采用局部刷新技術,翻頁后的url仍然不變。
獲取“下一頁”或頁碼的超鏈接,再依次調用urllib2.urlopen(url)函數訪問URL并實現網頁跳轉。
分析網站Networks提交請求的參數,通過Python設置參數實現翻頁,常用于POST表單。
采用網頁自動操作技術,獲取下一頁按鈕或超鏈接進行自動點擊跳轉,如selenium技術中的鼠標點擊事件。
本文主要采用第一種分析方法,后面講述Selenium技術時,會介紹鼠標模擬點擊事件操作的跳轉方法。
通過點擊圖6中的第2頁、第3頁、第10頁,我們可以看到網頁URL的變化如下。
第2頁URL:https://movie.douban.com/top250?start=25&filter= 第3頁URL:https://movie.douban.com/top250?start=50&filter= 第10頁URL:https://movie.douban.com/top250?start=225&filter=
它是存在一定規律的,top250?start=25表示獲取第2頁(序號為26到50號)的電影信息;top250?start=50表示獲取第3頁(序號為51到75號)的電影信息,依次類推,我們寫一個循環即可獲取完整的250部電影信息。核心代碼如下:
i = 0 while i<10: num = i*25 #每次顯示25部 URL序號按25增加 url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter=' crawl(url) i = i + 1
注意:當i初始值為0,num值為0,獲取第1頁信息;當i增加為1,num值為25,獲取第2頁信息;當i增加為9,num值為225,獲取第10頁的信息。
講到這里,爬取豆瓣網電影信息的DOM樹結構分析、網頁鏈接跳轉已經分析完成,下一小節是講解完整的代碼。
二.爬取豆瓣電影信息
完整代碼為test02.py文件,如下所示。
test02.py
# -*- coding: utf-8 -*- # By:Eastmount CSDN import urllib.request import re from bs4 import BeautifulSoup import codecs #-------------------------------------爬蟲函數------------------------------------- def crawl(url, headers): page = urllib.request.Request(url, headers=headers) page = urllib.request.urlopen(page) contents = page.read() soup = BeautifulSoup(contents, "html.parser") infofile.write("") print('爬取豆瓣電影250: \n') for tag in soup.find_all(attrs={"class":"item"}): #爬取序號 num = tag.find('em').get_text() print(num) infofile.write(num + "\r\n") #電影名稱 name = tag.find_all(attrs={"class":"title"}) zwname = name[0].get_text() print('[中文名稱]', zwname) infofile.write("[中文名稱]" + zwname + "\r\n") #網頁鏈接 url_movie = tag.find(attrs={"class":"hd"}).a urls = url_movie.attrs['href'] print('[網頁鏈接]', urls) infofile.write("[網頁鏈接]" + urls + "\r\n") #爬取評分和評論數 info = tag.find(attrs={"class":"star"}).get_text() info = info.replace('\n',' ') info = info.lstrip() print('[評分評論]', info) #獲取評語 info = tag.find(attrs={"class":"inq"}) if(info): #避免沒有影評調用get_text()報錯 content = info.get_text() print('[影評]', content) infofile.write(u"[影評]" + content + "\r\n") print('') #-------------------------------------主函數------------------------------------- if __name__ == '__main__': #存儲文件 infofile = codecs.open("Result_Douban.txt", 'a', 'utf-8') #消息頭 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'} #翻頁 i = 0 while i<10: print('頁碼', (i+1)) num = i*25 #每次顯示25部 URL序號按25增加 url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter=' crawl(url, headers) infofile.write("\r\n\r\n") i = i + 1 infofile.close()
運行結果如圖7所示,爬取了電影名稱、網頁連接、評分評論數和影評等信息。
并且將爬取的250部電影信息存儲到“Result_Douban.txt”文件中,如下圖所示。
在代碼中,主函數定義循環依次獲取不同頁碼的URL,然后調用crawl(url)函數對每頁的電影信息進行定向爬取。在crawl(url)函數中,通過urlopen()函數訪問豆瓣電影網址,然后調用BeautifulSoup函數進行HTML分析,前面第一部分講解了每部電影都位于< li >< div class=“item” >…< /div >< /li >節點下,故采用如下for循環依次定位到每部電影,然后再進行定向爬取。
for tag in soup.find_all(attrs={"class":"item"}): #分別爬取每部電影具體的信息
具體方法如下。
(1) 獲取序號
序號對應的HTML源碼如圖8所示,需要定位到< em class >1< /em >節點,通過find(‘em’)函數獲取具體的內容。
對應的代碼如下:
num = tag.find('em').get_text() print(num)
(2) 獲取電影名稱
電影名稱對應的HTML源碼如圖9所示,包括class='title’對應中文名稱和英文名稱,class='other’對應電影其他名稱。
對應的代碼如下,因為HTML中包含兩個title,即< span class=‘title’ >< /span >,所以使用下面的函數獲取兩個標題:
tag.find_all(attrs={“class”:“title”})
但這里我們僅需要中文標題,則直接通過變量name[0]獲取其第一個值,即為中文名稱,再調用get_text()函數獲取其內容。
name = tag.find_all(attrs={"class":"title"}) zwname = name[0].get_text() print('[中文名稱]', zwname) infofile.write("[中文名稱]" + zwname + "\r\n")
同時,上述代碼調用codecs庫進行了文件處理,其中文件操作的核心代碼如下,打開文件三個參數分別是:文件名、讀寫方式、編碼方式,此處文件名為“Result_Douban.txt”,采用文件寫方式(a),編碼方式是utf-8。
infofile = codecs.open("Result_Douban.txt", 'a', 'utf-8') #打開文件 infofile.write(num+" "+name+"\r\n") #寫文件 infofile.close() #關閉文件
3.獲取電影鏈接
電影鏈接對應的HTML源碼如上圖9所示,定位到< div class=‘hd’ >節點下的< a >< /a >節點,然后獲取屬性位href的值,即:attrs[‘href’]。
url_movie = tag.find(attrs={"class":"hd"}).a urls = url_movie.attrs['href'] print('[網頁鏈接]', urls)
獲取評分和內容的方法一樣,調用函數即可獲取:
find(attrs={“class”:“star”}).get_text()
但是存在一個問題,它輸出的結果將評分數和評價數放在了一起,如“9.4 783221人評價”,而通常在做分析的時候,我們是將評分數存在一個變量中,評價數存在另一變量中。
這就需要進行簡單的文本處理,這里推薦大家使用前面講述過的正則表達式來處理,將此段代碼修改如下,調用re.compile(r’\d+.?\d*’)獲取字符串中的數字。第一個數字為電影的分數,第二個數字為評論數。
#爬取評分和評論數 info = tag.find(attrs={"class":"star"}).get_text() info = info.replace('\n',' ') info = info.lstrip() print(info) mode = re.compile(r'\d+\.?\d*') #正則表達式獲取數字 print(mode.findall(info)) i = 0 for n in mode.findall(info): if i==0: print('[分數]', n) infofile.write("[分數]" + n + "\r\n") elif i==1: print('[評論]', n) infofile.write(u"[評論]" + n + "\r\n") i = i + 1
獲取的結果前后對比如圖10所示。
三.鏈接跳轉分析及詳情頁面爬取
https://movie.douban.com/subject/1292052/
該網頁打開如圖11所示。
1.爬取詳情頁面基本信息
下面對詳情頁面進行DOM樹節點分析,其基本信息位于< div class=‘article’ >…< /div >標簽下,核心內容位于該節點下的子節點中,即< div id=‘info’ >…< /div >。使用如下代碼獲取內容:
info = soup.find(attrs={"id":"info"}) print(info.get_text())
other = soup.find(attrs={"class":"related-info"}).get_text() print other.replace('\n','').replace(' ','') #過濾空格和換行
3.爬取詳情頁面電影熱門評論信息
熱門評論信息位于< div id=‘hot-comments’ >…< /div >節點下,然后獲取節點下的多個class屬性為“comment-item”的div布局,如下圖所示。在使用find()或find_all()函數進行爬取時,需要注意標簽屬性是class還是id,或是其它,必須與之對應一致,才能正確爬取。
#評論 print('\n評論信息:') for tag in soup.find_all(attrs={"id":"hot-comments"}): for comment in tag.find_all(attrs={"class":"comment-item"}): com = comment.find("p").get_text() #爬取段落p print com.replace('\n','').replace(' ','')
完整代碼如下:
test03.py
# -*- coding: utf-8 -*- # By:Eastmount CSDN import urllib.request import re from bs4 import BeautifulSoup import codecs #-----------------------------------爬取詳細信息------------------------------------- def getInfo(url, headers): page = urllib.request.Request(url, headers=headers) page = urllib.request.urlopen(page) content = page.read() soup = BeautifulSoup(content, "html.parser") #電影簡介 print('電影簡介:') info = soup.find(attrs={"id":"info"}) print(info.get_text()) other = soup.find(attrs={"class":"related-info"}).get_text() print(other.replace('\n','').replace(' ','')) #評論 print('\n評論信息:') for tag in soup.find_all(attrs={"id":"hot-comments"}): for comment in tag.find_all(attrs={"class":"comment-item"}): com = comment.find("p").get_text() print(com.replace('\n','').replace(' ','')) print("\n\n\n----------------------------------------------------------------") #-------------------------------------爬蟲函數------------------------------------- def crawl(url, headers): page = urllib.request.Request(url, headers=headers) page = urllib.request.urlopen(page) contents = page.read() soup = BeautifulSoup(contents, "html.parser") for tag in soup.find_all(attrs={"class":"item"}): #爬取序號 num = tag.find('em').get_text() print(num) #電影名稱 name = tag.find_all(attrs={"class":"title"}) zwname = name[0].get_text() print('[中文名稱]', zwname) #網頁鏈接 url_movie = tag.find(attrs={"class":"hd"}).a urls = url_movie.attrs['href'] print('[網頁鏈接]', urls) #爬取評分和評論數 info = tag.find(attrs={"class":"star"}).get_text() info = info.replace('\n',' ') info = info.lstrip() #正則表達式獲取數字 mode = re.compile(r'\d+\.?\d*') i = 0 for n in mode.findall(info): if i==0: print('[電影分數]', n) elif i==1: print('[電影評論]', n) i = i + 1 #獲取評語 getInfo(urls, headers) #-------------------------------------主函數------------------------------------- if __name__ == '__main__': #消息頭 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'} #翻頁 i = 0 while i<10: print('頁碼', (i+1)) num = i*25 #每次顯示25部 URL序號按25增加 url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter=' crawl(url, headers) i = i + 1
其中爬取的《龍貓》電影信息輸出如圖16所示。
講到這里,使用BeautifulSoup技術分析爬取豆瓣電影前250部電影信息的實例已經講解完畢,但在實際爬取過程中可能由于某些頁面不存在會導致爬蟲停止,這時需要使用異常語句“try-except-finally”進行處理。
同時,爬取過程中需要結合自己所需數據進行定位節點,存儲至本地文件中,也需要結合字符串處理過濾一些多余的空格或換行。
四.總結
該系列所有代碼-:
https://github.com/eastmountyxz/Python-zero2one
2020年在github的綠瓷磚終于貼完了第一年提交2100余次,獲得1500多+stars,開源93個倉庫,300個粉絲。挺開心的,希望自己能堅持在github打卡五年,督促自己不斷前行。簡單總結下,最滿意的資源是YQ爆發時,去年2月分享的輿情分析和情感分析,用這系列有溫度的代碼為武漢加油;最高贊的是Python圖像識別系列,也獲得了第一位來自國外開發者的貢獻補充;最花時間的是Wannacry逆向系列,花了我兩月逆向分析,幾乎成為了全網最詳細的該蠕蟲分析;還有AI系列、知識圖譜實戰、CVE復現、APT報告等等。當然也存在很多不足之處,希望來年分享更高質量的資源,也希望能將安全和AI頂會論文系列總結進來,真誠的希望它們能幫助到大家,感恩有你,一起加油~
希望能與大家一起在華為云社區共同承載,原文地址:https://blog.csdn.net/Eastmount/article/details/108887652
【生長吧!Python】有獎征文火熱進行中:https://bbs.huaweicloud.com/blogs/278897
(By:娜璋之家 Eastmount 2021-07-07 夜于武漢)
參考文獻如下:
北京豆網科技有限公司——豆瓣
[python爬蟲] BeautifulSoup和Selenium對比爬取豆瓣Top250電影信息 - Eastmount
Crummy.com網站. BeautifulSoup 4.2.0文檔
[python知識] 爬蟲知識之BeautifulSoup庫安裝及簡單介紹 - Eastmount
GitHub Python 網絡
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。