寫在前面的話:因爲本人在職,所以沒有充分的時間寫博客,所以經常是寫好整個框架,再陸陸續續的補充修改。所以如果發現什麼錯誤請留言。
這是一個心情愉悅的週六下午,在免費給公司加了幾個小時班後,突然想看電影了,但是作爲一個junior engineer,我怎麼能像那些senior engineer一樣拿着輕鬆賺來毛爺爺去電影院揮霍呢?(看官COS:電影都捨不得看,還說的這麼冠冕堂皇,真特麼屌絲!)
首先我們先來學習一下復聯4的英文名字Avengers: Endgame
Avengers: 復仇者
Endgame:最後階段,尾聲
那麼從這裏開始我們就稱這個電影爲AE吧。
AE上映於xxx年xx月xx日,導演XXX,算了我不編了,自己點擊下方的鏈接進行閱讀吧。
https://movie.ccccc.com/subject/26100958/
反正那幾個演員的名字除了小羅伯特唐尼其他都記不住,因爲這人演的鋼鐵俠據說原型是特斯拉的創始人XXX,好吧,其實記外國人的人名對我來說是有一定困難的。
收,下面正式開始我的用數據看電影之旅。
第一階段 分析項目風險
作爲一個三無人員,我需要自己採集數據,這裏就用爬蟲來抓取豆瓣網的數據吧。在爬取數據之前,需要先看一下網站上數據長什麼樣子,預估一下什麼數據可以爬取到手。
上面這個截圖出自之前的那個介紹鏈接,圖中紅色區域的數據是本次所必須爬取的數據,而黃色的則是Nice to have.
除了這個基本介紹頁面,豆瓣還有一個專門的影評頁面:
https://movie.douban.com/subject/26100958/reviews
這裏同樣用紅色和黃色的框框標出了需要爬取的數據。
好的,到這裏第一步的目標就確定了,接下來用工具看一下豆瓣網頁的源碼。
從源碼來看基本上設定的目標數據都能夠爬取到,除了那個影評的展開功能,那本次爬取就先不獲取全部影評了。當然,目前的設想也是沒有考量豆瓣網在反爬蟲的技術所做的工作的。(非技術人員可以跳過這個部分了O(∩_∩)O哈哈~)
下面廢話不多說,開始寫爬蟲(寫完才發現豆瓣做了反爬蟲,至今還沒想到如何解決,但是小弟表示不服,這個flag先立在這了····)。
第二階段 數據收集
1. 獲取自己的cookie
打開Chrome,找到開發者工具或者按F12,選到cookie標籤頁,輸入document.cookie即可獲得你自己這個時刻訪問所使用的cookie,這個cookie只有幾分鐘的時效,會經常變,所以每次跑程序之前先獲取一下這個cookie。
2. 爬取home頁面的basic info
首先右鍵download一下這個頁面,這樣的話你在本地就可以看到源碼,方便自己調試。
這裏直接貼上爬取數據的代碼,至於別的代碼可以去我的GitHub上下載。
# 獲取主頁的的信息
def get_home_info(url):
html = get_html(url)
doc = pq(html)
# movie_name = doc('#content h1 span').text()
movie = doc('#content h1 span').items()
movie_info = [x.text() for x in movie]
final_rate = doc('.ll.rating_num').text()
total_people = doc('.rating_people span').text()
rating = doc('.item span').items()
rating_info = [x.text() for x in rating]
res = [movie_info, final_rate, total_people, rating_info]
return res
3. 爬取comments頁面的user,time,comments
這個頁面稍微複雜些,其實最大的問題還是那個只能訪問12頁的問題,希望各位大蝦能夠給點指導意見。(已解決,小樣需要知道怎麼解決的,給個專注,私信告訴你。)
# return a dataframe
def parse_content(html):
doc = pq(html)
users = doc('.avatar a').items()
users_herf = doc('.avatar a').items()
comment_votes = doc('.comment-vote span').items()
comment_time = doc('.comment-time').items()
comment_content = doc('.comment-item p').items()
res_df = df(columns=['user_name', 'user_url', 'comment_votes', 'comment_time', 'comment_content'])
res_df['user_name'] = [u.attr('title') for u in users]
res_df['user_url'] = [h.attr('href') for h in users_herf]
res_df['comment_votes'] = [v.text() for v in comment_votes]
res_df['comment_time'] = [h.attr('title') for h in comment_time]
res_df['comment_content'] = [v.text() for v in comment_content]
return res_df
4. 爬取user主頁的basic info
根據前面爬取到的user Home 頁面的URL,再去爬取其的基礎信息,這裏只爬取了用戶註冊的地址。
# Get user's location
def get_user_info(url):
html = get_html(url)
doc = pq(html)
location = doc('.user-info a')
return location.text(), location.attr('href')
5. 針對主頁的幾個基本信息爬取
這一步其實是個多餘的操作,主頁我需要的信息也不是很多,直接寫死也就好了。
但是你們覺得我會只曾這一部電影的熱度嗎?呵o( ̄︶ ̄)o,年輕人!!!
def get_home_info(url):
html = get_html(url)
doc = pq(html)
# movie_name = doc('#content h1 span').text()
movie = doc('#content h1 span').items()
movie_info = [x.text() for x in movie]
final_rate = doc('.ll.rating_num').text()
total_people = doc('.rating_people span').text()
rating = doc('.item span').items()
rating_info = [x.text() for x in rating]
res = [movie_info, final_rate, total_people, rating_info]
return res
第三部分 數據分析
至此,我們獲取了660條評論數據,數據維度包括評論人,評論時間,評論內容,評論地點(用戶的註冊地址)。接下來的操作大部分都會選用在pandas的datafram裏面進行操作。
1. 數據清洗
不要以爲數據少,就不做清洗了。這裏把用戶數據裏沒有填寫的空值都轉換成了other,而有一些寫的是豆瓣二十二島主之類,就把豆瓣這兩個字去除了。因爲這裏不打算做地圖chart,所以只是簡單做了些處理。刪掉了無用列,轉換了時間格式,清洗了用戶註冊的地點數據。
def loc_clean(user_loc):
print(user_loc.dtype)
l = user_loc.size
for i in range(l):
tmp = re.sub('\s+$', '', str(user_loc[i]))
if "豆瓣" in tmp:
user_loc[i] = re.sub("\s+豆瓣(.|\n|[^\x00-\xff])*", "", tmp)
else:
user_loc[i] = tmp
return user_loc
def data_clean(data_df):
print(data_df.columns)
data_df.drop(axis=1, labels=['user_url', 'user_loc_url'], inplace=True)
data_df["commemt_dt"] = data_df["comment_time"].str[0:9]
data_df["commemt_ts"] = pd.to_datetime(data_df["comment_time"].str[11:], format='%H:%M:%S')
data_df["comment_time"] = pd.to_datetime(data_df["comment_time"], format='%Y-%m-%d %H:%M:%S')
data_df["user_loc"].replace(NaN, 'other', inplace=True)
data_df["user_loc"] = loc_clean(data_df["user_loc"])
print(data_df.info())
return data_df
d. 分詞
2. 多維數據分析
首先先用descrip()函數來看一下comment_votes的分佈情況
votes_se=pd.Series(data_df['comment_votes'])
pd.plotting.bootstrap_plot(votes_se)
votes_se.plot()
plt.show()
metric | votes |
---|---|
count | 659.000000 |
mean | 699.251897 |
std | 2185.730366 |
min | 0.000000 |
25% | 27.000000 |
50% | 53.000000 |
75% | 236.000000 |
max | 22969.000000 |
從引導圖中可以看出數據大部分在50左右,不是一個特別標準的正態分佈。
從表中數據上看最高投票數爲22969,從第二個圖裏看至少有兩個這樣的評論,讓我們來看看他是誰,講了啥
index_list = data_df[data_df.comment_votes >= 22960].index.tolist()
for i in index_list:
print(data_df['user_name'][i])
print(data_df['comment_time'][i])
print(data_df['comment_content'][i])
結果有點出乎意料,竟然開頭是這麼簡單的一句話,而且是任何電影都適用的一句話(這裏加粗顯示一下,畢竟人家是冠軍):
“如果你不喜歡這部電影,說明他不是爲你準備的,故事的終章是爲讀過故事的人準備的。。。”
由於第一次爬豆瓣網,技術有限,只是抓了其短評,但是個人覺得這也說明了一些問題,這句話很貼合粉絲的心意呀,爲了向那些不瞭解自己的故事的人證明自己的價值,這句話是內心裏最有力的安慰。
突然想給這篇文章加第一個評論:
如果你看不懂這篇文章,說明他不是爲你準備的,文章的結論是爲學過數據分析技術的人準備的。。。
這裏純屬娛樂,還是歡迎大家吐槽和批評指正的,這是一個言論自由的技術交流平臺,歡迎大家隨便吐槽。
a. 評論時間的分佈情況
從時間上看4月24日上映當日,發表的評論獲得的投票最多,所以想要在豆瓣上發表評論收到更多的vote,可以選擇在第一時間進行影評發表。
b. 評論地點的分佈情況
由於豆瓣的反爬蟲,只能抓取12頁短評,總共240條短評數據。數據不具有代表性,這裏花了三天,嘗試了代理IP,cookie,切換agent等方法,終於想到辦法破解豆瓣12頁數據的現在已經完整的爬取了復聯4全部短評。
這裏講一下豆瓣短評的背景知識,豆瓣提供了一個平臺,用戶可以註冊,登錄對自己感興趣的電影評論和點擊“想看”和“已經看過”,同時用戶也可以對別人的評價進行認可投票(vote)。
畫圖的代碼如下:
def data_analysis_loc(csv):
data_df = pd.read_csv(csv, header=0, encoding='utf8')
print(data_df.columns)
data_df = data_clean(data_df)
tmp_df = data_df[['comment_votes', 'user_loc']]
data_grouped = tmp_df.groupby('user_loc')
# res = pd.DataFrame(data_grouped.sum()).sort_values('comment_votes', ascending=False)
# label_list = res.index[:10]
# x_list = res['comment_votes'][:10]
# p = plt.pie(x_list, labels=label_list, shadow=True, autopct='%.2f')
# for front in p[1]:
# front.set_fontproperties(myfont)
# plt.title("來自不同地方的短評獲得投票總數統計", fontproperties=myfont)
# # res.plot.pie(subplots=True, )
# plt.show()
# print(res.info())
tmp_df = data_df[['comment_content', 'user_loc']]
data_grouped = tmp_df.groupby('user_loc')
res = pd.DataFrame(data_grouped.count()).sort_values('comment_content', ascending=False)
print("==" * 30)
print(res.index)
print(res.columns)
label_list = res.index[:10]
x_list = res['comment_content'][:10]
print(label_list)
print(x_list)
# res.columns = ['user_loc', 'comment_content']
# res.set_index(['user_loc'], inplace=True)
# plt.xlabel(x_list, fontproperties=myfont)
# plt.ylabel(label_list, fontproperties=myfont)
p = plt.pie(x_list, labels=label_list, shadow=True, autopct='%.2f')
for front in p[1]:
front.set_fontproperties(myfont)
plt.title("不同地方參與短評的人數統計", fontproperties=myfont)
# res.plot.pie(subplots=True, )
plt.show()
print(res.info())
上面兩個餅圖,是此次數據的一個統計。從這兩圖裏看出北京上海無論是人數還是獲得的投票數都是最高的,而有一個城市非常有意思,那就是山西太原,他們參與短評的人數都排不進前十名,也就是不到2.24%,而獲得的投票數卻佔了16.64%,同樣現象也發生在南京人身上。
跪求下面請南京人和太原人多多給本文一些評論,這樣我的文章就可以上頭條了。(加粗)
c. 評論內容的詞雲
這裏分爲三個步驟
步驟一: 使用jieba分詞對短評內容進行分詞,製作初版的詞雲。
for c in data_df["comment_content"]:
#這裏選了false是爲了避免重複的詞
c_words += " ".join(jieba.cut(c, cut_all=False))
print(c_words)
all_words.append(" ".join(jieba.cut(c, cut_all=False)))
data_df["comment_words"]=all_words
這裏選擇鋼鐵俠的靚照作爲背景圖,主要是考慮到配色比較鮮明。
將所有的評論放在一起,得到這樣一個詞雲,可以發現這裏有幾個問題。
- 詞語"漫威",“電影”,“我們”等詞語都是沒有意義的。
- 副詞、介詞、連詞、助詞這些虛詞沒有去除。
- 缺少專有名詞,例如:粉絲喜歡稱
步驟二: 對短評分詞後的數據進行清洗。
這裏參考一下以下文章,加入一些粉絲創造的詞語,看了之後發現急需一個真粉絲出來一個個解釋下,這些別名都是怎麼回事。
https://baijiahao.baidu.com/s?id=1597535514187562838&wfr=spider&for=pc
https://baijiahao.baidu.com/s?id=1620981295879150934&wfr=spider&for=pc
這裏把寡姐等詞語加入到詞庫後,再對“電影”,“漫威”等詞語進行了過濾,並使用了extract_tags函數對短評進行了關鍵字提取後這纔得到一個勉強能用的詞雲。
下面是代碼和詞雲:
def word_cloud(words, id):
# new_worlds = " ".join(words)
# 選了一個鋼鐵俠的照片做了背景圖
coloring = np.array(Image.open("ironman.jpg"))
my_wordcloud = WordCloud(background_color="white", max_words=800, mask=coloring,
max_font_size=120, random_state=30, scale=2,
font_path='c:\windows\fonts\STKAITI.TTF').generate(words)
image_colors = ImageColorGenerator(coloring)
plt.imshow(my_wordcloud.recolor(color_func=image_colors))
plt.imshow(my_wordcloud)
plt.axis("off")
plt.show()
# 保存圖片
my_wordcloud.to_file('word_cloud_{0}.png'.format(id))
def word_analysis(csv):
data_df = pd.read_csv(csv, header=0, encoding='utf8')
print(data_df.columns)
data_df = data_clean(data_df)
all_words = list()
#
c_words = ''
# 加載定義詞庫
jieba.load_userdict('custom_words.txt')
stop_words=["漫威","電影","復仇者","粉絲","最後","英雄",""]
for c in data_df["comment_content"]:
# c = jieba.analyse.extract_tags(c, allowPOS=('ns', 'n', 'vn', 'v', 'nr', 'nz'))
tmp = jieba.analyse.extract_tags(c)
for t in tmp:
if t in stop_words:
tmp.remove(t)
c_words += " ".join(tmp)
# 精準模式分詞
# c_words += " ".join(jieba.cut(c, cut_all=False))
# 搜索引擎分詞
# c_words += " ".join(jieba.cut_for_search(c))
all_words.append(" ".join(jieba.cut(c, cut_all=False)))
data_df["comment_words"] = all_words
word_cloud(c_words, "all_words")
步驟三: 按照不同空間和時間對數據進行分類,分別製作詞雲。
3. 情感分析(待續)
目前已經有全部(總共二十二部電影)短評數據,希望感興趣的大蝦留言一起研究分析。
4. 總結
本文得到三個結論:
- 評論的字數不在於長,而在於精。例如使用以下句式造句“xxx是爲懂的人準備的,而不是xxxx,如果xxxx,那麼說明xxxx”
- 要想自己的評論得到更多的投票數,請一定要在第一時間發表自己的評論。
- 山西太原人和南京人的評論更受歡迎,熱烈歡迎南京人和太原人在下方吐槽我,哈哈哈!