JB的Python之旅-每句話背後的情緒值
每個人相處都有一套生活方式,跟女人也不一樣,不同的女人要用不同的邏輯思考,要琢磨不同語句背後的含義,生活,不容易;

對於情場小白而言,最擔心的就是女朋友不開心了,畢竟好不容易才從右手變成了實物,肯定是加倍愛惜的;
但,你真的瞭解每一句話的情緒分析嗎? 給一句話,能知道這句話的情緒佔比嗎?
情緒分析
情緒分析關鍵的是 詞典 ,網上找了下,大連理工情感詞彙本體庫比較有名,那就試試看;
下載地址:
連結:https://pan.baidu.com/s/18PeWl-9EjZ7O5Rdfejzgig 提取碼:qc3n 複製程式碼
下載完後,看了下說明文件,瞭解下,詞庫裡面的格式是這樣的:

看起來好像不錯,就試試看~
解壓說,發現有這幾個檔案:

簡單介紹下2個py檔案:
- evaluate.py,把情感詞彙.xlsx轉成情感詞彙.csv
- ofollow,noindex">process.py
這裡需要注意,使用evaluate.py時,有可能會報 UnicodeEncodeError
這個錯;

解決也很簡單,在原指令碼的第11行,指定encoding用 utf-8
即可:
with open('情感詞彙.csv', 'w',encoding='utf-8') as out_file: 複製程式碼
而執行evaluate.py時候,需要import docopy跟pandas兩個庫,自行安裝吧;
pip install docopy pandas的話,之前聽說很多依賴,後來jb安裝個anaconda就好了(全家桶); 複製程式碼
docopt
安裝完兩個庫,因docopy這個庫沒了解過,因此上官網瞭解下:
docopt官網上的介紹是:
Command-line interface description language docopt helps you: define interface for your command-line app, and automatically generate parser for it. 複製程式碼
從中可以看出docopt的兩個主要功能:
- 定義互動引數
- 解析引數資訊
再看下官網的例子:
Naval Fate. Usage: naval_fate ship new <name>... naval_fate ship <name> move <x> <y> [--speed=<kn>] naval_fate ship shoot <x> <y> naval_fate mine (set|remove) <x> <y> [--moored|--drifting] naval_fate -h | --help naval_fate --version Options: -h --helpShow this screen. --versionShow version. --speed=<kn>Speed in knots [default: 10]. --mooredMoored (anchored) mine. --driftingDrifting mine. 複製程式碼
在這個例子中, Naval Fate
是app名稱, naval_fate
是命令列命令, ship
、 new
、 move
這些是可選的執行命令(commands), x
、 y
、 name
這些是位置引數(positional arguments), -h
、 --help
、 --speed
等這些是選項引數(options);
例子裡面用
- "[]"描述可選元素(optional)
- "()"描述必要元素(required)
- "|" 描述互斥元素(mutually exclusive)
- "..."描述重複元素(repeating)
這些引數,前面加上naval_fate就形成了可用的命令,這些命令在Usage中介紹;
Usage下面的Options裡羅列了選項(options)及其描述,它具體描述了
- 選項是否有長/短形式,如-h, --help
- 選項後面是否帶引數,如--speed=
- 選項是否有預設值,如[default: 10]
Usage和options裡的內容就組成了幫助資訊,當用戶輸入-h或--help引數時,命令列就會輸出幫助資訊。
docopt會抽取幫助資訊裡的內容,然後對命令列傳入的引數進行解析。
例項
用例項來說明,建立一個test.py文件:
"""Naval Fate. Usage: naval_fate.py ship new <name>... naval_fate.py ship <name> move <x> <y> [--speed=<kn>] naval_fate.py ship shoot <x> <y> naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting] naval_fate.py (-h | --help) naval_fate.py --version Options: -h --helpShow this screen. --versionShow version. --speed=<kn>Speed in knots [default: 10]. --mooredMoored (anchored) mine. --driftingDrifting mine. """ from docopt import docopt if __name__ == '__main__': arguments = docopt(__doc__, version='Naval Fate 2.0') print(arguments) 複製程式碼
執行命令:
python test.py ship new jb 複製程式碼
結果:
{'--drifting': False, '--help': False, '--moored': False, '--speed': '10', '--version': False, '<name>': ['jb'], '<x>': None, '<y>': None, 'mine': False, 'move': False, 'new': True, 'remove': False, 'set': False, 'ship': True, 'shoot': False} 複製程式碼
然後再嘗試一個Usage裡沒有的命令:
Usage: naval_fate.py ship new <name>... naval_fate.py ship <name> move <x> <y> [--speed=<kn>] naval_fate.py ship shoot <x> <y> naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting] naval_fate.py (-h | --help) naval_fate.py --version 複製程式碼
小小結
- docopt( doc )這個函式根據幫助文件的說明解析命令列引數,然後將結果返回為一個字典;
- 當用戶使用不在Usage之內的命令,輸出幫助文件;
- 要使用的時候,
from docopt import docopt
呼叫即可; - 必填引數,
doc
,其他4個是可選(help
、version
、argv
、options_first
);
evaluate
看回evaluate.py這個檔案,頂部有這麼一段:
__doc__ = ''' Usage: emotion WORD With Python: EmotionDict() --> init EmotionDict.evaluate(word) --> tuple(詞語(str), 情感分類(str), 強度(int), 極性(int)) or None ''' 複製程式碼
這裡面也專門告訴py怎麼用了,那新建個test.py試試:
from 情感詞彙.evaluate import EmotionDict test = EmotionDict() print(test.evaluate(word="戰禍")) 複製程式碼
直接執行,得到的輸出:
('戰禍', 'ND', 5, 2) 複製程式碼
對比了Excel的內容,內容是一樣的;

- ND代表憎惡;
- 強度有1、3、5、7、9,9是最高,5則說明一般;
- 極性有4類,0代表中性,1代表褒義,2代表貶義,3帶邊兼有褒貶兩性;
其他的,請看 說明.doc
,都有說明;
因此,戰禍這個詞,用貶義的方式來表達內心的憎惡? 不知道為什麼,總感覺怪怪的;
試試一句話
一個詞就是上面的用法,那一段話呢?
就需要分詞了,中文分詞用的最多就是 jieba
庫,不瞭解的同學,請移步此處;
某博直接找來一段話,結合分詞,一起看看:
分詞
seg_list = jieba.cut("帶著立場看比賽註定是痛苦的,倒不如好好品品比賽中每一個精彩的瞬間!",cut_all=False) print("Default Mode: " + "/ ".join(seg_list)) 複製程式碼
輸出:
Default Mode: 帶/ 著/ 立場/ 看/ 比賽/ 註定/ 是/ 痛苦/ 的/ ,/ 倒不如/ 好好/ 品品/ 比賽/ 中/ 每/ 一個/ 精彩/ 的/ 瞬間/ ! 複製程式碼
組合
seg_list = jieba.cut("帶著立場看比賽註定是痛苦的,倒不如好好品品比賽中每一個精彩的瞬間!",cut_all=False) test = EmotionDict() for i in seg_list: print(i) print(test.evaluate(word=i)) 複製程式碼
輸出:
帶 None 著 None 立場 None 看 None 比賽 None 註定 None 是 None 痛苦 ('痛苦', 'NB', 7, 0) 的 None , None 倒不如 None 好好 None 品品 None 比賽 None 中 None 每 None 一個 None 精彩 ('精彩', 'PH', 7, 1) 的 None 瞬間 None ! None 複製程式碼
標點符號沒做過濾,不太影響; 簡單看了下,那麼多個詞,只有 精彩、痛苦
是有返回內容的,也就說明,原來的詞庫遠遠不夠;
而且要把對應的PH、數字對應起來,還需要單獨寫一個轉換邏輯,還要過濾各種符號,這裡面還是有很多小細節做的,到這裡,效果實在太差了,主要是, 詞庫內容太少了,很多詞語都沒有,壓根就沒辦法判斷;
看看別人的
既然不自己造輪子,那就看看別人的吧,這種語境分析,第一時間就想起BAT了,那就一起看看BAT吧;
某度
直接某度搜索 某度自然語言處理
,直接彈出某度AI開放平臺,點選後看下產品服務,選擇自然語言處理,就看到有提供 情感傾向分析
,同時也有 對話情緒識別
兩個服務,兩者應該是共同原理,就看前者了;

點選後,登入,直接點選api文件,翻到情感傾向分析介面;
介面描述
對包含主觀觀點資訊的文字進行情感極性類別(積極、消極、中性)的判斷,並給出相應的置信度。
請求說明
請求示例
- HTTP方法: POST
- 請求URL: aip.baidubce.com/rpc/2.0/nlp…
URL引數
引數 | 值 |
---|---|
access_token | 通過API Key和Secret Key獲取的access_token,參考“Access Token獲取” |
Header如下
引數 | 值 |
---|---|
Content-Type | application/json |
Body請求示例
{ "text": "蘋果是一家偉大的公司" } 複製程式碼
請求引數
引數 | 型別 | 描述 |
---|---|---|
text | string | 文字內容(GBK編碼),最大2048位元組 |
返回說明
返回引數引數|說明|描述 --|--| log_id|uint64|請求唯一標識碼 sentiment|int|表示情感極性分類結果,0:負向,1:中性,2:正向 confidence|float|表示分類的置信度,取值範圍[0,1] positive_prob|float|表示屬於積極類別的概率 ,取值範圍[0,1] negative_prob|float|表示屬於消極類別的概率,取值範圍[0,1]
返回示例
{ "text":"蘋果是一家偉大的公司", "items":[ { "sentiment":2,//表示情感極性分類結果 "confidence":0.40, //表示分類的置信度 "positive_prob":0.73, //表示屬於積極類別的概率 "negative_prob":0.27//表示屬於消極類別的概率 } ] } 複製程式碼
Access Token獲取
Access Token獲取是通過API Key和Secret Key來獲取的,那這兩個怎麼獲取?
還記得情感傾向分析的主頁嗎?有個立即使用的按鈕,要去建立應用;
點選建立應用,輸入應用名稱、描述,然後點選檢視應用詳情,這裡面的API Key 跟Secret Key需要使用到;

來到access token獲取網址,按照要求試試,官方給的是py2,用py3重弄下,程式碼如下:
import requests url = 'https://aip.baidubce.com/oauth/2.0/token' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.32 Safari/537.36", "Content-Type":"application/json" } params = { "grant_type":"client_credentials", "client_id":你的API Key, "client_secret":你的Secret Key } response = requests.post(url,headers=headers,params=params) text = response.json().get("access_token") print(text) 複製程式碼
對應的結果就是access_token的值啦;
爽一把
這裡遇到個坑,按照官方文件操作,用requests庫,無論怎麼調,最終都會報下面這個錯;
{'log_id': 3838837857684473751, 'error_code': 282004, 'error_msg': 'invalid parameter(s)'} 複製程式碼
最後網上找了好久,改用urllib庫就好了,一臉懵逼。。貼程式碼:
import json import urllib # 獲取情緒內容 access_token=你的access_token值 url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify?access_token='+access_token headers={'Content-Type':'application/json'} post_data = {"text":"帶著立場看比賽註定是痛苦的,倒不如好好品品比賽中每一個精彩的瞬間!"} data=json.dumps(post_data).encode('GBK') request = urllib.request.Request(url, data) response = urllib.request.urlopen(request) content = response.read() content_str = str(content, encoding="gbk") print(content_str) 複製程式碼
輸出:
{"log_id": 830621152984506211, "text": "帶著立場看比賽註定是痛苦的,倒不如好好品品比賽中每一個精彩的瞬間!", "items": [{"positive_prob": 0.521441, "confidence": 0.571177, "negative_prob": 0.478559, "sentiment": 1}]} 複製程式碼
根據官網文件,上面4個欄位含義如下:
- sentiment,表示情感極性分類結果,官方沒具體說明,猜測是跟上面一樣,即0代表中性,1代表褒義,2代表貶義,3帶邊兼有褒貶兩性;
- confidence,表示分類的置信度;
- positive_prob,表示屬於積極類別的概率
- negative_prob,表示屬於消極類別的概率
按照上面的結果,那這句話應該是屬於中性詞,偏積極;
某訊
直接找,會發現文智自然語言處理,產品介紹文件在這裡,API指南在這裡,官方提供demo,py的demo在 這裡 ;
download程式碼,github上說需要安全憑證,點選登入獲取;
然後還要安裝對應的依賴庫,提供2種方式任君選擇:
$ pip install qcloudapi-sdk-python 或者下載原始碼安裝 $ git clone https://github.com/QcloudApi/qcloudapi-sdk-python 複製程式碼
pythonsetup.py install
然後開啟tests下的demo.py ,修改模組、介面名、介面引數即可;
#!/usr/bin/python # -*- coding: utf-8 -*- # 引入雲API入口模組 from QcloudApi.qcloudapi import QcloudApi ''' module: 設定需要載入的模組 ''' module = 'wenzhi' ''' action: 對應介面的介面名,請參考wiki文件上對應介面的介面名 ''' action = 'TextSentiment' ''' config: 雲API的公共引數 ''' config = { 'Region': 'ap-guangzhou', 'secretId': 'AKIDmmuRdgSV8sjR0eokVh2159Kp2OiyPHPQ', 'secretKey': 'DNS9h6aBFLYo2BAEBPePI3d3IMGzb7ml', } # 介面引數 action_params = { "content":"帶著立場看比賽註定是痛苦的,倒不如好好品品比賽中每一個精彩的瞬間!" } try: service = QcloudApi(module, config) print(service.generateUrl(action, action_params)) print(service.call(action, action_params)) except Exception as e: import traceback print('traceback.format_exc():\n%s' % traceback.format_exc()) 複製程式碼
輸出:
b'{"code":0,"message":"","codeDesc":"Success","positive":0.58672362565994,"negative":0.41327640414238}' 複製程式碼
- positive,積極
- negative,負面
對了,某訊沒有免費的體驗,jb剛好是新人領了個免費禮包,如果不是新手,就要自己充錢的,很X訊;

某裡
點選這裡-情感分析,登入,點選開通,然後來到控制檯;
點選基礎版,api除錯:

選擇api,這裡關於情感分析的,只有電商類的,其他都跟情感沒啥關係:

按照要求,輸入你的access key跟secret,點選除錯即可:

關於響應的講解,點選這裡,可以看到polarity的引數值,因此,例子是負面的,很合理;

剩下的,就是購買了,270起,感興趣的瞭解下,使用的話,就到這裡了;
阿里提供線上除錯,比較方便,但是型別太少了,而且不夠詳細,結果就是正面、負面、中性3選1,一旦哪天有Bug,就慘了;
定時爬取微博
這個章節不想講述太多內容,之前思路都有講過,只是把程式碼結合下而已,詳情請參考下面兩篇文章:
上面3個平臺的結果很明顯,只能用某度,畢竟,免費嘛;
push到微信用的是server醬,直接貼程式碼:
#!/usr/bin/python3 # -*- coding: utf-8 -*- import re from json import JSONDecodeError import time import requests from apscheduler.schedulers.blocking import BlockingScheduler import json import urllib wb_url = "https://m.weibo.cn/profile/info?uid=你要關注的微博使用者id" server_url = "http://sc.ftqq.com/你的server醬.send" # 獲取情緒內容 access_token='你的百度access_token值' bd_url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify?access_token='+access_token wb_headers = { "Host": "m.weibo.cn", "Referer": "https://m.weibo.cn/u/隨便,一般是你要關注的微博使用者id", "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) " "Version/9.0 Mobile/13B143 Safari/601.1", } wb_params = { "text": "{text}", "desp": "{desp}" } statuses_id = "" scheduler = BlockingScheduler() page_size = 10 def get_time(): """ 獲取當前時間 """ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) def push_wx(text=None, desp=None): """ 推送訊息到微信 :param text: 標題 :param desp: 內容 :return: """ wb_params['text'] = text wb_params['desp'] = desp response = requests.get(server_url, params=wb_params) json_data = response.json() if json_data['errno'] == 0: print(get_time() + " 推送成功。") else: print(json_data) print("{0} 推送失敗:{1} \n {2}".format(get_time(), json_data['errno'], json_data['errmsg'])) def filter_emoji(text, replace=""): """ 過濾Emoji表情 :param text: 原文 :param replace: 將Emoji替換為此內容 :return: 過濾後的內容 """ try: co = re.compile(u'[\U00010000-\U0010ffff]') except re.error: co = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]') return co.sub(replace, text) def get_desp(user, statuse): """ 獲取微博內容 """ global text; global nick_name; # 個人資訊 avatar = user['profile_image_url']# 頭像 nick_name = user['screen_name']# 暱稱 follow_count = user['follow_count']# 關注 followers_count = user['followers_count']# 粉絲 description = user['description']# 個性簽名 # 微博資訊 image = "" created_at = statuse['created_at']# 時間 source = statuse['source']# 傳送微博的裝置 # 微博內容 if 'raw_text' in statuse: print(statuse) text = statuse['raw_text'] else: text = statuse['text'] text = filter_emoji(text, "[emoji]") # 獲取圖片 if 'pics' in statuse: pics = statuse['pics'] for pic in pics: image += "\n\n".format(pic['url']) return "\n\n### {1}\n\n關注:{2} and 粉絲:{3}\n\n簽名:{4}\n\n傳送時間:{5}\n\n裝置:{6}\n\n微博內容:\n\n{7}\n\n{8}" \ .format(avatar, nick_name, follow_count, followers_count, description, created_at, source, text, image) def start_task(): # print("執行查詢任務") response = requests.get(wb_url, headers=wb_headers) try: json_data = response.json() except JSONDecodeError as e: print(get_time() + " Json解析異常, 跳過此次迴圈:" + str(e)) return state = json_data['ok'] if state != 1: push_wx(get_time() + " 你的女朋友又掛啦,狀態碼:" + str(state) + ",快去看看吧。", "") scheduler.remove_job('wb') return data = json_data['data'] user = data['user'] statuses = data['statuses'] size = len(statuses) if size < page_size: print(get_time() + " 返回資料不正確,跳過本次迴圈。 size:" + str(size)) return first_statuse = statuses[0] new_id = first_statuse['id'] global statuses_id if new_id != statuses_id: print(get_time() + " 有新微博! id-> " + new_id) # 獲取微博資訊 desp = get_desp(user, first_statuse) title = "女神更新微博啦" release_text = SentimentAnalysis() push_wx(title, release_text+desp + "\n\n[微博原文](https://m.weibo.cn/profile/2105667905)") statuses_id = new_id def SentimentAnalysis(): post_data = {"text": text} data = json.dumps(post_data).encode('GBK') request = urllib.request.Request(bd_url, data) response = urllib.request.urlopen(request) content = response.read() content_str = str(content, encoding="gbk") data = json.loads(content_str) # 積極、消極、可信度的概率 positive_prob = '%.2f%%' % (data["items"][0]["positive_prob"] * 100) negative_prob = '%.2f%%' % (data["items"][0]["negative_prob"] * 100) confidence = '%.2f%%' % (data["items"][0]["confidence"] * 100) sentiment = data["items"][0]["sentiment"] if (positive_prob > negative_prob): prob = positive_prob elif (positive_prob < negative_prob): prob = negative_prob else: prob = positive_prob if (sentiment == 0 ): prob_text = "負面" elif (sentiment == 1 ): prob_text = "中性" elif (sentiment == 2): prob_text = "正向" analysis_text = "你女神博主:"+nick_name + ",釋出了情緒值為"+prob+",疑似是"+prob_text+"情緒的微博,快來看看吧,可信度:"+confidence+",微博原文是:"+text return analysis_text if __name__ == '__main__': print(get_time() + " 騷年,噩夢來襲!") scheduler.add_job(start_task, "interval", seconds=6, id="wb") scheduler.start() 複製程式碼
程式碼不能直接用,要手動輸入幾個值,微博使用者id、某度access_token、server醬,完;
效果圖

%
不知道為什麼被過濾了,正常是XX%這樣的格式,但是看著懂就好了,不糾結;
通過上面的推送資訊,資訊最大化,也得出對應的情緒值,但是,女人說的話,要視不同場景而決定其含義;
比如吵架時的分手,其實就是要你哄,要你抱; 比如成家後的不要,是不捨得,偷偷買吧; 複製程式碼
而這種含義,不結合語境是沒法判斷的;
而女人的心思,別猜,買/哄/舔就對了;
對了,前提是得有個男/女朋友,不然,還是買點護膚品慰勞下自己的右手吧;
小結
本文主要介紹了情緒分析的內容,有手動統計,也有利用BAT平臺的介面,出了某度有免費介面提供外,其他都要收費,而且不低,用來除錯或者內部用用,用某度的挺好的,量多可能會收費,但沒找到具體文件,不糾結了;
同時學習了Py的docopt模組,會抽取幫助資訊裡的內容,然後對命令列傳入的引數進行解析;
而在試用BAT平臺時,會發現呼叫介面都需要安全憑證/授權校驗,目的還是為了安全性,這塊是值得學習的,回想下,內部介面是否無需校驗就可直接呼叫?是否會被第三方利用的可能?
最好,祝有女朋友的,幸福美滿,避開所有障礙,早日拉埋天窗; 沒女朋友的,學會聊天,保持自信,別太死板,最重要是有上進心,陽光活力,換位思考,如果你是女生,你會喜歡自己嗎?
最後,謝謝大家!
