📌 引言

想不想让你的电脑每天自动播报天气预报?用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)

  • wavestruct:Python内置,无需安装,用于WAV文件处理


🔑 第一步:获取API密钥

1. 高德开放平台

  • 访问 高德开放平台 注册并登录

  • 进入控制台 → 应用管理 → 创建新应用

  • 添加Key,服务平台选择 Web服务,即可获取一个API Key(示例代码中的 AMAP_KEY

点击设置:

把这些配置好往下滑点击提交即可,这样我们就创建好了

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

    • extensionsbase返回实时天气,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,再调用合成接口。这恰好是我们学习 POSThashlibbase64 的好机会。

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 库使用起来非常简单:

  1. 用 wave.open 读取WAV文件,获取参数(声道数、采样宽度、帧速率)

  2. 用 pyaudio.PyAudio() 实例化一个音频对象

  3. 打开一个输出流,设定格式与WAV一致

  4. 循环读出音频帧并写入流

  5. 关闭流并终止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()

 

 

更多推荐