Python爬取《流浪地球》豆瓣影評與資料分析
編者按
豬年春節的賀歲檔電影火爆異常,尤其是口碑爆棚的《流浪地球》更是收穫的無數影迷和國人的讚譽。事實勝於雄辯,資料是最有力度的事實。我們用python爬取了豆瓣的影評,客觀的分析一下這部國產的劃時代的科幻電影。事實證明,《流浪地球》是非常值得觀看的電影,並且這部電影正在帶領中國的科幻產業走向成熟。
大年初一《流浪地球》全國上映。在豆瓣評分上,首日開分站穩8分以上,延續了之前點映的高口碑。微博上跟著出現吳京客串31天與投資6000萬的熱搜。知乎上關於“如何評價劉慈欣小說改編的同名電影《流浪地球》”的回答引起了眾多人關注,包括該片導演郭帆的最高贊回答。
本篇文章爬取了豆瓣網上《流浪地球》的部分影評,並進行資料分析及視覺化處理。下面是爬取分析的整個過程,讓我們愉快開始吧!
一、網頁分析
豆瓣網從2017年10月開始全面禁止爬取資料。在非登入狀態下僅僅可以爬取200條短評,登入狀態下僅可以爬取500條資料。白天一分鐘最多可爬40次,晚上60次,超過次數就會封IP地址。小本聰爬取資料獲得400條時被封了IP,賬號被強制下線封號,之後發簡訊賬號恢復,因此不建議多次爬取(另外,有很多解決方法,請自行搜尋)。
獲取物件
-
評論使用者
-
評論內容
-
評分
-
評論日期
-
使用者所在城市
值得注意的是,在位址列我們會發現電影名字的ID編號為26266893(其他電影只需更換ID即可),並且每頁有20條短評,因此我爬取了20頁。評論頁面沒有使用者所在城市,需要進入使用者頁面獲取資訊。
二、資料獲取與儲存
1 獲取cookies
小本聰用的是Chrome瀏覽器,Ctrl+F12進入開發者工具頁面。F5重新整理一下出現資料,找到cookies、headers。
2 載入headers、cookies,並用requests庫獲取資訊
def get_content(id, page): 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'} cookies = {'cookie': 'bid=GOOb4vXwNcc; douban-fav-remind=1; ps=y; ue="[email protected]"; push_noty_num=0; push_doumail_num=0; ap=1; ll="108288"; dbcl2="181095881:BSb6IVAXxCI"; ck=Fd1S; ct=y'} url = "https://movie.douban.com/subject/" + str(id) + "/comments?start=" + str(page * 10) + "&limit=20&sort=new_score&status=P" res = requests.get(url, headers=headers, cookies=cookies)3 解析需求資料
此處運用xpath解析。發現有的使用者雖然給了評論,但是沒有給評分,所以score和date這兩個的xpath位置是會變動的。因此需要加判斷,如果發現score裡面解析的是日期,證明該條評論沒有給出評分。
for i in range(1, 21):# 每頁20個評論使用者 name = x.xpath('//*[@id="comments"]/div[{}]/div[2]/h3/span[2]/a/text()'.format(i)) # 下面是個大bug,如果有的人沒有評分,但是評論了,那麼score解析出來是日期,而日期所在位置spen[3]為空 score = x.xpath('//*[@id="comments"]/div[{}]/div[2]/h3/span[2]/span[2]/@title'.format(i)) date = x.xpath('//*[@id="comments"]/div[{}]/div[2]/h3/span[2]/span[3]/@title'.format(i)) m = '\d{4}-\d{2}-\d{2}' try: match = re.compile(m).match(score[0]) except IndexError: break if match is not None: date = score score = ["null"] else: pass content = x.xpath('//*[@id="comments"]/div[{}]/div[2]/p/span/text()'.format(i)) id = x.xpath('//*[@id="comments"]/div[{}]/div[2]/h3/span[2]/a/@href'.format(i)) try: city = get_city(id[0], i)# 呼叫評論使用者的ID城市資訊獲取 except IndexError: city = " " name_list.append(str(name[0])) score_list.append(str(score[0]).strip('[]\''))# bug 有些人評論了文字,但是沒有給出評分 date_list.append(str(date[0]).strip('[\'').split(' ')[0]) content_list.append(str(content[0]).strip()) city_list.append(city)
4 獲取電影名稱
從url上只能獲取電影的subject的8位ID數值,引起需要自行解析網頁獲取ID號對應的電影名稱,該功能是後期改進新增的,因此為避免現有程式碼改動多(偷個懶),採用了全域性變數賦值給 movie_name
,需要注意全域性變數呼叫時,要加 global
宣告一下。
pattern = re.compile('<div id="wrapper">.*?<div id="content">.*?<h1>(.*?) 短評</h1>', re.S) global movie_name movie_name = re.findall(pattern, res.text)[0]# list型別
5 資料儲存
由於資料不多,選擇CSV儲存即可。
def main(ID, pages): global movie_name for i in tqdm(range(0, pages)):# 豆瓣只開放500條評論 get_content(ID, i)# 第一個<mark data-id="2e982b73-88e2-41e8-a430-f7ae5a9af4bf" data-type="technologies">引數</mark>是豆瓣電影對應的id序號,第二個<mark data-id="2e982b73-88e2-41e8-a430-f7ae5a9af4bf" data-type="technologies">引數</mark>是想爬取的評論頁數 time.sleep(round(random.uniform(3, 5), 2))# 設定延時發出請求 infos = {'name': name_list, 'city': city_list, 'content': content_list, 'score': score_list, 'date': date_list} data = pd.DataFrame(infos, columns=['name', 'city', 'content', 'score', 'date']) data.to_csv(movie_name + ".csv")# 儲存名為 電影名.csv
三、資料分析與視覺化
1 獲取cookies
城市資訊篩選中文字
def translate(str): line = str.strip() p2 = re.compile('[^\u4e00-\u9fa5]')# 中文的編碼範圍是:\u4e00到\u9fa5 zh = " ".join(p2.split(line)).strip() zh = ",".join(zh.split()) str = re.sub("[A-Za-z0-9!!,%\[\],。]", "", zh) return str
匹配pyecharts支援的城市列表
d = pd.read_csv(csv_file, engine='python', encoding='utf-8') motion_list = [] for i in d['content']: try: s = round(SnowNLP(i).sentiments, 2) motion_list.append(s) except TypeError: continue result = {} for i in set(motion_list): result[i] = motion_list.count(i) return result
2 基於snownlp的情感分析
snownlp主要可以進行中文分詞(演算法是Character-Based Generative Model)、詞性標註(原理是TnT、3-gram 隱馬)、情感分析(官網木有介紹原理,但是指明購物類的評論的準確率較高,其實是因為它的語料庫主要是購物方面的,可以自己構建相關領域語料庫,替換原來的,準確率也挺不錯的)、文字分類(原理是樸素貝葉斯)、轉換拼音、繁體轉簡體、提取文字關鍵詞(原理是TextRank)、提取摘要(原理是TextRank)、分割句子、文字相似(原理是BM25)【摘自CSDN】。在看此之前,建議先看一下官網,裡面有最基礎的一些命令的介紹。官網連結:https://pypi.org/project/snownlp/
由於snownlp全部是unicode編碼,所以要注意資料是否為unicode編碼。因為是unicode編碼,所以不需要去除中文文本里面含有的英文,因為都會被轉碼成統一的編碼上面只是呼叫snownlp原生語料庫對文字進行分析,snownlp重點針對購物評價領域,所以為了提高情感分析的準確度可以採取訓練語料庫的方法。
attr, val = [], [] info = count_sentiment(csv_file) info = sorted(info.items(), key=lambda x: x[0], reverse=False)# dict的排序方法 for each in info[:-1]: attr.append(each[0]) val.append(each[1]) line = Line(csv_file+":影評情感分析") line.add("", attr, val, is_smooth=True, is_more_utils=True) line.render(csv_file+"_情感分析曲線圖.html")
3 評論來源城市分析
呼叫pyecharts的page函式,可以在一個影象物件中建立多個 chart
,只需要對應的add即可。
geo1 = Geo("", "評論城市分佈", title_pos="center", width=1200, height=600, background_color='#404a59', title_color="#fff") geo1.add("", attr, val, visual_range=[0, 300], visual_text_color="#fff", is_geo_effect_show=False, is_piecewise=True, visual_split_number=10, symbol_size=15, is_visualmap=True, is_more_utils=True) # geo1.render(csv_file + "_城市dotmap.html") page.add_chart(geo1) geo2 = Geo("", "評論來源熱力圖",title_pos="center", width=1200,height=600, background_color='#404a59', title_color="#fff",) geo2.add("", attr, val, type="heatmap", is_visualmap=True, visual_range=[0, 50],visual_text_color='#fff', is_more_utils=True) # geo2.render(csv_file+"_城市heatmap.html")# 取CSV檔名的前8位數 page.add_chart(geo2) bar = Bar("", "評論來源排行", title_pos="center", width=1200, height=600 ) bar.add("", attr, val, is_visualmap=True, visual_range=[0, 100], visual_text_color='#fff',mark_point=["average"],mark_line=["average"], is_more_utils=True, is_label_show=True, is_datazoom_show=True, xaxis_rotate=45) bar.render(csv_file+"_城市評論bar.html")# 取CSV檔名的前8位數 page.add_chart(bar) pie = Pie("", "評論來源餅圖", title_pos="right", width=1200, height=600) pie.add("", attr, val, radius=[20, 50], label_text_color=None, is_label_show=True, legend_orient='vertical', is_more_utils=True, legend_pos='left') pie.render(csv_file + "_城市評論Pie.html")# 取CSV檔名的前8位數 page.add_chart(pie) page.render(csv_file + "_城市評論分析彙總.html")
4 影片情感分析
0.5以下為負面情緒,0.5以上為正面情緒。可以看到好評還是很不錯的,至於豆瓣上一些看衰評論只是少數。
5 電影評分走勢分析
-
讀取csv檔案,以dataframe(df)形式儲存
-
遍歷df行,儲存到list
-
統計相同日期相同評分的個數
-
轉換為df格式,設定列名
-
按日期排序
-
遍歷新的df,每個日期的評分分為5種,因此需要插入補充缺失數值。
creat_df = pd.DataFrame(columns = ['score', 'date', 'votes']) # 建立空的dataframe for i in list(info_new['date']): location = info_new[(info_new.date==i)&(info_new.score=="力薦")].index.tolist() if location == []: creat_df.loc[mark] = ["力薦", i, 0] mark += 1 location = info_new[(info_new.date==i)&(info_new.score=="推薦")].index.tolist() if location == []: creat_df.loc[mark] = ["推薦", i, 0] mark += 1 location = info_new[(info_new.date==i)&(info_new.score=="還行")].index.tolist() if location == []: creat_df.loc[mark] = ["還行", i, 0] mark += 1 location = info_new[(info_new.date==i)&(info_new.score=="較差")].index.tolist() if location == []: creat_df.loc[mark] = ["較差", i, 0] mark += 1 location = info_new[(info_new.date==i)&(info_new.score=="很差")].index.tolist() if location == []: creat_df.loc[mark] = ["很差", i, 0] mark += 1 info_new = info_new.append(creat_df.drop_duplicates(), ignore_index=True)
由於允許爬取的量少和時間問題,部分資料不是很明顯。但依然可以得出一些發現。在影片上映開始的一週內,為評論高峰,尤其是上映3天內,這符合常識,但是也可能有偏差,因為爬蟲獲取的資料是經過豆瓣電影排序的,倘若資料量足夠大得出的趨勢可能更接近真實情況。
另外發現,影片在上映前也有部分評論,分析可能是影院公映前的小規模試映,且這些提前批的使用者的評分均值,差不多接近影評上映後的大規模評論的最終評分 ,從這些細節中,我們或許可以猜測,這些能提前觀看影片的,可能是資深影迷或者影視從業人員,他們的評論有著十分不錯的參考價值。
6 影評詞雲圖
詞雲圖製作時,先讀取CSV檔案一dataframe形式儲存,去除評論中非中文文字,選了胡歌照片作為背景,並設定了停用詞表。
wc = WordCloud(width=1024, height=768, background_color='white', mask=backgroud_Image, font_path="C:\simhei.ttf", stopwords=stopwords, max_font_size=400,random_state=50)
可以看到高頻詞“可以”表現出對該片的認可,“特效”體現出特效鏡頭對科幻片的重要性,“科幻電影”體現出影迷對科幻類電影的濃厚興趣。
以上就是本次爬取豆瓣網《流浪地球》短評的過程與資料分析。