JB的Python之旅-每句話背後的情緒值
前言
每個人相處都有一套生活方式,跟女人也不一樣,不同的女人要用不同的邏輯思考,要琢磨不同語句背後的含義,生活,不容易;
對於情場小白而言,最擔心的就是女朋友不開心了,畢竟好不容易才從右手變成了實物,肯定是加倍愛惜的;
但,你真的瞭解每一句話的情緒分析嗎? 給一句話,能知道這句話的情緒佔比嗎?
情緒分析
情緒分析關鍵的是詞典,網上找了下,大連理工情感詞彙本體庫比較有名,那就試試看;
下載地址:
連結:https://pan.baidu.com/s/18PeWl-9EjZ7O5Rdfejzgig
提取碼:qc3n
複製程式碼
下載完後,看了下說明文件,瞭解下,詞庫裡面的格式是這樣的:
看起來好像不錯,就試試看~
解壓說,發現有這幾個檔案:
簡單介紹下2個py檔案:
- evaluate.py,把情感詞彙.xlsx轉成情感詞彙.csv
- 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 --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting 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 --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting 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
複製程式碼
python setup.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,就慘了;
定時爬取微博
這個章節不想講述太多內容,之前思路都有講過,只是把程式碼結合下而已,詳情請參考下面兩篇文章:
JB的Python之旅-豆瓣自動頂貼功能
JB的Python之旅-爬蟲篇-新浪微博內容爬取
上面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 += "![]({0})\n\n".format(pic['url'])
return "![]({0})\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平臺時,會發現呼叫介面都需要安全憑證/授權校驗,目的還是為了安全性,這塊是值得學習的,回想下,內部介面是否無需校驗就可直接呼叫?是否會被第三方利用的可能?
最好,祝有女朋友的,幸福美滿,避開所有障礙,早日拉埋天窗; 沒女朋友的,學會聊天,保持自信,別太死板,最重要是有上進心,陽光活力,換位思考,如果你是女生,你會喜歡自己嗎?
最後,謝謝大家!