Python打造智能天气语音播报系统
📌 引言
想不想让你的电脑每天自动播报天气预报?用Python调用高德地图天气API获取实时数据,再通过百度语音合成把它“念”出来,最后用PyAudio播放——这就是一个完整的信息获取→处理→输出的物联网小项目。
本教程将从零开始,手把手教你完成:(我会把我的代码放到最后面)
-
🔑 注册并获取高德地图与百度智能云API密钥
-
📡 调用高德天气API获取实时天气
-
🎤 用百度语音合成API把文字转成语音(POST请求,PCM格式)
-
🔐 实践中理解hashlib(MD5)和base64编码
-
🎵 给PCM裸数据添加WAV头,转为播放器可直接打开的wav文件
-
🔊 使用PyAudio播放生成的天气语音
🧰 环境与工具准备
首先确保你的电脑已安装Python(推荐3.7+),然后安装以下第三方库:
bash
pip install requests pyaudio wave
-
requests:发送HTTP请求 -
pyaudio:跨平台音频播放(底层基于PortAudio) -
wave、struct:Python内置,无需安装,用于WAV文件处理
🔑 第一步:获取API密钥
1. 高德开放平台
-
访问 高德开放平台 注册并登录
-
进入控制台 → 应用管理 → 创建新应用

-
添加Key,服务平台选择 Web服务,即可获取一个API Key(示例代码中的
AMAP_KEY)
点击设置:

把这些配置好往下滑点击提交即可,这样我们就创建好了
-
天气查询的城市编码可参考 高德城市编码表 ,例如喀什地区编码为
653100
https://lbs.amap.com/api/webservice/guide/api/weatherinfo(这个是相关的api文档网址)

城市编码的获取方式:


2. 百度智能云
-
前往 百度智能云控制台 注册并登录
-
创建应用:在产品服务中找到 语音技术 → 创建应用,勾选“语音合成”

-
应用创建后你会获得
API Key和Secret Key(即示例中的BAIDU_API_KEY和BAIDU_SECRET_KEY)

🌤️ 第二步:获取实时天气(高德天气API详解)
高德天气API的调用非常简单,是一个HTTP GET请求。(这个前面说过了)
接口说明
-
接口地址:
https://restapi.amap.com/v3/weather/weatherInfo -
请求方式:GET
-
参数:
-
city:城市编码(如653100) -
key:你的高德API Key -
extensions:base返回实时天气,all返回预报,这里用base
-
代码实现
python
def get_weather():
weather_url = "https://restapi.amap.com/v3/weather/weatherInfo"
params = {
"city": CITY_CODE,
"key": AMAP_KEY,
"extensions": "base"
}
resp = requests.get(weather_url, params=params)
data = resp.json()
if data.get("status") != "1":
raise Exception(f"天气API错误: {data}")
live = data["lives"][0]
city = live["city"]
weather = live["weather"]
temperature = live["temperature"]
wind_direction = live["winddirection"]
wind_power = live["windpower"]
humidity = live.get("humidity", "未知")
text = (f"{city}当前天气{weather},温度{temperature}摄氏度,"
f"{wind_direction}风{wind_power}级,空气湿度{humidity}%。")
return text
返回的 text 就是我们后续要合成语音的播报内容,例如:
“喀什地区当前天气晴,温度25摄氏度,西北风3级,空气湿度40%。”
🤖 第三步:百度语音合成——从文字到声音
百度语音合成采用 OAuth 2.0 客户端模式 鉴权,需要先拿 access_token,再调用合成接口。这恰好是我们学习 POST、hashlib、base64 的好机会。
3.1 获取 access_token


接口详情:
-
地址:
https://aip.baidubce.com/oauth/2.0/token -
方式:POST
-
参数:
-
grant_type:固定为client_credentials -
client_id:即百度API Key -
client_secret:即百度Secret Key
-
python
def get_baidu_access_token():
token_url = "https://aip.baidubce.com/oauth/2.0/token"
params = {
"grant_type": "client_credentials",
"client_id": BAIDU_API_KEY,
"client_secret": BAIDU_SECRET_KEY
}
resp = requests.post(token_url, params=params)
token_data = resp.json()
if "access_token" not in token_data:
raise Exception(f"获取token失败: {token_data}")
# 知识点演示:base64编码
token_base64 = base64.b64encode(token_data["access_token"].encode()).decode()
print("base64示例:", token_base64[:50])
return token_data["access_token"]
这里我们故意把 access_token 做了 base64 编码输出,让你直观感受编码过程(实际上API无需此步骤,仅为教学展示)。base64 是一种用64个字符表示二进制数据的方法,常用于网络传输。
3.2 文本转语音(POST请求)


-
合成接口:
https://tsn.baidu.com/text2audio -
方式:POST,表单提交(
Content-Type: application/x-www-form-urlencoded) -
必填参数:
-
tex:要合成的文本(需UTF-8编码) -
tok:上一步获取的 access_token -
cuid:用户唯一标识,随便填一个字符串 -
ctp:客户端类型,固定1(web端) -
lan:语言,中文填zh
-
-
可选调优参数:
-
per:发音人,4=度丫丫(可爱女声),0/1为普通女/男声 -
spd:语速0-9,5为中速 -
pit:音调0-9,5正常 -
vol:音量0-15,9较大 -
format:返回格式,我们填pcm以便演示转换
-
代码中,我们还顺便展示了 hashlib.md5 的使用,计算播报文本的MD5摘要,增加趣味性。
python
def text_to_speech(text, access_token):
# MD5演示
text_md5 = hashlib.md5(text.encode('utf-8')).hexdigest()
print("文本MD5:", text_md5)
data = {
"tex": text,
"lan": "zh",
"ctp": "1",
"tok": access_token,
"cuid": "PythonClient_Weather",
"per": "4",
"spd": "5",
"vol": "9",
"pit": "5",
"format": "pcm"
}
resp = requests.post(TTS_URL, data=data)
# 如果返回JSON说明出错
if resp.headers.get('Content-Type', '').startswith('application/json'):
error_info = resp.json()
raise Exception(f"语音合成错误: {error_info}")
audio_data = resp.content
return audio_data
成功调用后,audio_data 就是纯PCM音频的二进制数据,无文件头,不能直接双击播放。
🎚️ 第四步:PCM转WAV(深入理解WAV文件头)
PCM(脉冲编码调制)是纯粹的音频采样数据,缺少描述采样率、声道数等元信息的文件头。而WAV格式就是在PCM数据前加一个44字节的标准头,结构如下:
| 偏移 | 字段 | 大小 | 说明 |
|---|---|---|---|
| 0 | ChunkID | 4 | “RIFF” |
| 4 | ChunkSize | 4 | 文件总长-8 |
| 8 | Format | 4 | “WAVE” |
| 12 | Subchunk1ID | 4 | “fmt ” |
| 16 | Subchunk1Size | 4 | 16 (PCM) |
| 20 | AudioFormat | 2 | 1 = PCM |
| 22 | NumChannels | 2 | 声道数,1为单声道 |
| 24 | SampleRate | 4 | 采样率,百度默认16000 |
| 28 | ByteRate | 4 | 每秒字节数 |
| 32 | BlockAlign | 2 | 每个采样帧的字节数 |
| 34 | BitsPerSample | 2 | 位深度,16bit→2字节 |
| 36 | Subchunk2ID | 4 | “data” |
| 40 | Subchunk2Size | 4 | 实际音频数据长度 |
| 44 | data | PCM数据 |
我们用 struct.pack 按小端格式将这些值打包写入文件即可。
python
def pcm_to_wav(pcm_data, wav_path, sample_rate=16000, channels=1, sample_width=2):
num_samples = len(pcm_data) // sample_width
byte_rate = sample_rate * channels * sample_width
block_align = channels * sample_width
with open(wav_path, 'wb') as f:
f.write(b'RIFF')
f.write(struct.pack('<I', 36 + len(pcm_data))) # ChunkSize
f.write(b'WAVE')
f.write(b'fmt ')
f.write(struct.pack('<I', 16)) # Subchunk1Size
f.write(struct.pack('<H', 1)) # PCM format
f.write(struct.pack('<H', channels))
f.write(struct.pack('<I', sample_rate))
f.write(struct.pack('<I', byte_rate))
f.write(struct.pack('<H', block_align))
f.write(struct.pack('<H', sample_width * 8))
f.write(b'data')
f.write(struct.pack('<I', len(pcm_data)))
f.write(pcm_data)
生成的WAV文件可以用任何音乐播放器打开,甚至能直接拖进剪辑软件。
🔊 第五步:PyAudio播放语音
pyaudio 库使用起来非常简单:
-
用
wave.open读取WAV文件,获取参数(声道数、采样宽度、帧速率) -
用
pyaudio.PyAudio()实例化一个音频对象 -
打开一个输出流,设定格式与WAV一致
-
循环读出音频帧并写入流
-
关闭流并终止PyAudio
python
def play_wav(wav_path):
wf = wave.open(wav_path, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(1024)
while data:
stream.write(data)
data = wf.readframes(1024)
stream.stop_stream()
stream.close()
p.terminate()
wf.close()
🧩 完整主流程串联
python
def main():
text = get_weather() # 1.获取天气文本
token = get_baidu_access_token() # 2.拿token
pcm_audio = text_to_speech(text, token) # 3.文本→PCM
with open(SAVE_PCM_PATH, 'wb') as f:
f.write(pcm_audio) # 保存原始PCM(学习用)
pcm_to_wav(pcm_audio, SAVE_WAV_PATH) # 4.PCM→WAV
play_wav(SAVE_WAV_PATH) # 5.播放
运行后,你就能听到用甜美的度丫丫语音播报的真实天气了!全程仅需几秒,还自动生成了PCM和WAV文件。
下面我给你们提供我的完整的代码:
# -*- coding: utf-8 -*-
"""
实时天气播报(使用高德地图 + 百度语音合成)
实现功能:
1. 获取实时天气数据(地区、温度、湿度、风力、天气状况等)
2. 使用百度语音合成API,采用POST请求,获取PCM音频数据
3. 演示hashlib(MD5)和base64编码概念
4. PCM转WAV文件(添加WAV头)
5. 使用pyaudio库播放合成语音
6. 保存音频文件
"""
import requests
import json
import hashlib
import base64
import time
import pyaudio
import wave
import struct
from datetime import datetime
# ====================== 配置 ======================
AMAP_KEY = "2a151bfbe125ff1eb35167690125a672" # 高德地图API Key
CITY_CODE = "653100" # 喀什地区编码
SAVE_PCM_PATH = r"F:\物联网媒体技术\保存录音文件\喀什天气播报.pcm"#文件保存路径填写实时路径
SAVE_WAV_PATH = r"F:\物联网媒体技术\保存录音文件\喀什天气播报.wav"#文件保存路径填写实时路径
BAIDU_API_KEY = "5djvuCHLL5Sa3Rb1RwsYap7b" # 百度API Key
BAIDU_SECRET_KEY = "aTEYYHQVGJdlSMc8BsVenYHleGBTw8Nh" # 百度Secret Key
# 百度语音合成参数
TTS_URL = "https://tsn.baidu.com/text2audio"
# ==================================================
def get_weather():
"""
步骤1:使用高德地图API获取实时天气数据
原理:发送HTTP GET请求,参数包含城市编码和API Key,返回JSON格式天气信息
知识点:requests库、JSON解析、HTTP GET请求
"""
weather_url = "https://restapi.amap.com/v3/weather/weatherInfo"
params = {
"city": CITY_CODE,
"key": AMAP_KEY,
"extensions": "base" # base表示实时天气,all表示天气预报
}
resp = requests.get(weather_url, params=params)
data = resp.json()
if data.get("status") != "1":
raise Exception(f"天气API错误: {data}")
live = data["lives"][0]
# 提取所需信息:城市、天气、温度、风向、风力、湿度(高德返回的湿度字段为humidity)
city = live["city"]
weather = live["weather"]
temperature = live["temperature"]
wind_direction = live["winddirection"]
wind_power = live["windpower"]
humidity = live.get("humidity", "未知") # 有些版本可能没有湿度
# 构造播报文本
text = (f"{city}当前天气{weather},温度{temperature}摄氏度,"
f"{wind_direction}风{wind_power}级,空气湿度{humidity}%。")
print("✅ 天气数据获取成功")
print(f"📅 当前时间戳: {int(time.time())} (秒) | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"📢 播报内容:{text}")
return text
def get_baidu_access_token():
"""
步骤2:获取百度API访问令牌(access_token)
原理:使用client_credentials授权模式,通过API Key和Secret Key换取token
知识点:OAuth 2.0 客户端凭证模式、POST请求
"""
token_url = "https://aip.baidubce.com/oauth/2.0/token"
params = {
"grant_type": "client_credentials",
"client_id": BAIDU_API_KEY,
"client_secret": BAIDU_SECRET_KEY
}
resp = requests.post(token_url, params=params) # 使用POST方法(符合实验要求)
token_data = resp.json()
if "access_token" not in token_data:
raise Exception(f"获取token失败: {token_data}")
print("✅ 已获取百度access_token")
# 演示base64编码:将access_token进行base64编码(知识点展示)
token_base64 = base64.b64encode(token_data["access_token"].encode()).decode()
print("🔐 演示base64编码(access_token编码后前50字符):", token_base64[:50], "...")
return token_data["access_token"]
def text_to_speech(text, access_token):
"""
步骤3:调用百度语音合成API,获取PCM音频数据(POST请求)
参数说明:
tex: 要合成的文本
lan: 语言,zh中文
ctp: 客户端类型,1为web端
tok: access_token
cuid: 用户标识(可自定义)
per: 发音人选择,4=度丫丫
spd: 语速,0-9,5为中速
vol: 音量,0-15,9为较大
pit: 音调,0-9,5为正常
format: 音频格式,pcm(便于后续演示PCM转WAV)
其他参数参考百度文档
知识点:HTTP POST请求、application/x-www-form-urlencoded编码、音频二进制数据处理
"""
# 演示hashlib MD5:计算文本的MD5值(知识点展示)
text_md5 = hashlib.md5(text.encode('utf-8')).hexdigest()
print("🔐 演示MD5编码(播报文本的MD5值):", text_md5)
# 请求参数(使用POST方式,需放在data中)
data = {
"tex": text,
"lan": "zh",
"ctp": "1",
"tok": access_token,
"cuid": "PythonClient_Weather",
"per": "4", # 度丫丫(可爱女声)
"spd": "5",
"vol": "9",
"pit": "5",
"format": "pcm" # 返回原始PCM数据,便于演示格式转换
}
# 发送POST请求,Content-Type默认application/x-www-form-urlencoded
resp = requests.post(TTS_URL, data=data)
# 检查返回内容:如果content-type包含json说明有错误
if resp.headers.get('Content-Type', '').startswith('application/json'):
error_info = resp.json()
raise Exception(f"语音合成API错误: {error_info}")
# 正常情况返回二进制音频数据
audio_data = resp.content
print(f"✅ 语音合成成功,获取到 {len(audio_data)} 字节的PCM数据")
return audio_data
def pcm_to_wav(pcm_data, wav_path, sample_rate=16000, channels=1, sample_width=2):
"""
步骤4:将PCM原始数据转换为WAV文件(添加WAV头)
原理:WAV文件是RIFF格式,包含文件头(44字节)描述采样率、位深度、声道数等信息。
PCM是纯音频数据,需加上头才能被常见播放器识别。
知识点:WAV文件结构、struct.pack打包二进制头、字节操作
参数:
pcm_data: bytes, PCM原始音频数据
wav_path: 输出WAV文件路径
sample_rate: 采样率(百度默认16000)
channels: 声道数(百度默认1)
sample_width: 采样位宽(字节数,2表示16位)
"""
num_samples = len(pcm_data) // sample_width
byte_rate = sample_rate * channels * sample_width
block_align = channels * sample_width
with open(wav_path, 'wb') as wav_file:
# RIFF头
wav_file.write(b'RIFF')
wav_file.write(struct.pack('<I', 36 + len(pcm_data))) # 文件总长度-8
wav_file.write(b'WAVE')
# fmt子块
wav_file.write(b'fmt ')
wav_file.write(struct.pack('<I', 16)) # fmt块长度(16字节)
wav_file.write(struct.pack('<H', 1)) # 音频格式(1=PCM)
wav_file.write(struct.pack('<H', channels))
wav_file.write(struct.pack('<I', sample_rate))
wav_file.write(struct.pack('<I', byte_rate))
wav_file.write(struct.pack('<H', block_align))
wav_file.write(struct.pack('<H', sample_width * 8))
# data子块
wav_file.write(b'data')
wav_file.write(struct.pack('<I', len(pcm_data)))
wav_file.write(pcm_data)
print(f"✅ PCM已转换为WAV文件:{wav_path}")
def play_wav(wav_path):
"""
步骤5:使用pyaudio库播放WAV文件
原理:pyaudio基于PortAudio,提供跨平台的音频I/O。先读取WAV文件头获取参数,
然后打开音频流,分块写入数据。
知识点:pyaudio.open、流操作、回调函数或阻塞式写入
"""
# 打开WAV文件获取参数
wf = wave.open(wav_path, 'rb')
# 初始化pyaudio
p = pyaudio.PyAudio()
# 打开音频流
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
# 读取数据并播放
chunk_size = 1024
data = wf.readframes(chunk_size)
while data:
stream.write(data)
data = wf.readframes(chunk_size)
# 停止并关闭流
stream.stop_stream()
stream.close()
p.terminate()
wf.close()
print("✅ 语音播放完成")
def main():
"""
主函数:按实验步骤依次执行
"""
print("=" * 50)
print("开始实时天气播报程序")
print("=" * 50)
try:
# 第一步:获取天气数据并构造播报文本
text = get_weather()
# 第二步:获取百度access_token
token = get_baidu_access_token()
# 第三步:语音合成获取PCM音频数据
pcm_audio = text_to_speech(text, token)
# 第四步:保存PCM文件(可选,便于调试)
with open(SAVE_PCM_PATH, 'wb') as f:
f.write(pcm_audio)
print(f"💾 PCM文件已保存:{SAVE_PCM_PATH}")
# 第五步:将PCM转换为WAV格式(满足操作要点“PCM文件和wav文件的转换”)
pcm_to_wav(pcm_audio, SAVE_WAV_PATH)
# 第六步:播放WAV音频文件(实现天气播报)
print("🔊 正在播放天气播报...")
play_wav(SAVE_WAV_PATH)
print("🎉 所有步骤执行成功!")
except Exception as e:
print(f"❌ 程序出错:{e}")
if __name__ == "__main__":
main()
更多推荐

所有评论(0)