簡介
剛接觸python不久,做一個小項目來練練手。前幾天看了《戰(zhàn)狼2》,發(fā)現(xiàn)它在最新上映的電影里面是排行第一的,如下圖所示。準備把豆瓣上對它的影評(短評)做一個分析。

目標總覽
主要做了三件事:
抓取網(wǎng)頁數(shù)據(jù)
清理數(shù)據(jù)
用詞云進行展示
使用的python版本是3.5.
運行環(huán)境:jupyer notebook,如在其他環(huán)境下運行報錯了,請查看評論區(qū)的討論,里面有一些解決辦法。
一、抓取網(wǎng)頁數(shù)據(jù)
第一步要對網(wǎng)頁進行訪問,python中使用的是urllib庫。代碼如下:
from urllibimportrequestresp = request.urlopen('https://movie.douban.com/nowplaying/hangzhou/')html_data = resp.read().decode('utf-8')
其中https://movie.douban.com/nowp...,可以在瀏覽器中輸入該網(wǎng)址進行查看。
html_data是字符串類型的變量,里面存放了網(wǎng)頁的html代碼。
輸入print(html_data)可以查看,如下圖所示:

第二步,需要對得到的html代碼進行解析,從里面提取我們需要的數(shù)據(jù)。
在python中使用BeautifulSoup庫進行html代碼的解析。
(注:如果沒有安裝此庫,則使用pip install BeautifulSoup進行安裝即可?。?/p>
BeautifulSoup使用的格式如下:
BeautifulSoup(html,"html.parser")
第一個參數(shù)為需要提取數(shù)據(jù)的html,第二個參數(shù)是指定解析器,然后使用find_all()讀取html標簽中的內(nèi)容。
但是html中有這么多的標簽,該讀取哪些標簽呢?其實,最簡單的辦法是可以打開我們爬取網(wǎng)頁的html代碼,然后查看我們需要的數(shù)據(jù)在哪個html標簽里面,再進行讀取就可以了。如下圖所示:

從上圖中可以看出在div id="nowplaying"標簽開始是我們想要的數(shù)據(jù),里面有電影的名稱、評分、主演等信息。所以相應的代碼編寫如下:
frombs4importBeautifulSoupasbssoup = bs(html_data,'html.parser')? ? nowplaying_movie = soup.find_all('div', id='nowplaying')nowplaying_movie_list = nowplaying_movie[0].find_all('li', class_='list-item')
其中nowplaying_movie_list?是一個列表,可以用print(nowplaying_movie_list[0])查看里面的內(nèi)容,如下圖所示:

python學習交流群:923414804,群內(nèi)每天分享干貨,包括最新的企業(yè)級案例學習資料和零基礎(chǔ)入門教程,歡迎小伙伴入群學習。
在上圖中可以看到data-subject屬性里面放了電影的id號碼,而在img標簽的alt屬性里面放了電影的名字,因此我們就通過這兩個屬性來得到電影的id和名稱。(注:打開電影短評的網(wǎng)頁時需要用到電影的id,所以需要對它進行解析),編寫代碼如下:
nowplaying_list = []foriteminnowplaying_movie_list:nowplaying_dict = {}? ? ? ? ? ? ? ? nowplaying_dict['id'] = item['data-subject']fortag_img_iteminitem.find_all('img'):? ? ? ? ? ? ? ? ? ? ? ? nowplaying_dict['name'] = tag_img_item['alt']? ? ? ? ? ? ? ? ? ? ? ? nowplaying_list.append(nowplaying_dict)
其中列表nowplaying_list中就存放了最新電影的id和名稱,可以使用print(nowplaying_list)進行查看,如下圖所示:

可以看到和豆瓣網(wǎng)址上面是匹配的。這樣就得到了最新電影的信息了。接下來就要進行對最新電影短評進行分析了。例如《戰(zhàn)狼2》的短評網(wǎng)址為:https://movie.douban.com/subject/26363254/comments?start=0&limit=20
其中26363254就是電影的id,start=0表示評論的第0條評論。
接下來接對該網(wǎng)址進行解析了。打開上圖中的短評頁面的html代碼,我們發(fā)現(xiàn)關(guān)于評論的數(shù)據(jù)是在div標簽的comment屬性下面,如下圖所示:

因此對此標簽進行解析,代碼如下:
requrl='https://movie.douban.com/subject/'+ nowplaying_list[0]['id'] +'/comments'+'?'+'start=0'+'&limit=20'resp= request.urlopen(requrl)html_data= resp.read().decode('utf-8')soup= bs(html_data,'html.parser')comment_div_lits= soup.find_all('div', class_='comment')
此時在comment_div_lits?列表中存放的就是div標簽和comment屬性下面的html代碼了。在上圖中還可以發(fā)現(xiàn)在p標簽下面存放了網(wǎng)友對電影的評論,如下圖所示:

因此對comment_div_lits?代碼中的html代碼繼續(xù)進行解析,代碼如下:
eachCommentList = [];foritemincomment_div_lits:ifitem.find_all('p')[0].stringisnotNone:? ? ? ? ? ? ? ? eachCommentList.append(item.find_all('p')[0].string)
使用print(eachCommentList)查看eachCommentList列表中的內(nèi)容,可以看到里面存里我們想要的影評。如下圖所示:

好的,至此我們已經(jīng)爬取了豆瓣最近播放電影的評論數(shù)據(jù),接下來就要對數(shù)據(jù)進行清洗和詞云顯示了。
二、數(shù)據(jù)清洗
為了方便進行數(shù)據(jù)進行清洗,我們將列表中的數(shù)據(jù)放在一個字符串數(shù)組中,代碼如下:
comments =''forkinrange(len(eachCommentList)):? ? comments = comments + (str(eachCommentList[k])).strip()
使用print(comments)進行查看,如下圖所示:

可以看到所有的評論已經(jīng)變成一個字符串了,但是我們發(fā)現(xiàn)評論中還有不少的標點符號等。這些符號對我們進行詞頻統(tǒng)計時根本沒有用,因此要將它們清除。所用的方法是正則表達式。python中正則表達式是通過re模塊來實現(xiàn)的。代碼如下:
importrepattern = re.compile(r'[\u4e00-\u9fa5]+')filterdata = re.findall(pattern, comments)cleaned_comments =''.join(filterdata)
繼續(xù)使用print(cleaned_comments)語句進行查看,如下圖所示:

我們可以看到此時評論數(shù)據(jù)中已經(jīng)沒有那些標點符號了,數(shù)據(jù)變得“干凈”了很多。
因此要進行詞頻統(tǒng)計,所以先要進行中文分詞操作。在這里我使用的是結(jié)巴分詞。如果沒有安裝結(jié)巴分詞,可以在控制臺使用pip install jieba進行安裝。(注:可以使用pip list查看是否安裝了這些庫)。代碼如下所示:
import jieba#分詞包import pandas as pd? segment = jieba.lcut(cleaned_comments)words_df=pd.DataFrame({'segment':segment})
因為結(jié)巴分詞要用到pandas,所以我們這里加載了pandas包??梢允褂脀ords_df.head()查看分詞之后的結(jié)果,如下圖所示:

從上圖可以看到我們的數(shù)據(jù)中有“看”、“太”、“的”等虛詞(停用詞),而這些詞在任何場景中都是高頻詞,并且沒有實際的含義,所以我們要將他們清除。
我把停用詞放在一個stopwords.txt文件中,將我們的數(shù)據(jù)與停用詞進行比對即可(注:只要在百度中輸入stopwords.txt,就可以下載到該文件)。去停用詞代碼如下:
stopwords=pd.read_csv("stopwords.txt",index_col=False,quoting=3,sep="\t",names=['stopword'], encoding='utf-8')#quoting=3全不引用words_df=words_df[~words_df.segment.isin(stopwords.stopword)]
繼續(xù)使用words_df.head()語句來查看結(jié)果,如下圖所示,停用詞已經(jīng)被除去了。

接下來就要進行詞頻統(tǒng)計了,代碼如下:
import numpy#numpy計算包words_stat=words_df.groupby(by=['segment'])['segment'].agg({"計數(shù)":numpy.size})words_stat=words_stat.reset_index().sort_values(by=["計數(shù)"],ascending=False)
用words_stat.head()進行查看,結(jié)果如下:

由于我們前面只是爬取了第一頁的評論,所以數(shù)據(jù)有點少,在最后給出的完整代碼中,我爬取了10頁的評論,所以數(shù)據(jù)還是有一定參考價值的。
三、用詞云進行顯示
代碼如下:
importmatplotlib.pyplotasplt%matplotlib inlineimportmatplotlibmatplotlib.rcParams['figure.figsize'] = (10.0,5.0)fromwordcloudimportWordCloud#詞云包wordcloud=WordCloud(font_path="simhei.ttf",background_color="white",max_font_size=80)#指定字體類型、字體大小和字體顏色word_frequence = {x[0]:x[1]forxinwords_stat.head(1000).values}word_frequence_list = []forkeyinword_frequence:? ? temp = (key,word_frequence[key])? ? word_frequence_list.append(temp)wordcloud=wordcloud.fit_words(word_frequence_list)plt.imshow(wordcloud)
其中simhei.ttf使用來指定字體的,可以在百度上輸入simhei.ttf進行下載后,放入程序的根目錄即可。顯示的圖像如下:

到此為止,整個項目的介紹就結(jié)束了。由于自己也還是個初學者,接觸python不久,代碼寫的并不好。而且第一次寫技術(shù)博客,表達的有些冗余,請大家多多包涵,有不對的地方,請大家批評指正。以后我也會將自己做的小項目以這種形式寫在博客上和大家一起交流!最后貼上完整的代碼。
完整代碼
#coding:utf-8
__author__ = 'hang'
import warnings
warnings.filterwarnings("ignore")
import jieba? ? #分詞包
import numpy? ? #numpy計算包
import codecs? #codecs提供的open方法來指定打開的文件的語言編碼,它會在讀取的時候自動轉(zhuǎn)換為內(nèi)部unicode
import re
import pandas as pd?
import matplotlib.pyplot as plt
from urllib import request
from bs4 import BeautifulSoup as bs
%matplotlib inline
import matplotlib
matplotlib.rcParams['figure.figsize'] = (10.0, 5.0)
from wordcloud import WordCloud#詞云包
#分析網(wǎng)頁函數(shù)
def getNowPlayingMovie_list():?
? ? resp = request.urlopen('https://movie.douban.com/nowplaying/hangzhou/')? ? ? ?
? ? html_data = resp.read().decode('utf-8')? ?
? ? soup = bs(html_data, 'html.parser')? ?
? ? nowplaying_movie = soup.find_all('div', id='nowplaying')? ? ? ?
? ? nowplaying_movie_list = nowplaying_movie[0].find_all('li', class_='list-item')? ?
? ? nowplaying_list = []? ?
? ? for item in nowplaying_movie_list:? ? ? ?
? ? ? ? nowplaying_dict = {}? ? ? ?
? ? ? ? nowplaying_dict['id'] = item['data-subject']? ? ?
? ? ? ? for tag_img_item in item.find_all('img'):? ? ? ? ? ?
? ? ? ? ? ? nowplaying_dict['name'] = tag_img_item['alt']? ? ? ? ? ?
? ? ? ? ? ? nowplaying_list.append(nowplaying_dict)? ?
? ? return nowplaying_list
#爬取評論函數(shù)
def getCommentsById(movieId, pageNum):
? ? eachCommentList = [];
? ? if pageNum>0:
? ? ? ? start = (pageNum-1) * 20
? ? else:
? ? ? ? return False
? ? requrl = 'https://movie.douban.com/subject/' + movieId + '/comments' +'?' +'start=' + str(start) + '&limit=20'
? ? print(requrl)
? ? resp = request.urlopen(requrl)
? ? html_data = resp.read().decode('utf-8')
? ? soup = bs(html_data, 'html.parser')
? ? comment_div_lits = soup.find_all('div', class_='comment')
? ? for item in comment_div_lits:
? ? ? ? if item.find_all('p')[0].string is not None:? ?
? ? ? ? ? ? eachCommentList.append(item.find_all('p')[0].string)
? ? return eachCommentList
def main():
? ? #循環(huán)獲取第一個電影的前10頁評論
? ? commentList = []
? ? NowPlayingMovie_list = getNowPlayingMovie_list()
? ? for i in range(10):? ?
? ? ? ? num = i + 1
? ? ? ? commentList_temp = getCommentsById(NowPlayingMovie_list[0]['id'], num)
? ? ? ? commentList.append(commentList_temp)
? ? #將列表中的數(shù)據(jù)轉(zhuǎn)換為字符串
? ? comments = ''
? ? for k in range(len(commentList)):
? ? ? ? comments = comments + (str(commentList[k])).strip()
? ? #使用正則表達式去除標點符號
? ? pattern = re.compile(r'[\u4e00-\u9fa5]+')
? ? filterdata = re.findall(pattern, comments)
? ? cleaned_comments = ''.join(filterdata)
? ? #使用結(jié)巴分詞進行中文分詞
? ? segment = jieba.lcut(cleaned_comments)
? ? words_df=pd.DataFrame({'segment':segment})
? ? #去掉停用詞
? ? stopwords=pd.read_csv("stopwords.txt",index_col=False,quoting=3,sep="\t",names=['stopword'], encoding='utf-8')#quoting=3全不引用
? ? words_df=words_df[~words_df.segment.isin(stopwords.stopword)]
? ? #統(tǒng)計詞頻
? ? words_stat=words_df.groupby(by=['segment'])['segment'].agg({"計數(shù)":numpy.size})
? ? words_stat=words_stat.reset_index().sort_values(by=["計數(shù)"],ascending=False)
? ? #用詞云進行顯示
? ? wordcloud=WordCloud(font_path="simhei.ttf",background_color="white",max_font_size=80)
? ? word_frequence = {x[0]:x[1] for x in words_stat.head(1000).values}
? ? word_frequence_list = []
? ? for key in word_frequence:
? ? ? ? temp = (key,word_frequence[key])
? ? ? ? word_frequence_list.append(temp)
? ? wordcloud=wordcloud.fit_words(word_frequence_list)
? ? plt.imshow(wordcloud)
#主函數(shù)
main()
結(jié)果顯示如下:

上圖基本反映了《戰(zhàn)狼2》這部電影的情況。PS:我本人并不喜歡這部電影,內(nèi)容太空洞、太假,為了愛國而愛國,沒意思。哎,這兩年真是國產(chǎn)電影的低谷啊,沒有一部拿得出手的國產(chǎn)電影,看看人家印度拍的《摔跤吧,爸爸》那才是拍的有深度,同樣是表現(xiàn)愛國,國產(chǎn)電影還是需要向別的國家好好學學。