百度API接口+图灵机器人=语音助手

百度API接口+图灵机器人=语音助手

  • 制作流程
    • 技术
    • 百度API准备
    • 图灵机器人注册
    • 功能
    • 环境准备
    • 导入模块
    • 完整代码
    • 运行结果
    • 制作过程的问题及解决办法
    • 参考链接

制作流程

技术

  1. Python
  2. 人工智能
  3. 语音识别
  4. 爬虫
  5. GUI界面设计

百度API准备

去官网申请一个百度API,可以参考链接: https://blog.csdn.net/weixin_38753698/article/details/103142965?utm_source=app&app_version=4.5.2.

图灵机器人注册

注册图灵机器人现在有免费、标准、专业、叠加包四个版本,下面介绍各个版本:

  • 免费版:免费版适用于个人或测试使用,图灵机器人平台每天会赠送免费版用户100次调用量,并开放基本功能模块和技能服务,可满足大部分用户或学习或测试使用。
  • 标准版:标准版更适用于开发者用户使用,图灵机器人平台针对标准版用户开放了全部机器人属性设置功能,满足开发者搭建个性化机器人需求,并在此基础上开放知识库增删改查接口,让开发者更自由的将知识库服务对接到自己的产品中。
  • 专业版:专业版适合自媒体及企业用户使用,由于富媒体及语音合成功能可以在微信公众号上使用,故可最大程度帮助自媒体用户在公众号拉新、促活跃;而同义词、关联问题、聊天记录导出等功能可帮助企业用户降低语料库维护成本,更快捷准确的回复用户业务咨询等问题。
  • 叠加包:叠加包是为解决开发者及企业用户调用次数超过当日调用上限的解决方案。如:若个人版用户当日调用次数超达到500次,已经超出400次但离标准版1000次/天还很远,此时即可购买叠加包服务,后续超出的调用次数可从叠加包中扣除,直至叠加包调用次数用尽为止。

作者本次因个人使用,所以只选了免费版(虽然写的是免费版本,实际意思是比其他三个版本便宜,只需19.9人名币/月)
进入图灵官网注册账号,官网地址: http://www.tuling123.com.

功能

  • 人机交互:在程序中可以自己定义人机交互次数,作者设置的9次,由于选择的免费版本所以功能较少
  • 音乐模式:进去该模式后,直接说出想要播放的音乐即可
  • 天气播报:由于作者个人能力,此功能待完善,但是在人机交互模式中也可以播报天气情况

环境准备

  1. Pycharm3.9
  2. Win10系统

导入模块

import os
import wx
import pyttsx3
import requests
from bs4 import BeautifulSoup
import chardet
from lxml import etree
import urllib
import pygame
import pyaudio
import wave
import win32com.client
from aip import AipSpeech
import pyttsx3
import time
import base64
import random
from binascii import hexlify
from Crypto.Cipher import AES
import json

完整代码

# 导入录音接口的包
import pyaudio
# 导入打开录音文件并设置音频参数的包
import wave
import win32com.client
from aip import AipSpeech
from bs4 import BeautifulSoup
import requests
import chardet
from lxml import etree
import pyttsx3
import pygame
import time
# 初始化语音:SpVoice类是支持语音合成(TTS)的核心类,通过SpVoice对象调用TTS引擎,实现朗读功能
speaker = win32com.client.Dispatch("SAPI.SpVoice")
def record(file_path):
    """pyaudio参数"""
    CHUNK = 1024  # 数据包或者数据片段
    FORMAT = pyaudio.paInt16  # pyaudio.paInt16表示我们使用量化位数 16位来进行录音
    CHANNELS = 1  # 声道,1为单声道,2为双声道
    RATE = 16000  # 采样率,每秒钟16000次
    RECORD_SECONDS = 5  # 录音时间
    WAVE_OUTPUT_FILENAME = file_path  # 录音文件保存为WAVE_OUTPUT_FILENAME
    pau = pyaudio.PyAudio()
    stream = pau.open(format=FORMAT,
                      channels=CHANNELS,
                      rate=RATE,
                      input=True,
                      frames_per_buffer=CHUNK, )
    frames = []
    print("开始录音")
    speaker.Speak("开始录音")
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)
    print("录音结束")
    speaker.Speak("录音结束")
    stream.stop_stream()
    stream.close()
    pau.terminate()
    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(pau.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()
def voice2text(APP_ID, API_KEY, SECRET_KEY, file_path):
    client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
    ret = client.asr(get_data(file_path), 'pcm', 16000, {'dev_pid': 1536}, )
    return ret['result']
def get_data(file_path):
    with open(file_path, 'rb') as fp:
        return fp.read()
#人机互动
import requests
import time
import pygame
import requests
# 导入文本转语音的包
import pyttsx3
# 初始化语音库
engine = pyttsx3.init()
# 向api发送请求
def get_response(msg):
  apiUrl = 'http://www.tuling123.com/openapi/api'
  data = {
    'key': '你的图灵appkey',
    'info': msg,
    'userid': 'pth-robot',
  }
  try:
    r = requests.post(apiUrl, data=data).json()
    engine.say(r.get('text'))
    engine.runAndWait()
    print(r.get('text'))
  except:
    return
def say():
    global chat_message
    # 存放的文件名称的文件夹data
    file_path = "data/chat-audio.wav"
    # 百度需要的参数
    APP_ID = '你的APP_ID'
    API_KEY = '你的API_KEY'
    SECRET_KEY = 'SECRET_KEY'
    # 先调用录音函数
    record(file_path)
    # 语音转成文字的内容
    chat_message = voice2text(APP_ID, API_KEY, SECRET_KEY, file_path)
    print(chat_message)
# 设定人机交互次数函数(即人与图灵机器人的对话次数,注意这里初始值为0小于10,只能调用9次而不是10次)
def chatwithrobot():
    i=0
    while i < 10:
        say()
        get_response(chat_message)
        i = i+1

# 音乐播放
import base64
import random
from binascii import hexlify
# 特别注意Crypto模块的首字母要改成大写,这是一个加解密模块,在爬取音乐评论用到
from Crypto.Cipher import AES
import json
import requests
import urllib, requests
import pyttsx3
import pygame
class GetMusic:
    def __init__(self):
        self.key = GetParamsAndEncSecKey()
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
            'Referer': 'http://music.163.com/'}
        self.session = requests.Session()
        self.session.headers = self.headers
        self.conmment_url = 'https://music.163.com/weapi/v1/resource/comments/R_SO_4_{}?csrf_token='  # 评论
        self.lyric_url = 'https://music.163.com/weapi/song/lyric?csrf_token='  # 歌词
        self.music_url = 'https://music.163.com/weapi/song/enhance/player/url?csrf_token='  # 歌曲
        self.url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='  # 搜索歌曲列表,无歌曲链接
    def get_params_and_encSecKey(self, song=None):
        '''
        获取什么就返回所需要两个参数
        1. 歌曲
        2. 歌词
        3. 评论  默认
        4. 搜索的歌曲列表
        :param song:
        :return:
        '''
        if isinstance(song, int):
            data = {"ids": [song], "br": 128000, "csrf_token": ""}
        elif isinstance(song, str) and song.isdigit():
            data = {"id": song, "lv": -1, "tv": -1, "csrf_token": ""}
        elif song == None:
            data = {}
        else:
            data = {"hlpretag": "", "hlposttag": "", "s": song, "type": "1", "offset": "0",
                    "total": "true", "limit": "30", "csrf_token": ""}
        song = json.dumps(data)
        data = self.key.get(song)
        return data
    def get_music_list_info(self, name):
        '''
        获取歌曲详情:歌名+歌曲id+作者
        :param name:
        :return:
        '''
        data = self.get_params_and_encSecKey(name)
        res = self.session.post(self.url, data=data)  # 歌曲
        song_info = res.json()['result']['songs']
        for song in song_info:
            song_name = song['name']
            song_id = song['id']
            songer = song['ar'][0]['name']
            print(song_name, '\t', song_id, '\t', songer)
            global SongName  # 定义为全局变量
            global SongId  # 定义为全局变量
            global Songer  # 定义为全局变量
            SongName=song_name
            SongId=song_id
            Songer=songer
            self.get_music_url(song_id)
            self.get_music_lyric(song_id)
            self.get_music_comment(song_id)
            break
    def get_music_url(self, id):
        '''
        获取歌曲URL链接
        :param id:
        :return:
        '''
        global Song_url  # 定义为全局变量
        data = self.get_params_and_encSecKey(id)
        res = self.session.post(self.music_url, data=data)
        song_url = res.json()['data'][0]['url']
        Song_url=song_url
        #print(song_url)
    def get_music_lyric(self, id_str):
        '''
        获取歌词
        :param id_str:
        :return:
        '''
        data = self.get_params_and_encSecKey(str(id_str))
        res = self.session.post(self.lyric_url, data=data)
        lyric = res.json()['lrc']['lyric']
        #print(lyric)
    def get_music_comment(self, song_id):
        '''
        获取歌曲评论: 评论人+内容+头像
        :param song_id:
        :return:
        '''
        data = self.get_params_and_encSecKey()
        comment = self.session.post(self.conmment_url.format(str(song_id)), data=data)
        com_list = comment.json()['hotComments']
        for com in com_list:
            content = com['content']
            nickname = com['user']['nickname']
            user_img = com['user']['avatarUrl']
            #print(nickname, '!!!!' + content + '!!!!', user_img)
class GetParamsAndEncSecKey:
    def __init__(self):
        self.txt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        self.i = ''.join(random.sample(self.txt, 16))  # 16为随机数
        self.first_key = '0CoJUm6Qyw8W8jud'
    def get(self, song):
        '''
        获取加密的参数
        params是两次加密的
        :param song:
        :return:
        '''
        res = self.get_params(song, self.first_key)
        params = self.get_params(res, self.i)
        encSecKey = self.get_encSecKey()
        return {
            'params': params,
            'encSecKey': encSecKey
        }
    def get_params(self, data, key):
        '''
        获得params,加密字符长度要是16的倍数
        :param data:
        :param key:
        :return:
        '''
        iv = '0102030405060708'
        num = 16 - len(data) % 16
        data = data + num * chr(num)  # 补足
        cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode())
        result = cipher.encrypt(data.encode())
        result_str = base64.b64encode(result).decode('utf-8')
        return result_str
    def get_encSecKey(self):
        '''
        获取encSecKey,256个字符串
        hexlify--->转换为btyes类型
        pow--->两个参数是幂,三个参数是先幂在取余
        format(rs, 'x').zfill(256)-->256位的16进制
        :return:
        '''
        enc_key = '010001'
        modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
        rs = pow(int(hexlify(self.i[::-1].encode('utf-8')), 16), int(enc_key, 16), int(modulus, 16))
        return format(rs, 'x').zfill(256)
def music():
    file_path="data/music.wav"
    # 百度需要的参数
    APP_ID = '你的APP_ID'
    API_KEY = '你的API_KEY'
    SECRET_KEY = 'SECRET_KEY'
    # 先调用录音函数
    record(file_path)
    # 语音转成文字的内容
    song_message = voice2text(APP_ID, API_KEY, SECRET_KEY, file_path)
    print(song_message)
    input2=''.join(song_message)  #转换成字符串
    song_name=input2[2:]
    Msuic = GetMusic()
    Msuic.get_music_list_info(song_name)
    songid=str(SongId)
    url = 'https://music.163.com/song/media/outer/url?id='+songid
    headers = { 'User-agent':
                'Mozilla/5.0 (X11; Linux x86_64; rv:57.0)Gecko/20100101 Firefox/57.0',
                'Host':'music.163.com',
                'Referer':'https://music.163.com'}
    req = requests.get(url, headers=headers, allow_redirects=False) #拒绝默认的301/302重定向
    musicLink = req.headers['Location']    #从而可以通过html.headers[‘Location’]拿到重定向的URL。
    urllib.request.urlretrieve(musicLink,'data/'+SongName+".mp3")  #下载并重命名文件
    # 初始化engine
    engine = pyttsx3.init()
    # 合成语音
    engine.say("即将为您播放 "+Songer+' 的 '+SongName)
    engine.runAndWait()
    # 初始化混响器
    pygame.mixer.init()
    filename = 'data/' + SongName+'.mp3'
    # 加载音乐
    pygame.mixer.music.load(filename)
    # 循环播放
    pygame.mixer.music.play()


# 天气播报
def weather():
    # 存放的文件名称
    file_path='data/weather-audio.wav'
    # 百度需要的参数
    APP_ID = '你的APP_ID'
    API_KEY = '你的API_KEY'
    SECRET_KEY = 'SECRET_KEY'
    # 图灵需要的参数
    TULING_KEY = '你的图灵appkey'
    # 先调用录音函数
    record(file_path)
    # 语音转成文字的内容
    weather_message =voice2text(APP_ID, API_KEY, SECRET_KEY, file_path)
    print(weather_message)
    #抓取中国天气网指定城主天气
    #input_message=['播放杭州天气情况']
    input2=''.join(weather_message)  #转换成字符串
    city_name=input2[2:4]

    url='http://www.weather.com.cn/weather1d/101080101.shtml'
    

    r = requests.get(url)
    r.encoding = 'utf-8'
    # 提取相关天气信息
    date=r.json()["result"]['result']["date"]
    templow=r.json()["result"]['result']["templow"]
    temphigh=r.json()["result"]['result']["temphigh"]
    tempnow=r.json()["result"]['result']["temp"]
    week=r.json()["result"]['result']["week"]
    tip=r.json()["result"]['result']["index"][3]["detail"]
    weather=r.json()["result"]['result']['daily'][0]['night']['weather']
    add=r.json()["result"]['result']["city"]
    wind=r.json()["result"]['result']['winddirect']
    WS=r.json()["result"]['result']["windpower"]
    t = time.localtime() # 当前时间的纪元值
    fmt = "%H %M"
    now = time.strftime(fmt, t) # 将纪元值转化为包含时、分的字符串
    now = now.split(' ') #以空格切割,将时、分放入名为now的列表中
    hour = now[0]
    minute = now[1]
    wea='你好,今天是%s%s,现在北京时间%s时%s分,%s天气 %s,气温%s摄氏度~%s摄氏度,现在为%s摄氏度,%s,风力%s,%s'%(date,week,hour,minute,add,weather,templow,temphigh,tempnow,wind,WS,tip)
    print(wea)
    engine = pyttsx3.init()
    engine.say('即将为您播放'+city_name+"天气情况")
    engine.say(wea)
    engine.runAndWait()


#GUI界面
import os
import wx
import pyttsx3
import requests
from bs4 import BeautifulSoup
import chardet
from lxml import html
from PIL import ImageFile
import urllib
import pygame
import win32com.client
speaker = win32com.client.Dispatch("SAPI.SpVoice")
# 注意要将界面用到的图片放到和main.py文件同级目录下
class Panel1(wx.Panel):
    """class Panel1 creates a panel with an image on it, inherits wx.Panel"""
    def __init__(self, parent, id):
        # create the panel
        wx.Panel.__init__(self, parent,id)
        try:
            image_file = 'bg.png'
            bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
            self.bitmap1 = wx.StaticBitmap(self, -1, bmp1, (0, 0))
        except IOError:
            print ("Image file is not found") % ImageFile
            raise SystemExit

        pic1 = wx.Image("yinyue.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        pic2 = wx.Image("hudong.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        pic3 = wx.Image("tianqiqing.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()

        # 绘图按钮1,默认风格3D
        self.button1 = wx.BitmapButton(self.bitmap1, -1, pic1, pos=(400, 350), style=0, size=(50, 50))
        self.Bind(wx.EVT_BUTTON, self.On2Click, self.button1)
        self.button1.SetDefault()

        #绘图按钮2,默认风格3D
        self.button2 = wx.BitmapButton(self.bitmap1, -1, pic2, pos = (80, 350),style=0,size=(50,50))
        self.Bind(wx.EVT_BUTTON, self.On1Click, self.button2)
        self.button2.SetDefault()

        #绘图按钮3,默认风格3D
        self.button3 = wx.BitmapButton(self.bitmap1, -1, pic3, pos = (240, 350),style=0,size=(50,50))
        self.Bind(wx.EVT_BUTTON, self.On3Click, self.button3)
        self.button3.SetDefault()


    def On2Click(self, event):
        print("音乐播放")
        speaker.Speak("您已选择音乐播放模式 ")
        music()
        event.Skip()
    def On1Click(self, event):
        print("人机交互")
        speaker.Speak("您已选择人机交互模式 ")
        chatwithrobot()
        event.Skip()
    def On3Click(self, event):
        print("天气播报")
        speaker.Speak("您已选择天气播报模式 ")
        weather()
        event.Skip()

app = wx.App(False)
frame1 = wx.Frame(None, -1, title='小T', size=(600, 640))
# create the class instance
panel1 = Panel1(frame1, -1)
frame1.Show(True)
app.MainLoop()

运行结果

百度API接口+图灵机器人=语音助手_第1张图片

制作过程的问题及解决办法

  1. 申请完百度API时一要在接口界面领取语音合成免费额度,否则会出现502异常,即请求上限错误
  2. lxml包要用最新版,否则会出现不能导入etree模块问题
  3. 导入模块时有些模块只能手动导入,需要先下载各个包对应的whl,然后在终端直接pip安装whl,同时在安装whl时要注意首先cd到下载whl文件的目录,然后再安装,安装是要写whl文件全名(包括文件类型)
  4. 在导入录音模块pyaudio时,一定要注意完整导入,如果出现ModuleNotFoundError: No module named '_portaudio’问题,百度API接口+图灵机器人=语音助手_第2张图片
    说明还是没有完全导入该模块,解决办法可参考链接: https://blog.csdn.net/qq_43280079/article/details/99683448?utm_source=app&app_version=4.5.2
  5. python中下载各轮的地址链接: https://www.lfd.uci.edu/~gohlke/pythonlibs/
  6. GUI界面设计所需的图片bg.png(背景图片)、hudong.png、yinyue.png、tianqiqing.png要和main.py文件在同级目录下,同样在该目录下需要新建一个data文件夹来存储运行后产生的语音文件、音乐作品文件等
  7. 代码中申请百度API后对应的三个参数APP_ID = ‘’, API_KEY = ‘’, SECRET_KEY = ''要换成自己的,同理图灵机器人的appkey也要换成自己的
  8. Crypt模块在下载时首字母是小写的,但是导入时候要修改成大写,否则不会识别

参考链接

原文链接: https://blog.csdn.net/aqqwvfbukn/article/details/106349903

本人小白一枚,刚写博客不久,不喜勿喷,如果有什么问题,还请各位指点,虚心求教

你可能感兴趣的:(python,pycharm,语音识别)