我用Python爬了鹿晗、關曉彤微博的熱門評論,并進行了情感分析
相信最近科技圈都在調侃一件事:10 月 8 日中午的一條微博,引發了一場新浪微博用戶們(尤其是女性用戶)之間的軒然大波,導致新浪微博癱瘓。
本文主要涉及知識點包括新浪微博爬蟲、Python 對數據庫的簡單讀寫、簡單的列表數據去重和自然語言處理(snowNLP 模塊、機器學習)。適合有一定編程基礎,并對 Python 有所了解的盆友閱讀。
這條微博的始作俑者,就是全球超人氣偶像明星鹿晗。
程序員們紛紛開啟了科♂學地討論:
詳細內容可見昨日的圖文:鹿晗是如何將微博服務器搞炸的?
微博工程師是如何一邊結婚一邊加班的:
淘寶程序員是如何原諒鹿晗的:
在這一刻,全世界都知道鹿晗戀愛了:
全球的女鹿飯們一起失戀了。
那么鹿晗的粉絲們情緒如何呢?我們來分析一下鹿晗戀情微博的評論,分析評論時粉絲們的心情狀態,且聽我娓娓道來。(想看分析結果的可直接跳到第 5 節)
新浪微博 API
在經歷了幾次爬蟲被禁的悲痛(真的很痛)之后,我學會了在爬網站之前先查有沒有 API 的“優良”習慣。
新浪作為一個大廠,怎么會不推出新浪微博 API 呢,面向開發者新浪有自己的開放平臺,這里是 Python 調用微博 API 的方法,通過登錄 App_key 和 App_secret 方式訪問微博 API 的代碼,代碼是基于 PY2 的。PY3 對 Weibo 模塊使用存在一定問題。
- from weibo import APIClient
- import webbrowser
- import sys
- reload(sys)
- sys.setdefaultencoding('utf-8')
- APP_KEY = '你的App Key ' #獲取的App Key
- APP_SECRET = '你的AppSecret' #獲取的AppSecret
- CALLBACK_URL = 'https://api.weibo.com/oauth2/default.html' #回調鏈接
- client = APIClient(app_key=APP_KEY, app_secret=APP_SECRET, redirect_uri=CALLBACK_URL)
- url = client.get_authorize_url()
- webbrowser.open_new(url) #打開默認瀏覽器獲取code參數
- print '輸入url中code后面的內容后按回車鍵:'
- code = raw_input()
- r = client.request_access_token(code)
- access_token = r.access_token
- expires_in = r.expires_in
- client.set_access_token(access_token, expires_in)
知道如何登錄 API 了,辣么如何調用 API 爬單條微博的評論呢?一行代碼搞定。
- r = client.comments.show.get(id = 4160547165300149,count = 200,page = 1)
所有關于單條微博的評論信息都在 r.comments 中了,這里需要對照微博 API 文檔,微博 API 有聲明調用微博評論 API 需要獲取用戶授權。
但是捏,只要知道單條微博的 id,就可以調用這個 API 了,關于單條微博的 id 如何獲取在后面會說(小聲一點,千萬別讓微博知道,萬一封了呢)。
按照 client. 接口名 .get(請求參數)的方式獲取 API,獲取 API 后的規格可在接口詳情中查看,文檔中有給出返回結果的示例。
文檔中也給出了關鍵數據的 json 接口名稱。
如果我們要獲取微博評論的內容,只需要調用 text 接口即可。
- for st in r.comments:
- text = st.text
微博爬蟲
通過調用新浪微博 API 的方式,我們就可以簡單獲取單條微博的評論信息了,為啥說簡單呢,因為人紅信息貴啊!
你以為大 V 的微博就這么免費的給你 API 調用了嗎,非認證應用開發者單日只能請求千次 API,這對像鹿晗這樣單條微博幾十萬評論的大 V 來說…太少了(TT)
所以捏,還是要寫微博爬蟲。
正所謂,知己知彼百戰不殆,新浪作為大廠,怎么說也是身經百戰,必定是經歷了無數場爬蟲與反爬之間的戰爭,必然有著健全的反爬策略。正所謂,強敵面前,繞道而行,有位大佬說得好,爬網站,先爬移動端:https://m.weibo.cn/
登錄微博后,進入到鹿晗公布戀情的微博中去,_(:зゝ∠)_ 已經有 200w+ 評論了,可以看到安靜的微博下粉絲們不安的心…
移動端微博的網址顯得肥腸簡單,不似 PC 端那么復雜而不明邏輯:https://m.weibo.cn/status/4160547165300149
多點幾條微博就可以知道 status 后面的數字,就是單條微博的 id 了。
評論里包含了熱門評論和***評論兩種,但無論是哪種評論,繼續往下翻,網址都不會變化。在 chrome 瀏覽器右鍵“檢查”,觀察 Network 變化。
從 Network 的 xhr 文件中,可以得知熱門評論的變化規律是:
- 'https://m.weibo.cn/single/rcList?format=cards&id=' + 單條微博id + '&type=comment&hot=1&page=' + 頁碼
***評論的變化規律是:
'https://m.weibo.cn/api/comments/show?id=' + 單條微博id + '&page=' + 頁碼
打開
https://m.weibo.cn/single/rcList?format=cards&id=4154417035431509&type=comment&hot=1&page=1 就可以看到熱門評論的 json 文件。
接下來就是套路了,偽裝瀏覽器 header,讀取 json 文件,遍歷每一頁…這都不是重點!直接上代碼~這里開始是 PY3 的代碼了~
- import re,time,requests,urllib.request
- weibo_id = input('輸入單條微博ID:')
- # url='https://m.weibo.cn/single/rcList?format=cards&id=' + weibo_id + '&type=comment&hot=1&page={}' #爬熱門評論
- url='https://m.weibo.cn/api/comments/show?id=' + weibo_id + '&page={}' #爬時間排序評論
- headers = {
- 'User-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0',
- 'Host' : 'm.weibo.cn',
- 'Accept' : 'application/json, text/plain, */*',
- 'Accept-Language' : 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
- 'Accept-Encoding' : 'gzip, deflate, br',
- 'Referer' : 'https://m.weibo.cn/status/' + weibo_id,
- 'Cookie' : '登錄cookie信息',
- 'DNT' : '1',
- 'Connection' : 'keep-alive',
- }
- i = 0
- comment_num = 1
- while True:
- # if i==1: #爬熱門評論
- # r = requests.get(url = url.format(i),headers = headers)
- # comment_page = r.json()[1]['card_group']
- # else:
- # r = requests.get(url = url.format(i),headers = headers)
- # comment_page = r.json()[0]['card_group']
- r = requests.get(url = url.format(i),headers = headers) #爬時間排序評論
- comment_page = r.json()['data']
- if r.status_code ==200:
- try:
- print('正在讀取第 %s 頁評論:' % i)
- for j in range(0,len(comment_page)):
- print('第 %s 條評論' % comment_num)
- user = comment_page[j]
- comment_id = user['user']['id']
- print(comment_id)
- user_name = user['user']['screen_name']
- print(user_name)
- created_at = user['created_at']
- print(created_at)
- text = re.sub('<.*?>|回復<.*?>:|[\U00010000-\U0010ffff]|[\uD800-\uDBFF][\uDC00-\uDFFF]','',user['text'])
- print(text)
- likenum = user['like_counts']
- print(likenum)
- source = re.sub('[\U00010000-\U0010ffff]|[\uD800-\uDBFF][\uDC00-\uDFFF]','',user['source'])
- print(source + '\r\n')
- comment_num+=1
- i+=1
- time.sleep(3)
- except:
- i+1
- pass
- else:
- break
這里有四點說明:
- 設置爬取間隔時間之后,微博爬蟲被禁的概率降低了很多(特別是晚上)。
- 新浪每次返回的 json 數據條數隨機,所以翻頁之后會出現數據重復的情況,所以用到了數據去重,這會在后面說。
- 在 text 和 source 中添加了去除 emoji 表情的代碼(折騰了很久寫不進數據庫,差點就從刪庫到跑路了/(ㄒoㄒ)/),同時也去除了摻雜其中的回復他人的 html 代碼。
- 我只寫了讀取數據,沒有寫如何保存,因為我們要用到數!據!庫!辣!(這是重點!敲黑板)
Python 中數據庫的讀取與寫入
雖然微博爬蟲大大提高了數據獲取量,但也因為是爬蟲而容易被新浪封禁。
這里結束循環的判斷是網絡狀態不是 200,但當微博發現是爬蟲時,微博會返回一個網頁,網頁中什么實質內容都木有,這時候程序就會報錯,而之前爬到的數據,就啥也沒有了。
但是如果爬一會,保存一次數據,這數據量要一大起來…冷冷的文件在臉上胡亂地拍…我的心就像被…這時候我們就需要用到數據庫了。
數據庫,顧名思義,就是存放數據的倉庫,數據庫作為一個發展了 60 多年的管理系統,有著龐大的應用領域和復雜的功能……好了我編不下去了。
在本文中,數據庫的主要作用是 AI 式的 excel 表格(●—●)。在爬蟲進行的過程中,爬到一個數就存進去,爬到一個數就存進去,即使爬蟲程序運行中斷,中斷前爬到的數據都會存放在數據庫中。
大多數數據庫都能與 Python 對接使用的,米醬知道的有 MySQL、SQLite、Mongodb、Redis。
這里用的是 MySQL,Mac上 MySQL 的安裝,管理數據庫的軟件 Navicat 使用幫助,其他系統自己找吧,安裝使用過程中有啥問題,請不要來找我(逃
根據上面的代碼,在 navicat 中創建數據庫、表和域以及域的格式。在 Python 程序中添加代碼。
- conn =pymysql.connect(host='服務器IP(默認是127.0.0.1)',user='服務器名(默認是root)',password='服務器密碼',charset="utf8",use_unicode = False) #連接服務器
- cur = conn.cursor()
- sql = "insert into nlp.love_lu(comment_id,user_name,created_at,text,likenum,source) values(%s,%s,%s,%s,%s,%s)" #格式是:數據名.表名(域名)
- param = (comment_id,user_name,created_at,text,likenum,source)
- try:
- A = cur.execute(sql,param)
- conn.commit()
- except Exception as e:
- print(e)
- conn.rollback()
運行 Python 程序,大概爬了 1w 條實時評論,在進行下一步研究之前,我們還要將數據庫中的內容讀取出來,Python 中數據庫的讀取代碼也很簡單。
- conn =pymysql.connect(host='服務器IP',user='用戶名',password='密碼',charset="utf8") #連接服務器
- with conn:
- cur = conn.cursor()
- cur.execute("SELECT * FROM nlp.love_lu WHERE id < '%d'" % 10000)
- rows = cur.fetchall()
這樣之前爬取的信息就被讀取出來了,但是前面也說了,微博爬蟲翻頁時返回數據條數隨機,所以會出現重復的狀況,所以讀取之后,需要用 if…not in 語句進行一個數據去重。
- for row in rows:
- row = list(row)
- del row[0]
- if row not in commentlist:
- commentlist.append([row[0],row[1],row[2],row[3],row[4],row[5]])
完整代碼在文末。
自然語言處理 NLP
NLP 是人工智能的一個領域,可以通過算法的設計讓機器理解人類語言,自然語言也屬于人工智能中較為困難的一環。
像中文這么博大精深、變幻莫測的語言更是 NLP 中的一大難點,Python 中有很多 NLP 相關的模塊,有興趣的盆友可以通過用 Python 實現簡單的文本情感分析初探 NLP。
我參(ban)考(yun)了一些現成的情感分析算法,對爬取的評論進行分析,錯誤率肥腸高_(:зゝ∠)_ ,這可腫么辦?難道要重新設計算法?我仿佛遇到了人生中***個因為語文沒學好而引發的重大問題……
當然像我這樣靈(lan)活(duo)的姑娘,自然是很快發現了 Python 中較為出名的一個中文 NLP 庫:snowNLP。snowNLP 調用的方法比較簡單,源碼中詳細解釋了調用方法和生成結果。
- def snowanalysis(textlist):
- sentimentslist = []
- for li in textlist:
- s = SnowNLP(li)
- print(li)
- print(s.sentiments)
- sentimentslist.append(s.sentiments)
這段代碼中獲取了讀取數據庫后由評論主體 text 生成的列表文件,并依次對每一個評論進行情感值分析。
snowNLP 能夠根據給出的句子生成一個 0-1 之間的值,當值大于 0.5 時代表句子的情感極性偏向積極,當分值小于 0.5 時,情感極性偏向消極,當然越偏向倆頭,情緒越明顯咯,讓我們來看看測試評論的結果。
可以從文字內容和下面對應的數值看出,祝福或者表現的積極的情緒,分值大多高于 0.5,而期盼分手或者表達消極情緒的分值,大多低于 0.5。分析結果中也存在一定的誤差,可以通過訓練對算法進行優化,米醬語文不好就不瞎搞了…(逃
分析結果
讓我們來看看本次分析的結果(●—●)。
- plt.hist(sentimentslist,bins=np.arange(0,1,0.02))
- plt.show()
對上節經過處理得到的情感值列表進行統計,并生成分布圖。下圖數據采集時間 10 月 9 日 19 時,采集評論 1w 條。

↑鹿晗宣布戀愛微博評論情感值分布
↑關曉彤對應微博評論情感值分布
根據這兩張圖,可以看到情感值在接近 0、1 兩端以及 0.5 左右位置頻率較高,說明粉絲們對于此類事件的情緒無論是積極還是消極都是比較明顯的。但也可以看出,積極的情緒更多于消極的情緒。
我又對評論中出現的微博表情進行了統計。
給鹿晗的評論中表情的數量是關曉彤的近 3 倍,而排在***位的,都是 [加油],可以看到粉絲們對鹿晗的戀情還是支持居多的,當然也不乏有些人想要 [bm投訴] 主角們了,也有部分人感到 [悲傷],想要冷靜一下 [別煩我]。
再對評論內容進行一下詞云分析。
↑鹿晗宣布戀愛微博評論詞云
↑關曉彤對應微博評論詞云
在鹿晗的微博下面出現了大量的祝福、支持、一起等詞匯,也有一些為什么、不配、分手之類質疑的聲音;在關曉彤的微博下面也存在相同的詞匯,但是好像還有大量的關于熱巴、李易峰的字眼,看來兩位都有緋聞 CP 呀。
你們猜詞云的背景圖是什么?我就不說了,你們自己感受。
參考資料:
1.微博開放平臺:http://open.weibo.com/
2.Python調用微博API的方法:http://blog.csdn.net/gamer_gyt/article/details/51839159
3.微博API文檔:http://open.weibo.com/wiki/%E5%BE%AE%E5%8D%9AAPI
4.MySQL的安裝:http://www.jianshu.com/p/2d902dd4fff4
5.Navicat使用幫助:http://www.jianshu.com/p/326c1aaa1052
6.if…not in語句:http://www.cnblogs.com/ranjiewen/p/6305684.html
7.用Python實現簡單的文本情感分析:https://zhuanlan.zhihu.com/p/23225934
8.snowNLP:https://github.com/isnowfy/snownlp