一、程序概述
本程序是一个基于 Python 的语音查询天气系统,它结合了百度语音识别、百度语音合成和和风天气 API,允许用户通过语音输入查询指定城市和日期(今天、明天、后天)的天气信息,并以语音形式输出查询结果。
二、环境准备
2.1 依赖库安装
在运行本程序之前,需要安装以下 Python 库:
requests
:用于发送 HTTP 请求,获取和风天气的天气信息。baidu-aip
:百度 AI 开放平台的 Python SDK,用于语音识别和语音合成。sounddevice
:用于录制音频。numpy
:用于处理音频数据。re
:Python 内置的正则表达式库,用于解析用户输入的语音文本。urllib.parse
:Python 内置库,用于对城市名进行 URL 编码。
可以使用以下命令进行安装:
bash
pip install requests baidu-aip sounddevice numpy
2.2 API 密钥获取
- 百度语音识别和合成:
- 访问 百度 AI 开放平台,注册并登录账号。
- 创建一个语音识别和语音合成的应用,获取
BAIDU_APP_ID
、BAIDU_API_KEY
和BAIDU_SECRET_KEY
。
- 和风天气:
- 访问 和风天气开发者平台,注册并登录账号。
- 创建一个应用,获取
HEWEATHER_KEY
。
2.3 代码配置
将代码中的 BAIDU_APP_ID
、BAIDU_API_KEY
、BAIDU_SECRET_KEY
和 HEWEATHER_KEY
替换为你自己的 API 密钥。
import requests
from aip import AipSpeech
from datetime import datetime, timedelta
import sounddevice as sd
import numpy as np
import re
from urllib.parse import quote
# 百度语音识别配置
BAIDU_APP_ID = '百度ID'
BAIDU_API_KEY = '百度key'
BAIDU_SECRET_KEY = '百度key'
# 和风天气配置
HEWEATHER_KEY = '和风api'
# 初始化百度语音客户端
aip_client = AipSpeech(BAIDU_APP_ID, BAIDU_API_KEY, BAIDU_SECRET_KEY)
def speech_recognition(file_path):
"""百度语音识别"""
with open(file_path, 'rb') as f:
audio_data = f.read()
result = aip_client.asr(audio_data, 'pcm', 16000, {
'dev_pid': 1537 # 普通话(支持简单的英文识别)
})
if'result' in result:
return result['result'][0]
else:
raise Exception("语音识别失败:" + str(result))
# def parse_query(text):
"""解析查询文本"""
# 提取城市和日期信息
city = None
date_keyword = '今天'
# 简单模式匹配
patterns = [
r'(.*?)的?(今天|明天|后天)天气',
r'(今天|明天|后天)(.*?)的天气'
]
for pattern in patterns:
match = re.search(pattern, text)
if match:
groups = match.groups()
if len(groups) == 2:
city = groups[0] if groups[0] not in ['今天', '明天', '后天'] else groups[1]
date_keyword = groups[1] if groups[1] in ['今天', '明天', '后天'] else groups[0]
break
# 日期转换
today = datetime.now()
date_map = {
'今天': 0,
'明天': 1,
'后天': 2
}
target_date = today + timedelta(days=date_map.get(date_keyword, 0))
return city.strip(), target_date.strftime('%Y-%m-%d')
# def parse_query(text):
"""解析查询文本"""
# 提取城市和日期信息
city = None
date_keyword = '今天'
# 简单模式匹配
patterns = [
r'(.*?)的?(今天|明天|后天)天气',
r'(今天|明天|后天)(.*?)的天气'
]
for pattern in patterns:
match = re.search(pattern, text)
if match:
print(f"匹配结果: {match.groups()}") # 添加调试信息
groups = match.groups()
if len(groups) == 2:
city = groups[0] if groups[0] not in ['今天', '明天', '后天'] else groups[1]
date_keyword = groups[1] if groups[1] in ['今天', '明天', '后天'] else groups[0]
break
if city is None:
print("未识别到有效的城市信息,请重新录制。")
return None, None
# 日期转换
today = datetime.now()
date_map = {
'今天': 0,
'明天': 1,
'后天': 2
}
target_date = today + timedelta(days=date_map.get(date_keyword, 0))
return city.strip(), target_date.strftime('%Y-%m-%d')
# def get_weather_info(city_name, date):
"""获取天气信息"""
# 获取城市ID
location_url = f"https://geoapi.qweather.com/v2/city/lookup?location={city_name}&key={HEWEATHER_KEY}"
res = requests.get(location_url)
location_data = res.json()
if location_data['code'] != '200':
raise Exception("城市查询失败")
city_id = location_data['location'][0]['id']
# 获取天气预报
weather_url = f"https://devapi.qweather.com/v7/weather/3d?location={city_id}&key={HEWEATHER_KEY}"
res = requests.get(weather_url)
weather_data = res.json()
if weather_data['code'] != '200':
raise Exception("天气查询失败")
# 匹配目标日期
for forecast in weather_data['daily']:
if forecast['fxDate'] == date:
return {
'date': date,
'city': city_name,
'day_weather': forecast['textDay'],
'night_weather': forecast['textNight'],
'max_temp': forecast['tempMax'],
'min_temp': forecast['tempMin']
}
raise Exception("未找到对应日期的天气信息")
def parse_query(text):
"""解析查询文本"""
# 提取城市和日期信息
city = None
date_keyword = '今天'
# 简单模式匹配
patterns = [
r'(.*?)(今天|明天|后天)的?天气',
r'(今天|明天|后天)(.*?)的?天气'
]
for pattern in patterns:
match = re.search(pattern, text)
if match:
print(f"匹配结果: {match.groups()}")
groups = match.groups()
if len(groups) == 2:
city = groups[0] if groups[0] not in ['今天', '明天', '后天'] else groups[1]
date_keyword = groups[1] if groups[1] in ['今天', '明天', '后天'] else groups[0]
break
if city is None:
print("未识别到有效的城市信息,请重新录制。")
return None, None
# 日期转换
today = datetime.now()
date_map = {
'今天': 0,
'明天': 1,
'后天': 2
}
target_date = today + timedelta(days=date_map.get(date_keyword, 0))
return city.strip(), target_date.strftime('%Y-%m-%d')
# def get_weather_info(city_name, date):
"""获取天气信息"""
# 获取城市ID
location_url = f"https://geoapi.qweather.com/v2/city/lookup?location={city_name}&key={HEWEATHER_KEY}"
res = requests.get(location_url)
location_data = res.json()
print(f"城市查询返回数据: {location_data}") # 添加调试信息
try:
if location_data['code'] != '200':
raise Exception("城市查询失败")
except KeyError:
raise Exception(f"城市查询返回数据格式错误: {location_data}")
city_id = location_data['location'][0]['id']
# 获取天气预报
weather_url = f"https://devapi.qweather.com/v7/weather/3d?location={city_id}&key={HEWEATHER_KEY}"
res = requests.get(weather_url)
weather_data = res.json()
print(f"天气查询返回数据: {weather_data}") # 添加调试信息
try:
if weather_data['code'] != '200':
raise Exception("天气查询失败")
except KeyError:
raise Exception(f"天气查询返回数据格式错误: {weather_data}")
# 匹配目标日期
for forecast in weather_data['daily']:
if forecast['fxDate'] == date:
return {
'date': date,
'city': city_name,
'day_weather': forecast['textDay'],
'night_weather': forecast['textNight'],
'max_temp': forecast['tempMax'],
'min_temp': forecast['tempMin']
}
raise Exception("未找到对应日期的天气信息")
def get_weather_info(city_name, date):
"""获取天气信息"""
# 对城市名进行 URL 编码
encoded_city_name = quote(city_name)
# 获取城市ID
location_url = f"https://geoapi.qweather.com/v2/city/lookup?location={encoded_city_name}&key={HEWEATHER_KEY}"
res = requests.get(location_url)
location_data = res.json()
print(f"城市查询返回数据: {location_data}")
try:
if location_data['code'] != '200':
raise Exception("城市查询失败")
except KeyError:
raise Exception(f"城市查询返回数据格式错误: {location_data}")
city_id = location_data['location'][0]['id']
# 获取天气预报
weather_url = f"https://devapi.qweather.com/v7/weather/3d?location={city_id}&key={HEWEATHER_KEY}"
res = requests.get(weather_url)
weather_data = res.json()
print(f"天气查询返回数据: {weather_data}")
try:
if weather_data['code'] != '200':
raise Exception("天气查询失败")
except KeyError:
raise Exception(f"天气查询返回数据格式错误: {weather_data}")
# 匹配目标日期
for forecast in weather_data['daily']:
if forecast['fxDate'] == date:
return {
'date': date,
'city': city_name,
'day_weather': forecast['textDay'],
'night_weather': forecast['textNight'],
'max_temp': forecast['tempMax'],
'min_temp': forecast['tempMin']
}
raise Exception("未找到对应日期的天气信息")
def text_to_speech(text):
"""文本转语音"""
result = aip_client.synthesis(text, 'zh', 1, {
'vol': 5,
'per': 0 # 0-女声,1-男声
})
if not isinstance(result, dict):
with open('output.mp3', 'wb') as f:
f.write(result)
return 'output.mp3'
else:
raise Exception("语音合成失败")
def record_speech():
"""录制语音"""
input("请按回车键开始录制语音,录制时长为5秒...")
# 采样频率
fs = 16000
# 录制时长
duration = 5
print("开始录制...")
recording = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype=np.int16)
sd.wait()
print("录制完成。")
# 保存为PCM文件
with open('input.pcm', 'wb') as f:
f.write(recording.tobytes())
def main():
try:
# 1. 录制语音
record_speech()
# 2. 语音识别
text = speech_recognition('input.pcm')
print(f"识别结果:{text}")
if not text:
print("语音识别未得到有效文本,请重新录制。")
return
# 3. 解析查询内容
city, date = parse_query(text)
print(f"解析结果:城市={city}, 日期={date}")
# 4. 获取天气信息
weather = get_weather_info(city, date)
response_text = (
f"{weather['city']}{date}的天气预报:"
f"白天{weather['day_weather']},"
f"夜间{weather['night_weather']},"
f"最高气温{weather['max_temp']}度,"
f"最低气温{weather['min_temp']}度"
)
print(response_text)
# 5. 生成语音反馈
audio_file = text_to_speech(response_text)
print(f"语音反馈已生成:{audio_file}")
except Exception as e:
print(f"处理出错:{str(e)}")
if __name__ == '__main__':
main()