微软 Edge 浏览器的“大声朗读”功能因其丰富的音色、对上百种语言的支持,以及自然的合成效果,在社区中被广泛“白嫖”使用。
作为免费的语音合成服务,其几乎没有限制,且许多开发者也基于它开发了工具,其中 edge-tts 就是一个依赖此功能的优秀开源库。
最近,Edge 浏览器的“大声朗读”功能加入了两个新的限流参数:Sec-MS-GEC 和 Sec-MS-GEC-Version。如果这些参数缺失,在中国地区,用户将收到 403 错误,国外不受影响。这些参数的有效期也非常短,仅为 3-5 分钟,因此无法长期使用。这一变更影响了 edge-tts 语音合成库的正常功能,除非用户使用代理工具。
当参数缺失时,用户会看到如下报错:
raise WSServerHandshakeError(
aiohttp.client_exceptions.WSServerHandshakeError: 403, message='Invalid response status', url=URL('wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4&ConnectionId=4b190acbbb4548049ac8faa8772ce020')
临时解决方案
经过社区的讨论,现已找到一种临时的解决方法。由于 Edge 官方库由国外开发者维护,这些临时方法尚未集成到官方代码中。因此,社区成员 fork 了 edge-tts 项目并加入了修复措施,现在只需更新或重新安装 edge-tts 即可,且无需额外调整外部依赖。
引发 edge-tts 无法使用的原因
有关该问题的更多讨论请参考:
在 Edge 浏览器的请求中,每次都需要新的 Sec-MS-GEC 参数,其有效期很短。缺少或过期的参数将导致返回 403 错误,而国外地区则不受此影响。
使用代理/VPN的解决方案
由于该限流措施仅对中国地区生效,使用代理/VPN 是一种可行的解决办法。
通过实时抓取 Edge 流量中的参数搭建 API
GitHub 用户提供了一种通过抓取 Edge 流量参数的办法,具体方法参见:GitHub Issue #299
基本原理
- 安装 mitmproxy:
pip install mitmproxy - 在 Edge 浏览器中设置本地代理地址为
127.0.0.1:8080 - 创建一个本地 HTML 文件,通过循环移动文本保持 Edge 的“大声朗读”功能持续运行
- 在 Edge 浏览器中手动开启大声朗读,选择音色并将语速降到最低
- 编写 Python 脚本捕获
wss请求的 URL,并提取Sec-MS-GEC参数
示例代码如下:
# record.py
from mitmproxy import ctx, http
import urllib.parse
import json
TARGET_PATH = "/consumer/speech/synthesize/readaloud/edge/v1"
def request(flow: http.HTTPFlow) -> None:
if TARGET_PATH in flow.request.path:
url = flow.request.pretty_url
params = urllib.parse.parse_qs(urllib.parse.urlparse(url).query)
data = {
"TrustedClientToken": params.get("TrustedClientToken", [""])[0],
"Sec-MS-GEC": params.get("Sec-MS-GEC", [""])[0],
"Sec-MS-GEC-Version": params.get("Sec-MS-GEC-Version", [""])[0]
}
with open("token.json", "w") as f:
f.write(json.dumps(data))
导入 mitmproxy 证书到系统根信任证书,然后启动监听:
mitmdump -s record.py
目前可用的第三方 API
社区开发者 learnin9 和 BG5T 各自开发了可供调用的 API,其中 BG5T 的 API 原理不明,但估计类似于上述抓取方法。
修改 edge-tts 官方代码实现自动获取参数
为了更方便地获取 Sec-MS-GEC 参数,修改 edge-tts 源代码。具体修改步骤如下:
- 在
communicate.py文件的Communicate.__init__中新增get_sec函数,通过 API 自动获取参数。
def get_sec():
import requests
try:
res = requests.get("https://edgeapi.pyvideotrans.com/token.json")
if res.status_code == 200:
data = res.json()
return data.get("Sec-MS-GEC", ""), data.get("Sec-MS-GEC-Version", "")
except:
pass
try:
res = requests.get("http://123.207.46.66:8086/api/getGec")
if res.status_code == 200:
data = res.json()
return data.get("Sec-MS-GEC", ""), data.get("Sec-MS-GEC-Version", "")
except:
pass
return "", ""
- 安装或更新到修改版 edge-tts:
目前这些代码已提交到fork库 github.com/jianchang51… 可直接安装使用,希望未来官方edge-tts库能引入这2个参数的设置。至少目前官方edge-tts并无这个打算,毕竟该问题只针对国区出现。
pip install aiohttp certifi requests
pip install --upgrade git+https://github.com/jianchang512/edge-tts#egg=edge-tts
通过这些改动,可实现 edge-tts 的自动化运行,避免频繁过期的问题。