大年初一《流浪地球》全國上映。在豆瓣評分上,首日開分站穩8分以上,延續了以前點映的高口碑。微博上跟着出現吳京客串31天與投資6000萬的熱搜。知乎上關於「如何評價劉慈欣小說改編的同名電影《流浪地球》」的回答引發了衆多人關注,包括該片導演郭帆的最高贊回答。html
本篇文章爬取了豆瓣網上《流浪地球》的部分影評,並進行數據分析及可視化處理。下面是爬取分析的整個過程,讓咱們愉快開始吧!python
1、網頁分析算法
豆瓣網從2017年10月開始全面禁止爬取數據。在非登陸狀態下僅僅能夠爬取200條短評,登陸狀態下僅能夠爬取500條數據。白天一分鐘最多可爬40次,晚上60次,超過次數就會封IP地址。小本聰爬取數據得到400條時被封了IP,帳號被強制下線封號,以後發短信帳號恢復,所以不建議屢次爬取(另外,有不少解決方法,請自行搜索)。編程
評論用戶瀏覽器
評論內容bash
評分微信
評論日期cookie
用戶所在城市
app
值得注意的是,在地址欄咱們會發現電影名字的ID編號爲26266893(其餘電影只需更換ID便可),而且每頁有20條短評,所以我爬取了20頁。評論頁面沒有用戶所在城市,須要進入用戶頁面獲取信息。echarts
2、數據獲取與存儲
小本聰用的是Chrome瀏覽器,Ctrl+F12進入開發者工具頁面。F5刷新一下出現數據,找到cookies、headers。
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="maplekonghou@163.com"; 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)複製代碼
此處運用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)
複製代碼
從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類型
複製代碼
因爲數據很少,選擇CSV存儲便可。
def main(ID, pages):
global movie_name
for i in tqdm(range(0, pages)): # 豆瓣只開放500條評論
get_content(ID, i) # 第一個參數是豆瓣電影對應的id序號,第二個參數是想爬取的評論頁數
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複製代碼
3、數據分析與可視化
城市信息篩選中文字
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
複製代碼
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")
複製代碼
0.5如下爲負面情緒,0.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天內,這符合常識,可是也可能有誤差,由於爬蟲獲取的數據是通過豆瓣電影排序的,假若數據量足夠大得出的趨勢可能更接近真實狀況。
另外發現,影片在上映前也有部分評論,分析多是影院公映前的小規模試映,且這些提早批的用戶的評分均值,差很少接近影評上映後的大規模評論的最終評分 ,從這些細節中,咱們或許能夠猜想,這些能提早觀看影片的,多是資深影迷或者影視從業人員,他們的評論有着十分不錯的參考價值。
詞雲圖製做時,先讀取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)
複製代碼
能夠看到高頻詞「能夠」表現出對該片的承認,「特效」體現出特效鏡頭對科幻片的重要性,「科幻電影」體現出影迷對科幻類電影的濃厚興趣。
以上就是本次爬取豆瓣網《流浪地球》短評的過程與數據分析。
微信公衆號「學編程的金融客」後臺回覆「流浪地球」便可得到源碼。