itchat個人練習 語音與文本圖靈測試例程
背景介紹
itchat是一個開源的微信個人號接口,使用python調用微信從未如此簡單。
使用不到三十行的代碼,你就可以完成一個能夠處理所有信息的微信機器人。
官方文檔參考https://itchat.readthedocs.io/zh/latest/
最近要做一個自動應答機器人,獲得用戶消息GUI+語義分析+機器學習給出答案。
準備工作
需要安裝ffmpeg(百度搜索官網,下載windows版解壓後把bin目錄添加到系統變量的path中)
pip安裝 pydub,SpeechRecognition
pip install pydub pip install SpeechRecognition
綁定消息
GUI這部分使用微信的itchat接口,安裝和新手教程可以自己參考官方文檔。
綁定語音消息回復的方式為:
@itchat.msg_register(RECORDING) def tuling_reply(msg):
其中用的是RECORDING是因為之前代碼最開始有from itchat.content import *,否則就要使用itchat.content.RECORDING
關於@修飾符的作用,網上百度就有,說下自己的思考:
@de
def func1:
----- 等價於 ------
func1 = de( func1 )
Python解釋器讀到函數修飾符“@”的時候,後面步驟會是這樣了:
1. 去調用de函數,de函數的入口參數就是那個叫“func1”的函數;
2. de函數被執行,入口參數的(也就是func1函數)會被調用(執行);
換言之,修飾符帶的那個函數的入口參數,就是下面的那個整個的函數。
參考https://blog.csdn.net/972301/article/details/59537712和 https://blog.csdn.net/fwenzhou/article/details/8733857
所以我們使用@的時候,itchat.msg_register這個函數就被執行了,我們定義的tuling_reply作為參數傳了進去,所以才會讀取到消息就用這個函數處理消息
語音識別
由於微信保存的語音消息都是mp3格式,看了一圈發現只有騰訊語音識別支持mp3,之前嘗試過騰訊一句話識別語音API,但是官方沒有最新的例程,並且居然不同部分用的是不同版本的文檔說明,導致我鑒權一直失敗。到後來仔細研讀了下,自己寫了代碼,鑒權應該是通過了,但是返回的消息是x‘\98‘這樣的一個中文字符,並且解碼會失敗,這才發現可能是因為騰訊的只支持中文,雖然我在這個隨筆的例子是中文語音識別,但我實際項目要做的是英文語音識別。不過在這中間也學到了一些東西,比如加密算法的使用,還有python3的二進制和字符串消息的轉換關系。
1 import binascii 2 import hashlib 3 import hmac 4 import urllib.parse 5 import urllib.request 6 import time 7 import random 8 import base64 9 10 def asr(msg): 11 msg[‘Text‘](msg[‘FileName‘])#保存mp3語音 12 timeData = str(int(time.time())) # 時間戳 13 nonceData = int(random.random()*10000) # Nonce,官網給的信息:隨機正整數,與 Timestamp 聯合起來, 用於防止重放攻擊 14 with open(msg[‘FileName‘], ‘rb‘) as f: 15 voiceData = f.read()#讀取mp3語音,獲得byte數據,格式是b‘\x..‘ 16 os.remove(msg[‘FileName‘])#刪除mp3語音 17 DataLenData = len(voiceData)#讀取未base64編碼之前的文件長度 18 tmp = int(timeData)#time stamp 19 signDictData = {#需要註意的是字典的key值要按照ascii碼升序排序,並不一定是字典序,可以使用sorted(signDictData.keys())來查看ascii碼排序結果 20 ‘Action‘ : actionData, 21 ‘Data‘: base64.b64encode(voiceData).decode(‘utf8‘),#base64編碼,編碼後是二進制,再用decode解碼 22 # ‘Data‘: voiceData, 23 ‘DataLen‘: DataLenData, 24 ‘EngSerViceType‘: EngSerViceTypeData, 25 ‘Nonce‘ : nonceData, 26 ‘ProjectId‘:0, 27 ‘Region‘: ‘ap-shanghai‘, 28 ‘SecretId‘ : secretId, 29 # ‘SignatureMethod‘: ‘HmacSHA256‘,#加密算法可選,不指定這個參數默認是HmacSHA1加密 30 ‘SourceType‘: SourceTypeData, 31 ‘SubServiceType‘: SubServiceTypeData, 32 ‘Timestamp‘ : tmp, 33 ‘UsrAudioKey‘: UsrAudioKeyData, 34 ‘Version‘: versionData, 35 ‘VoiceFormat‘: VoiceFormatData 36 } 37 # 請求方法 + 請求主機 +請求路徑 + ? + 請求字符串 38 requestStr = "%s%s%s%s%s"%(requestMethod,uriData,"/","?",dictToStr(signDictData)) 39 # signData = urllib.parse.quote(sign(secretKey,requestStr,‘HmacSHA1‘)) 40 #生成簽名字符的時候一定是使用的沒有經過urlencode編碼的requestStr字符串,下面的加了encode的就是把字符串變成byte,sha1是算法,decode是把二進制解碼為字符串。digest()是把hmac.new()的結果解析成字符串,然後經過base64編碼為byte,再解碼為字符串 41 signData = binascii.b2a_base64(hmac.new(secretKey.encode(‘utf-8‘), requestStr.encode(‘utf-8‘), hashlib.sha1).digest())[:-1].decode() 42 # 上述操作是實現簽名,下面即進行請求 43 # 先建立請求參數, 此處參數只在簽名時多了一個Signature 44 actionArgs = { 45 ‘Action‘ : actionData, 46 ‘Data‘: base64.b64encode(voiceData).decode(‘utf8‘), 47 # ‘Data‘: voiceData, 48 ‘DataLen‘: DataLenData, 49 ‘EngSerViceType‘: EngSerViceTypeData, 50 ‘Nonce‘ : nonceData, 51 ‘ProjectId‘:0, 52 ‘Region‘: ‘ap-shanghai‘, 53 ‘SecretId‘ : secretId, 54 ‘SourceType‘: SourceTypeData, 55 ‘SubServiceType‘: SubServiceTypeData, 56 ‘Timestamp‘ : tmp, 57 ‘UsrAudioKey‘: UsrAudioKeyData, 58 ‘Version‘: versionData, 59 ‘VoiceFormat‘: VoiceFormatData, 60 "Signature": signData 61 } 62 # 根據uri構建請求的url 63 requestUrl = "https://%s/?"%(uriData) 64 # 將請求的url和參數進行拼接,使用urlencode會修改掉參數中的/和=等符號的表示方式 65 requestUrlWithArgs = requestUrl + urllib.parse.urlencode(actionArgs) 66 67 # actionArgs = signDictData #這是深復制,兩個字典就是一個字典 68 # actionArgs["Signature"] = signData 69 70 # # 根據uri構建請求的url 71 # requestUrl = "https://%s/?"%(uriData) 72 # # 將請求的url和參數進行拼接 73 # requestUrlWithArgs = requestUrl + dictToStr(actionArgs) 74 75 # 獲得response 76 responseData = urllib.request.urlopen(requestUrlWithArgs).read().decode("utf-8")# 根據uri構建 77 # return json.loads(responseData)["Response"]["Error"]["Message"] #處理錯誤消息 78 return json.loads(responseData)["Response"]["Result"]#處理正確消息讀取語音文件和騰訊API語音識別
後來一直在找能不能用別的語音api,由於百度的參考文檔最多,我在其中就發現大家為了能夠把音頻發到百度語音api上,就使用了pydub對原音頻文件進行了轉碼,這樣我們就可以發送wav格式的語音,由於本來是想識別英文呢語音的,所以我還是嘗試外國公司的api,嘗試了微軟語音識別,7天免費的那個,官方文檔對於REST接口的參考太少了,並且都不是python的,這時候我在github上發現了一個SpeechRecognition項目,原來以為是只有谷歌語音識別的接口,嘗試了一下結果果然被墻了,用了代理之後還是無法訪問,然後我就看了github主頁的Transcribe an audio file,在裏面找到了不止一個接口,其中就有Microsoft Bing Voice Recognition的例程,調用非常簡單,只需要語音文件和密鑰,並且支持語音文件的格式轉碼,自動給你轉成對應必應api的語音參數格式,各位可以自己進入r.recognize_bing()函數定義,在裏面詳細描述了如何使用必應語音服務(需要註意的是微軟一元試用雲服務的活動不支持必應語音識別這個模塊),在這裏把原話復制下來供參考:
""" Performs speech recognition on ``audio_data`` (an ``AudioData`` instance), using the Microsoft Bing Speech API. The Microsoft Bing Speech API key is specified by ``key``. Unfortunately, these are not available without `signing up for an account <https://azure.microsoft.com/en-ca/pricing/details/cognitive-services/speech-api/>`__ with Microsoft Azure. To get the API key, go to the `Microsoft Azure Portal Resources <https://portal.azure.com/>`__ page, go to "All Resources" > "Add" > "See All" > Search "Bing Speech API > "Create", and fill in the form to make a "Bing Speech API" resource. On the resulting page (which is also accessible from the "All Resources" page in the Azure Portal), go to the "Show Access Keys" page, which will have two API keys, either of which can be used for the `key` parameter. Microsoft Bing Speech API keys are 32-character lowercase hexadecimal strings. The recognition language is determined by ``language``, a BCP-47 language tag like ``"en-US"`` (US English) or ``"fr-FR"`` (International French), defaulting to US English. A list of supported language values can be found in the `API documentation <https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/bingvoicerecognition#recognition-language>`__ under "Interactive and dictation mode". Returns the most likely transcription if ``show_all`` is false (the default). Otherwise, returns the `raw API response <https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/bingvoicerecognition#sample-responses>`__ as a JSON dictionary. Raises a ``speech_recognition.UnknownValueError`` exception if the speech is unintelligible. Raises a ``speech_recognition.RequestError`` exception if the speech recognition operation failed, if the key isn‘t valid, or if there is no internet connection. """Bing語音識別使用說明
所以我們只需要獲得正確的密鑰,調用這個函數就可以啦,要註意的是中文語音識別需要在傳入參數中設置language="zh-CN"
代碼
全代碼如下:
# -*- coding: UTF-8 -*- import requests import itchat import json from itchat.content import * import os import speech_recognition as sr from pydub import AudioSegment def get_response_tuling(msg): # 這裏我們就像在“3. 實現最簡單的與圖靈機器人的交互”中做的一樣 # 構造了要發送給服務器的數據 apiUrl = ‘http://www.tuling123.com/openapi/api‘ data = { ‘key‘ : ‘8edce3ce905a4c1dbb965e6b35c3834d‘, ‘info‘ : msg, ‘userid‘ : ‘wechat-robot‘, } try: r = requests.post(apiUrl, data=data).json() # 字典的get方法在字典沒有‘text‘值的時候會返回None而不會拋出異常 return r.get(‘text‘) # 為了防止服務器沒有正常響應導致程序異常退出,這裏用try-except捕獲了異常 # 如果服務器沒能正常交互(返回非json或無法連接),那麽就會進入下面的return except: # 將會返回一個None return def asr(msg): #語音消息識別轉文字輸出 msg[‘Text‘](msg[‘FileName‘]) song = AudioSegment.from_mp3(msg[‘FileName‘]) song.export("tmp.wav", format="wav") r = sr.Recognizer() with sr.AudioFile(‘tmp.wav‘) as source: audio = r.record(source) # read the entire audio file os.remove(‘tmp.wav‘) os.remove(msg[‘FileName‘]) # recognize speech using Microsoft Bing Voice Recognition BING_KEY = "======修改成你自己的密鑰=======" # Microsoft Bing Voice Recognition API keys 32-character lowercase hexadecimal strings try: text = r.recognize_bing(audio, key=BING_KEY,language="zh-CN") print("Microsoft Bing Voice Recognition thinks you said " + text) return text except sr.UnknownValueError: print("Microsoft Bing Voice Recognition could not understand audio") except sr.RequestError as e: print("Could not request results from Microsoft Bing Voice Recognition service; {0}".format(e)) @itchat.msg_register(TEXT)#因為之前把itchat.content全部import了,裏面有TEXT變量 def tuling_reply_text(msg): # 註冊文字消息獲取後的處理 # 為了保證在圖靈Key出現問題的時候仍舊可以回復,這裏設置一個默認回復 defaultReply = ‘I received a: ‘ + msg[‘Text‘] return get_response_tuling(msg[‘Text‘]) or defaultReply @itchat.msg_register(RECORDING) def tuling_reply(msg): # 註冊語音消息獲取後的處理 # 為了保證在圖靈Key出現問題的時候仍舊可以回復,這裏設置一個默認回復 defaultReply = ‘I received a: ‘ + msg[‘Type‘] # 如果圖靈Key出現問題,那麽reply將會是None asrMessage = asr(msg) return get_response_tuling(asrMessage) or defaultReply # 為了讓實驗過程更加方便(修改程序不用多次掃碼),我們使用熱啟動hotReload=True itchat.auto_login(hotReload=True) itchat.run()
itchat個人練習 語音與文本圖靈測試例程