1. 程式人生 > >如何科學地蹭熱點:用python爬蟲獲取熱門微博評論並進行情感分析

如何科學地蹭熱點:用python爬蟲獲取熱門微博評論並進行情感分析

前言:本文主要涉及知識點包括新浪微博爬蟲、python對資料庫的簡單讀寫、簡單的列表資料去重、簡單的自然語言處理(snowNLP模組、機器學習)。適合有一定程式設計基礎,並對python有所瞭解的盆友閱讀。

甩鍋の宣告

1.本資料節選自新浪熱門微博評論,不代表本人任何觀點

2.本人不接受任何非技術交流類批評指責(誇我可以)

3.本次分析結果因技術問題存在一定誤差(是引入的包的問題,不是我的)

4.本次選取熱門微博為半個月以前的(翻譯一下:熱點已經冷了,我只是個寫教程的)

4.頂鍋蓋逃

繼上次更完“國慶去哪兒”文之後,被好多程式設計相關的公眾號翻了牌子_(:зゝ∠)_,讓我過了一把v的癮,也讓我更加努力的想要裝(消音)。

在我埋頭學習mysql、scrapy、django準備下一波吹水的時候,python交友群裡有個人說,你去爬下老薛的微博呀,還能蹭個熱點,這讓勤(mo)奮(mo)學(kou)習(jiao)的我停下了寄幾敲程式碼的手。

然後我趕緊去關注了一下最近老薛的新聞…在感受了劇情的複雜和案情的撲朔迷離之後…我默默地學習瞭如何閱讀合同…如何利用ELA分析圖片…如何寫作文…如何查別人的銀行流水…知識點有點多…讓我緩一會…

所以呢,這次的主題是分析老薛最新微博的評論,分析評論粉絲們的心情狀態,且聽我娓娓道來。

1.新浪微博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 = 4154417035431509,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

2.微博爬蟲

通過呼叫新浪微博API的方式,我們就可以簡單獲取單條微博的評論資訊了,為啥說簡單呢,因為人紅資訊貴啊!你以為大V的微博就這麼免費的給你API呼叫了嗎,非認證應用開發者單日只能請求幾千次API,這對像老薛這樣單條微博幾十萬評論的大V來說…太少了(TT)

所以捏,還是要寫微博爬蟲。

正所謂,知己知彼百戰不殆,新浪作為大廠,怎麼說也是身經百戰,必定是經歷了無數場爬蟲與反爬之間的戰爭,必然有著健全的反爬策略。正所謂,強敵面前,繞道而行,有位大佬說得好,爬網站,先爬移動端:https://m.weibo.cn/

登入微博後,進入到老薛迴應P圖事件的微博中去,_(:зゝ∠)_老薛啊,怪我,怪我來得太晚了,點進去的時候已經有70w+的評論了(截止至發文當天已經100w+的評論了),可以看到安靜的微博下粉絲們不安的心…

移動端微博的網址顯得肥腸簡單,不似PC端那麼複雜而不明邏輯:https://m.weibo.cn/status/4154417035431509 多點幾條微博就可以知道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=' + 頁碼

接下來就是套路了,偽裝瀏覽器header,讀取json檔案,遍歷每一頁…這都不是重點!而且這些我以前都講過\~直接上程式碼~

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

這裡有幾點說明:
1.設定爬取間隔時間之後,微博爬蟲被禁的概率降低了很多(特別是晚上)
2.新浪每次返回的json資料條數隨機,所以翻頁之後會出現資料重複的情況,所以用到了資料去重,這會在後面說。
3.在text和source中添加了去除emoji表情的程式碼(折騰了很久寫不進資料庫,差點就從刪庫到跑路了/(ㄒoㄒ)/),同時也去除了摻雜其中的回覆他人的html程式碼。
4.我只寫了讀取資料,沒有寫如何儲存,因為我們要用到數!據!庫!辣!(這是重點!敲黑板)

3.python中資料庫的讀取與寫入

雖然微博爬蟲大大提高了資料獲取量,但也因為是爬蟲而容易被新浪封禁。這裡結束迴圈的判斷是網路狀態不是200,但當微博發現是爬蟲時,微博會返回一個網頁,網頁中什麼實質內容都木有,這時候程式就會報錯,而之前爬到的資料,就啥也沒有了。

啥 都 沒 有 了

但是如果爬一會,儲存一次資料,這資料量要一大起來…冷冷的檔案在臉上胡亂地拍…我的心就像被…這時候我們就需要用到資料庫了。

資料庫,顧名思義,就是存放資料的倉庫,資料庫作為一個發展了60多年的管理系統,有著龐大的應用領域和複雜的功能……好了我編不下去了。

在本文中,資料庫的主要作用是AI式的excel表格(—)。在爬蟲進行的過程中,爬到一個數就存進去,爬到一個數就存進去,即使爬蟲程式執行中斷,中斷前爬到的資料都會存放在資料庫中。

配圖.gif

大多數資料庫都能與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 xue.xueresponse(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程式,大概爬了2w條實時評論,新浪微博的反爬還是很強大的,有倆個解決方法:更換IP和切換賬號,IP代理的使用方法我以前有寫過,賬號可以在X寶購買,但是!_(:зゝ∠)_由於本文的作者是一個肥腸貧窮肥腸摳門並且身患重病(懶癌)的人……2w條資料也是有研究價值的對不對(((;꒪ꈊ꒪;))),不如我們就這樣繼續我們研究吧(((;꒪ꈊ꒪;)))……

在進行下一步研究之前,我們還要將資料庫中的內容讀取出來,python中資料庫的讀取程式碼也很簡單。

conn =pymysql.connect(host='伺服器IP',user='使用者名稱',password='密碼',charset="utf8")    #連線伺服器
with conn:
    cur = conn.cursor()
    cur.execute("SELECT * FROM xue.xueresponse WHERE id < '%d'" % 20000)
    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]])

完整程式碼在文末。

4.自然語言處理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.01的分值,最後一條:力挺薛之謙 給出了0.99的分值。看其他幾句基本符合語境的態度,當需要評測單個商品的評價態度時就可以使用snowNLP。但是由於老薛的評論中涉及到三個人,他自己,李雨桐,高磊鑫,演算法無法判斷評論是關於誰的情感值,又因為微博評論可以粉絲之間互相回覆,這讓判斷評論的主體是誰更加撲朔迷離(機器表示我學習不能啊…)。

這好像代表了,本次分析的結果將…並沒有什麼卵用。發生這種事情,米醬也不想的…做人呢…最重要的是要開心…米醬不會寫演算法啊,米醬語文也不好啊,米醬真的做不到啊(๑°⌓°๑)…

5.分析結果

本次分析的目的愉快的從分析粉絲們對待薛之謙事件的情緒變成了單純分析粉絲們的情緒(—)。

plt.hist(sentimentslist,bins=np.arange(0,1,0.02))
plt.show()

對上節經過處理得到的情感值列表進行統計,並生成分佈圖。下圖資料採集時間9月27日19時,採集評論2w條。

薛之謙迴應P圖事件微博評論情感值分佈

我還順便(真的是順便,正經臉)爬了李雨桐將網友轉賬捐款的微博的2w條評論。

李雨桐將網友轉賬捐款微博評論情感值分佈

我又順順便(真的順便,看我真誠的眼神)爬了陳赫出軌時發表我錯了博文的微博評論。

陳赫發表我錯了博文評論情感值分佈

根據這三張圖,可以看到情感值在接近0、1兩端以及0.5左右位置頻率較高,說明粉絲們對於此類事件的情緒無論是積極還是消極都是比較明顯的。但也可以從圖片中看到一些微妙的差別,老薛的微博中情感值接近1的數量高於接近0的數量,但都沒有超過700,李雨桐微博中情感值接近1的數量遠低於接近0的數量,甚至接近0的數目到達了1000以上,但二者都超過了700,而陳赫的微博分析結果,倆者則都接近800。由於演算法存在一定誤差,不代表真實評論結果,我就不再多分析了(你們懂的)。

6.彩蛋

由於本次分析結果十分的……蒼白(我真的…盡力了…_(:зゝ∠)_

我又對評論中出現的微博表情進行了統計。

#薛微博評論表情統計
Counter({'[加油]': 128, '[哆啦A夢開心]': 28, '[哆啦A夢親親]': 17, '[哆啦A夢美味]': 12, '[em]': 4, '[/em]': 4, '[愛你]': 4, '[攤手]': 3, '[怒罵]': 3, '[好棒]': 3, '[轉發]': 2, '[可愛]': 1, '[太開心]': 1, '[/cp]': 1, '[呵呵]': 1, '[xkl轉圈]': 1, '[溫暖帽子]': 1, '[ok]': 1})
#李微博評論表情統計
Counter({'[攤手]': 13, '[加油]': 10, '[ok]': 8, '[皺眉]': 6, '[怒罵]': 4, '[太開心]': 3, '[左哼哼]': 3, '[饞嘴]': 2, '[擠眼]': 2, '[呵呵]': 2, '[嘿哈]': 2, '[機智]': 2, '[/cp]': 1, '[抱抱_舊]': 1, '[笑而不語]': 1, '[費解]': 1, '[cp]': 1})
#陳微博評論表情統計
Counter({'[呵呵]': 238, '[挖鼻屎]': 77, '[微風]': 45, '[好棒]': 26, '[打哈氣]': 21, '[ok]': 12, '[左哼哼]': 12, '[羊年大吉]': 12, '[懶得理你]': 11, '[崩潰]': 10, '[花心]': 9, '[困死了]': 8, '[淚]': 8, '[玫瑰]': 8, '[睡覺]': 6, '[雷鋒]': 5, '[炸雞和啤酒]': 5, '[帶著微博去旅行]': 4, '[有鴨梨]': 3, '[發紅包啦]': 3, '[馬到成功]': 3, '[丘位元]': 3, '[最右]': 3, '[花]': 3, '[打臉]': 3, '[別煩我]': 2, '[推薦]': 2, '[摳鼻屎]': 2, '[傷心]': 2, '[xkl轉圈]': 2, '[霹靂]': 2, '[em]': 2, '[/em]': 2, '[悲催]': 2, '[不要]': 2, '[ali轉圈哭]': 2, '[xkl糖豆]': 1, '[江南style]': 1, '[芒果萌萌噠]': 1, '[給勁]': 1, '[歪果仁夏克立]': 1, '[搶到啦]': 1, '[萌娃大竣]': 1, '[電影]': 1, '[憤怒]': 1, '[夏天公主]': 1, '[飛個吻]': 1, '[父親節]': 1, '[強]': 1, '[得瑟]': 1, '[被電]': 1, '[拜 拜]': 1, '[蠟燭]': 1, '[奧特曼]': 1, '[lt嚇]': 1, '[甩甩手]': 1, '[轉發]': 1, '[xkl頂]': 1, '[贊]': 1, '[愉快]': 1, '[糾結]': 1, '[/cp]': 1, '[bm贊]': 1, '[巨汗]': 1, '[最差]': 1, '[害怕]': 1, '[豬頭]': 1, '[bm哭泣]': 1, '[lt淚目]': 1, '[瞧瞧]': 1, '[打哈欠]': 1, '[xkl親親]': 1, '[bm會心笑]': 1, '[bm調皮]': 1})

李相比於倆位當時正當紅的明星,粉絲們使用表情的數目也是天壤之別啊。網友們對陳赫評論最多的表情是[呵呵],沒有一個[加油](也有可能當時沒有[加油]這個表情?),對老薛用了128次[加油],李雨桐10次,而評論李雨桐微博最多的表情也只有13個。果然是人紅熱點多,連表情包也多啊!

我還對參與評論的粉絲進行了統計,並提取了2w條微博評論裡參與評論最多的前20位粉絲。

#薛微博評論粉絲統計
[('方鹿亓', 150), ('使用者5238901365', 60), ('野百合xy', 57), ('憶盲i', 46), ('哎喲瑪', 44), ('學霸吖', 43), ('炒股屌絲', 42), ('賭神美好如初52', 38), ('梅蘭竹菊-世外桃源', 37), ('罌粟七年前', 36), ('陽光音樂i', 33), ('使用者5909206841', 33), ('林xiu霞', 32), ('醫鬧薛之謙', 32), ('wyx518052', 31), ('午後陽光喵喵', 29), ('藍天太陽我喜他', 29), ('不死就作的sr', 29), ('李遇可愛啵', 29), ('家駒一生所愛', 28)]
#李微博評論粉絲統計
[('方鹿亓', 139), ('0o夜雨狂瀾o0', 120), ('藍魅丶影', 98), ('mingyuanmumei', 76), ('曉清Z', 75), ('謙謙家的暴發戶', 65), ('一個玥寶貝兒', 64), ('qinglongzhilian', 62), ('出門左轉滾', 60), ('使用者5909206841', 58), ('水裡的魚188啊', 56), ('story他說', 53), ('C平民', 52), ('斷斷續續的思考', 50), ('鏗客HXRK', 48), ('我的網名只有8個字', 48), ('愛吃魚的貓妮', 47), ('唯ai薛之謙', 46), ('使用者6213147659', 45), ('洛杉磯的咖哩雞', 44)]

emmmmmm,這個我就不分析了,大家寄幾理解吧。那個叫方鹿亓的,你出來,你說說,你咋那麼閒呢?(逃

_(┐「ε:)_終於編…哦不寫完了,大家慢慢消化,原始碼已上傳至gayhub

感謝在這個長假依舊在閱讀本文的你,祝你早日升職加薪,大吉大利!~

建了一個qq群,歡迎各位來交♂流學♀習→python交友娛樂會所:613176398
為了營造良好的交流環境,入群需回答問題:匹配資訊用什麼表示式,請認真回答,否則會被拒絕的哦~