Python 智能语音机器人(改进版)

本篇为改进版,之前部分代码存在错误,部分网站api也已经失效,现在更换api,并对部分代码进行重写。

本次在Pycharm上测试
相关模块如下:

baidu-aip==4.16.11
beautifulsoup4==4.12.2
chardet==5.1.0
lxml==4.9.2
PyAudio==0.2.13
pycryptodome==3.18.0
pygame==2.4.0
pyttsx3==2.90
requests==2.31.0
wxPython==4.2.1

废话不多说,先上效果图

Python 智能语音机器人(改进版)_第1张图片

功能一:人机互动

Python 智能语音机器人(改进版)_第2张图片

功能二:播放音乐

Python 智能语音机器人(改进版)_第3张图片

功能三:天气

在这里插入图片描述

功能四:一句话新闻

Python 智能语音机器人(改进版)_第4张图片

把以下参数更换成你自己的

# 百度需要的参数
APP_ID = '000000'
API_KEY = 'xxxxxx'
SECRET_KEY = 'xxxxxx'
# 图灵机器人需要的参数
tuling_apiKey = "xxxxxx"
tuling_userId = "000000"
# 易客云需要的参数
yikeyun_appid = '000000'
yikeyun_appsecret = 'xxxxxx'

易客云平台(https://yikeapi.com/account)
Python 智能语音机器人(改进版)_第5张图片

图灵机器人
http://www.tuling123.com/member/robot/index.jhtml
Python 智能语音机器人(改进版)_第6张图片

百度语音识别api 介绍请看
https://ai.baidu.com/tech/speech?_=1687103124997

控制台:
https://console.bce.baidu.com/ai/?fromai=1#/ai/speech/overview/index
Python 智能语音机器人(改进版)_第7张图片
按照这些步骤一步一步创建自己的应用,完成个人实名认证可以有一定的免费使用量,创建应用完成后,从”我的应用“ 即可看到相应的userid, apikey 等。


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

# 百度需要的参数
APP_ID = '000000'
API_KEY = 'xxxxxx'
SECRET_KEY = 'xxxxxx'
# 图灵机器人需要的参数
tuling_apiKey = "xxxxxx"
tuling_userId = "000000"
# 易客云需要的参数
yikeyun_appid = '000000'
yikeyun_appsecret = 'xxxxxx'



speaker = win32com.client.Dispatch("SAPI.SpVoice")
def record(file_path):
    # 各路参数
    CHUNK = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 16000
    RECORD_SECONDS = 5
    WAVE_OUTPUT_FILENAME = file_path
    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()


#人机互动
engine = pyttsx3.init()
# 向api发送请求
def get_response(msg):
    apiUrl  ='http://openapi.turingapi.com/openapi/api/v2'
    data = {
        "reqType": 0,
        "perception": {
            "inputText": {
                "text":  msg
            },
        },
        "selfInfo": {
            "location": {
                "city": "北京",
                "province": "北京",
                "street": "信息路"
            }
        },
        "userInfo": {
            "apiKey": tuling_apiKey,
            "userId": tuling_userId,
        }
    }
    try:
        data = json.dumps(data)
        r = requests.post(apiUrl, data=data).json()
        res = r['results'][0]['values']['text']
        print(res)
        engine.say(res)
        engine.runAndWait()
    except:
        return

def say():
    global chat_message
    # 存放的文件名称
    file_path = "./data/chat-audio.wav"

    # 先调用录音函数
    record(file_path)
    # 语音转成文字的内容
    chat_message = voice2text(APP_ID, API_KEY, SECRET_KEY, file_path)
    print(chat_message)

def chatwithrobot():
    i=0
    while i<5:
        say()
        get_response(chat_message)
        i=i+1
    print("5次对话已经结束!")


#音乐播放

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"

    # 先调用录音函数
    record(file_path)
    # 语音转成文字的内容
    song_message = voice2text(APP_ID, API_KEY, SECRET_KEY, file_path)
    print(song_message)
    song_name = song_message[0].split('播放')[1]
    Msuic = GetMusic()
    Msuic.get_music_list_info(song_name)
    songid=str(SongId)

    headers = { 'User-agent':
                'Mozilla/5.0 (X11; Linux x86_64; rv:57.0)Gecko/20100101 Firefox/57.0',
                }

    response = requests.get("http://music.163.com/song/media/outer/url?id=" + songid + ".mp3",
                             headers = headers)
    content = response.content
    with open('./data/'+SongName+'.mp3', mode = "wb") as f:
        f.write(content)
    print(SongName + "  " + "下载完成")

    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'

    # 先调用录音函数
    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]

    s1 = f'https://v0.yiketianqi.com/api?unescape=1&version=v61&appid={yikeyun_appid}&appsecret={yikeyun_appsecret}&city='

    url=s1+city_name  #拼接 url
    r = requests.get(url)
    r.encoding = 'utf-8'
    # 提取相关天气信息
    date=r.json()["date"]
    templow=r.json()["tem2"]
    temphigh=r.json()["tem1"]
    tempnow=r.json()["tem"]
    week=r.json()["week"]
    tip=r.json()["air_tips"]
    weather=r.json()['wea_day']
    add=r.json()["city"]
    wind=r.json()['win']
    WS=r.json()["win_speed"]
    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()


#新闻播报
def news():
    def get_content():
        url="https://news.topurl.cn/" # 爬取一名话新闻网科技类新闻
        headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}
        rqg=requests.get(url,headers=headers) # 开始爬取,设置headers信息来伪装成浏览器请求数据
        rqg.encoding=chardet.detect(rqg.content)['encoding']
        html=rqg.content.decode('utf-8') #换化编码为utf-8
        soup=BeautifulSoup(html,'lxml')
        soup.prettify() #格式化soup对象
        return soup
    soup2=get_content()
    target=soup2.find_all(class_='news-wrap') #取新闻标题
    Tag=[]
    for tag in target:
        Tag.append(tag.get_text())  #取新闻标题文本添加到列表中
    list_news = Tag[0].replace('\n', '').split('\u2003')
    list_news.pop() # 删除最后一个空项

    engine = pyttsx3.init()
    engine.say('即将为您播放新闻')
    engine.runAndWait()
    for news in list_news:
        print(news)
        engine.say(news)
        engine.runAndWait()

#GUI界面

speaker = win32com.client.Dispatch("SAPI.SpVoice")
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 = './image/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") % image_file
            raise SystemExit
        pic1 = wx.Image("./image/hudong.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        pic2 = wx.Image("./image/yinyue.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        pic3 = wx.Image("./image/tianqiqing.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        pic4 = wx.Image("./image/xinwen.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        #绘图按钮1,默认风格3D
        self.button1 = wx.BitmapButton(self.bitmap1, -1, pic1, pos = (230, 420),style=0,size=(50,50))
        self.Bind(wx.EVT_BUTTON, self.On1Click, self.button1)
        self.button1.SetDefault()
        #绘图按钮2,默认风格3D
        self.button2 = wx.BitmapButton(self.bitmap1, -1, pic2, pos = (300, 420),style=0,size=(50,50))
        self.Bind(wx.EVT_BUTTON, self.On2Click, self.button2)
        self.button2.SetDefault()
        #绘图按钮3,默认风格3D
        self.button3 = wx.BitmapButton(self.bitmap1, -1, pic3, pos = (230, 480),style=0,size=(50,50))
        self.Bind(wx.EVT_BUTTON, self.On3Click, self.button3)
        self.button3.SetDefault()
        #绘图按钮4,默认风格3D
        self.button4 = wx.BitmapButton(self.bitmap1, -1, pic4, pos = (300, 480),style=0,size=(50,50))
        self.Bind(wx.EVT_BUTTON, self.On4Click, self.button4)
        self.button4.SetDefault()
    def On1Click(self, event):
        print("人机交互")
        speaker.Speak("您已选择人机交互模式 ")
        chatwithrobot()
        event.Skip()
    def On2Click(self, event):
        print("音乐播放")
        speaker.Speak("您已选择音乐播放模式 ")
        music()
        event.Skip()
    def On3Click(self, event):
        print("天气播报")
        speaker.Speak("您已选择天气播报模式 ")
        weather()
        event.Skip()
    def On4Click(self, event):
        print("新闻播报")
        speaker.Speak("您已选择新闻播报模式 ")
        news()
        event.Skip()


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

代码及图片地址:
链接:https://pan.baidu.com/s/1pIjEhUMss9cWIAtEmTSqhg
提取码:heao

你可能感兴趣的:(python,机器人,开发语言)