簡介
剛接觸python不久妈候,做一個小項(xiàng)目來練練手综看。前幾天看了《戰(zhàn)狼2》,發(fā)現(xiàn)它在最新上映的電影里面是排行第一的馒索,如下圖所示莹妒。準(zhǔn)備把豆瓣上對它的影評做一個分析。
目標(biāo)總覽
主要做了三件事:
抓取網(wǎng)頁數(shù)據(jù)
清理數(shù)據(jù)
用詞云進(jìn)行展示
使用的python版本是3.5.
一绰上、抓取網(wǎng)頁數(shù)據(jù)
第一步要對網(wǎng)頁進(jìn)行訪問旨怠,python中使用的是urllib庫。代碼如下:
from?urllib?import?request
resp?=?request.urlopen('https://movie.douban.com/nowplaying/hangzhou/')
html_data?=?resp.read().decode('utf-8')
其中https://movie.douban.com/nowplaying/hangzhou/是豆瓣最新上映的電影頁面蜈块,可以在瀏覽器中輸入該網(wǎng)址進(jìn)行查看鉴腻。
html_data是字符串類型的變量,里面存放了網(wǎng)頁的html代碼百揭。
輸入print(html_data)可以查看爽哎,如下圖所示:
第二步,需要對得到的html代碼進(jìn)行解析器一,得到里面提取我們需要的數(shù)據(jù)课锌。
在python中使用BeautifulSoup庫進(jìn)行html代碼的解析。
(注:如果沒有安裝此庫祈秕,則使用pip install BeautifulSoup進(jìn)行安裝即可C煜汀)
BeautifulSoup使用的格式如下:
BeautifulSoup(html,"html.parser")
第一個參數(shù)為需要提取數(shù)據(jù)的html,第二個參數(shù)是指定解析器请毛,然后使用find_all()讀取html標(biāo)簽中的內(nèi)容志鞍。
但是html中有這么多的標(biāo)簽,該讀取哪些標(biāo)簽?zāi)胤椒拢科鋵?shí)固棚,最簡單的辦法是我們可以打開我們爬取網(wǎng)頁的html代碼,然后查看我們需要的數(shù)據(jù)在哪個html標(biāo)簽里面仙蚜,再進(jìn)行讀取就可以了此洲。如下圖所示:
從上圖中可以看出在div id=”nowplaying“標(biāo)簽開始是我們想要的數(shù)據(jù),里面有電影的名稱鳍征、評分黍翎、主演等信息。所以相應(yīng)的代碼編寫如下:
from?bs4?import?BeautifulSoup?as?bs
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_movie_list 是一個列表艳丛,可以用print(nowplaying_movie_list[0])查看里面的內(nèi)容匣掸,如下圖所示:
在上圖中可以看到data-subject屬性里面放了電影的id號碼,而在img標(biāo)簽的alt屬性里面放了電影的名字氮双,因此我們就通過這兩個屬性來得到電影的id和名稱碰酝。(注:打開電影短評的網(wǎng)頁時需要用到電影的id,所以需要對它進(jìn)行解析)戴差,編寫代碼如下:
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)
其中列表nowplaying_list中就存放了最新電影的id和名稱送爸,可以使用print(nowplaying_list)進(jìn)行查看,如下圖所示:
可以看到和豆瓣網(wǎng)址上面是匹配的。這樣就得到了最新電影的信息了袭厂。接下來就要進(jìn)行對最新電影短評進(jìn)行分析了墨吓。例如《戰(zhàn)狼2》的短評網(wǎng)址為:https://movie.douban.com/subject/26363254/comments?start=0&limit=20
其中26363254就是電影的id,start=0表示評論的第0條評論纹磺。
接下來接對該網(wǎng)址進(jìn)行解析了帖烘。打開上圖中的短評頁面的html代碼,我們發(fā)現(xiàn)關(guān)于評論的數(shù)據(jù)是在div標(biāo)簽的comment屬性下面橄杨,如下圖所示:
因此對此標(biāo)簽進(jìn)行解析秘症,代碼如下:
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標(biāo)簽和comment屬性下面的html代碼了。在上圖中還可以發(fā)現(xiàn)在p標(biāo)簽下面存放了網(wǎng)友對電影的評論式矫,如下圖所示:
因此對comment_div_lits 代碼中的html代碼繼續(xù)進(jìn)行解析乡摹,代碼如下:
eachCommentList?=?[];
for?item?in?comment_div_lits:?
????????if?item.find_all('p')[0].string?is?not?None:?
????????????eachCommentList.append(item.find_all('p')[0].string)
使用print(eachCommentList)查看eachCommentList列表中的內(nèi)容,可以看到里面存里我們想要的影評采转。如下圖所示:
好的聪廉,至此我們已經(jīng)爬取了豆瓣最近播放電影的評論數(shù)據(jù),接下來就要對數(shù)據(jù)進(jìn)行清洗和詞云顯示了氏义。
二锄列、數(shù)據(jù)清洗
為了方便進(jìn)行數(shù)據(jù)進(jìn)行清洗,我們將列表中的數(shù)據(jù)放在一個字符串?dāng)?shù)組中惯悠,代碼如下:
comments?=?''
for?k?in?range(len(eachCommentList)):
????comments?=?comments?+?(str(eachCommentList[k])).strip()
使用print(comments)進(jìn)行查看邻邮,如下圖所示:
可以看到所有的評論已經(jīng)變成一個字符串了,但是我們發(fā)現(xiàn)評論中還有不少的標(biāo)點(diǎn)符號等克婶。這些符號對我們進(jìn)行詞頻統(tǒng)計時根本沒有用筒严,因此要將它們清除。所用的方法是正則表達(dá)式情萤。python中正則表達(dá)式是通過re模塊來實(shí)現(xiàn)的鸭蛙。代碼如下:
import?re
pattern?=?re.compile(r'[\u4e00-\u9fa5]+')
filterdata?=?re.findall(pattern,?comments)
cleaned_comments?=?''.join(filterdata)
繼續(xù)使用print(cleaned_comments)語句進(jìn)行查看,如下圖所示:
我們可以看到此時評論數(shù)據(jù)中已經(jīng)沒有那些標(biāo)點(diǎn)符號了筋岛,數(shù)據(jù)變得“干凈”了很多娶视。
因此要進(jìn)行詞頻統(tǒng)計,所以先要進(jìn)行中文分詞操作睁宰。在這里我使用的是結(jié)巴分詞肪获。如果沒有安裝結(jié)巴分詞,可以在控制臺使用pip install jieba進(jìn)行安裝柒傻。(注:可以使用pip list查看是否安裝了這些庫)孝赫。代碼如下所示:
import?jieba????#分詞包
import?pandas?as?pd??
segment?=?jieba.lcut(cleaned_comments)
words_df=pd.DataFrame({'segment':segment})
因?yàn)榻Y(jié)巴分詞要用到pandas,所以我們這里加載了pandas包红符∏啾可以使用words_df.head()查看分詞之后的結(jié)果伐债,如下圖所示:
從上圖可以看到我們的數(shù)據(jù)中有“看”、“太”致开、“的”等虛詞(停用詞)峰锁,而這些詞在任何場景中都是高頻時,并且沒有實(shí)際的含義喇喉,所以我們要他們進(jìn)行清除祖今。
我把停用詞放在一個stopwords.txt文件中,將我們的數(shù)據(jù)與停用詞進(jìn)行比對即可(注:只要在百度中輸入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)被出去了。
接下來就要進(jìn)行詞頻統(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()進(jìn)行查看莫辨,結(jié)果如下:
由于我們前面只是爬取了第一頁的評論,所以數(shù)據(jù)有點(diǎn)少毅访,在最后給出的完整代碼中沮榜,我爬取了10頁的評論,所數(shù)據(jù)還是有參考價值喻粹。
三蟆融、用詞云進(jìn)行顯示
代碼如下:
import?matplotlib.pyplot?as?plt
%matplotlib?inline
import?matplotlib
matplotlib.rcParams['figure.figsize']?=?(10.0,?5.0)
from?wordcloud?import?WordCloud#詞云包
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)
其中simhei.ttf使用來指定字體的守呜,可以在百度上輸入simhei.ttf進(jìn)行下載后型酥,放入程序的根目錄即可。顯示的圖像如下:
到此為止查乒,整個項(xiàng)目的介紹就結(jié)束了凑保。由于自己也還是個初學(xué)者统扳,接觸python不久,代碼寫的并不好。而且第一次寫技術(shù)博客岸更,表達(dá)的有些冗余,請大家多多包涵史汗,有不對的地方蛮艰,請大家批評指正。以后我也會將自己做的小項(xiàng)目以這種形式寫在博客上和大家一起交流拒担!最后貼上完整的代碼嘹屯。
完整代碼
#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()
????#使用正則表達(dá)式去除標(biāo)點(diǎn)符號
????pattern?=?re.compile(r'[\u4e00-\u9fa5]+')
????filterdata?=?re.findall(pattern,?comments)
????cleaned_comments?=?''.join(filterdata)
????#使用結(jié)巴分詞進(jìn)行中文分詞
????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)
????#用詞云進(jìn)行顯示
????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》這部電影的情況从撼。