手把手教你用 Python 来朗读网页

是不是有的时候懒得自己看新闻?那么不妨试试用 Python 来朗读给你听吧。

网页转换成语音,步骤无外乎:

  1. 网页正文识别,获取到正文的文本内容;
  2. 文本转语音,通过接口将文本转换成语音文件;
  3. 语音文件的发声,即将语音文件读出;

网页正文识别

我们之所以用 Python,就是因为 Python 有着丰富的库,网页正文识别也不在话下。这里我尝试了 readabilitygoose-extractorcx-extractor-python

readability

readability 支持 Python3,使用 pip install readability-lxml 安装即可。

readability 使用起来也很方便:

1
2
3
4
5
6
import requests
from readability import Document

response = requests.get('https://hoxis.github.io/run-ansible-without-specifying-the-inventory-but-the-host-directly.html')
doc = Document(response.text)
print(doc.title())

但是 readability 提取到的正文内容不是文本,里面仍包含 HTML 标签。

当然也可以结合其他组件再对 HTML 进行处理,如 html2text,我们这里就不再延伸,有兴趣的可以自行尝试。

goose3

Goose 本来是一个用 Java 编写的文章提取器,后来就有了 Python 实现版: goose3

使用起来也很方便,同时对中文支持也不错。使用 pip install goose3 即可安装。

1
2
3
4
5
6
7
8
9
10
>>> from goose3 import Goose
>>> from goose3.text import StopWordsChinese
>>> url = 'http://news.china.com/socialgd/10000169/20180616/32537640_all.html'
>>> g = Goose({'stopwords_class': StopWordsChinese})
>>> article = g.extract(url=url)
>>> print(article.cleaned_text[:150])
北京时间61523:00(圣彼得堡当地时间18:00),2018年世界杯B组一场比赛在圣彼得堡球场展开角逐,伊朗10险胜摩洛哥,伊朗前锋阿兹蒙半场结束前错过单刀机会,鲍哈杜兹第95分钟自摆乌
龙。这是伊朗20年来首度在世界杯决赛圈取胜。

本届世界杯,既相继出现替补便进球,贴补梅开二度以及东道主

可以看出网页正文提取效果还不错,基本满足我们的要求,可以使用!

注意:goose 还有另外一个 Python2 的版本:Python-Goose,使用方法和 goose3 基本一样。

文本转语音

文本转语音,百度阿里腾讯讯飞等都有提供 REST API 接口,阿里和腾讯的申请相对时间较长,阿里的貌似还要收费,百度和讯飞的在线申请后即可使用,没办法,好的东西得来总是要曲折一些。其中百度的没有调用量的限制(其实默认是 200000 次/天),讯飞有每天 500 次的限制。

这里我们使用百度的 REST API 接口中的语言合成接口,一方面原因是百度的调用次数没有限制,另一方面,我大致看了下讯飞的接口文档,接口限制还是比较多的。还有就是百度提供了 REST API 的 Python 封装,使用也更方便。

baidu-aip 的使用

百度提供了 Python SDK,使用 pip install baidu-aip 可以直接安装。接口的使用可以参考接口文档:http://ai.baidu.com/docs#/TTS-Online-Python-SDK/top

使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from aip import AipSpeech

"""
你的 APPID AK SK
均可在服务控制台中的应用列表中查看。
"""
APP_ID = '你的 App ID'
API_KEY = '你的 Api Key'
SECRET_KEY = '你的 Secret Key'

client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)


result = client.synthesis('你好,你在做什么', 'zh', 3, {
'vol': 5,
})

# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
if not isinstance(result, dict):
with open('auido.mp3', 'wb') as f:
f.write(result)

接口参数:

参数 类型 描述 是否必须
tex String 合成的文本,使用UTF-8编码,请注意文本长度必须小于1024字节
lang String 语言选择,填写zh
ctp String 客户端类型选择,web端填写1
cuid String 用户唯一标识,用来区分用户,填写机器 MAC 地址或 IMEI 码,长度为60以内
spd String 语速,取值0-9,默认为5中语速
pit String 音调,取值0-9,默认为5中语调
vol String 音量,取值0-15,默认为5中音量
per String 发音人选择,0为女声,1为男声,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女

接口对单次传入的文本进行了限制,合成文本长度必须小于1024字节,如果文本长度过长,就需要进行切割处理,采用多次请求的方式,分别转换成语音文件,最后再将多个语音文件合并成一个。

文本切割

可以使用如下代码将文本分割成多个长度为 500 的文本列表

1
2
# 将文本按 500 的长度分割成多个文本
text_list = [text[i:i+500] for i in range(0, len(text), 500)]

语言文件合并

我们使用 pydub 来处理生成的音频文件。使用 pip install pydub 即可安装。

另外还 Ubuntu 环境需要安装依赖 sudo apt-get install libav-tools,Windows 环境需要到 https://ffmpeg.zeranoe.com/builds/ 下载 FFmpeg,并将其配置到环境变量中。

若还有问题,可以参考官网配置:https://github.com/jiaaro/pydub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 合并音频文件
def merge_voice(file_list):
voice_dict = {}
song = None
for i,f in enumerate(file_list):
if i == 0:
song = AudioSegment.from_file(f,"mp3")
else:
# 拼接音频文件
song += AudioSegment.from_file(f,"mp3")
# 删除临时音频
os.unlink(f)

# 导出合并后的音频文件,格式为MP3格式
file_name = str(uuid.uuid1()) + ".mp3"
song.export(file_name, format="mp3")
return file_name

通过百度的接口,我们可以将文字转化成音频文件,下面的问题就是如何播放音频文件。

音频文件播放

网上获取到 Python 播放 wav 文件的方式由好几种,包括 pyaudio、pygame、winsound、playsound。不过测试下来,只有 playsound 成功。其他方式有兴趣的可以试下,有问题可以留言交流。

使用 pip install playsound 安装后即可使用。

使用也很简单:

1
2
>>> from playsound import playsound
>>> playsound('/path/to/a/sound/file/you/want/to/play.mp3')

说明:音频的播放需要在图形化页面下运行,因为命令行模式下,没有播放声音的出口。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# encoding:utf-8

import uuid
import re
import os
import argparse
from pydub import AudioSegment
from aip import AipSpeech
from playsound import playsound
from goose3 import Goose
from goose3.text import StopWordsChinese

""" 你的 百度 APPID AK SK """
APP_ID = '11407664'
API_KEY = 'GT69E8M6sgOcSnIGElrgXo1e'
SECRET_KEY = 'fOCr1mwnyGOEjZg93GoonaGqzqp0paIB'

# 命令行输入参数处理
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--url', type=str, help="input the target url")

# 获取参数
args = parser.parse_args()
URL = args.url

client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)

def text_to_voice(text):
file_name = str(uuid.uuid1()) + '.mp3'
result = client.synthesis(text, 'zh', 3, {
'vol': 5,
})

# 识别正确返回语音二进制 错误则返回 dict 参照下面错误码
if not isinstance(result, dict):
with open(file_name, 'wb+') as f:
f.write(result)
return file_name

def get_text(url):
g = Goose({'stopwords_class': StopWordsChinese})
article = g.extract(url=url)
return article.cleaned_text

# 合并音频文件
def merge_voice(file_list):
voice_dict = {}
song = None
for i,f in enumerate(file_list):
if i == 0:
song = AudioSegment.from_file(f,"mp3")
else:
# 拼接音频文件
song += AudioSegment.from_file(f,"mp3")
# 删除临时音频
os.unlink(f)

# 导出合并后的音频文件,格式为MP3格式
file_name = str(uuid.uuid1()) + ".mp3"
song.export(file_name, format="mp3")
return file_name

if __name__ == "__main__":
# url = "http://news.china.com/socialgd/10000169/20180616/32537640_all.html"
text = get_text(URL)

# 将文本按 500 的长度分割成多个文本
text_list = [text[i:i+500] for i in range(0, len(text), 500)]
file_list = []
for t in text_list:
file_list.append(text_to_voice(t))
# print(file_list)
final_voice = merge_voice(file_list)
print(final_voice)
# 播放音频
playsound(final_voice)

运行

1
python page2voice.py -u "https://so.gushiwen.org/shiwenv_c244fc77f6fb.aspx"

运行后,代码就会自动解析网页并进行朗读啦。

总结

至此,网页到音频的转换就结束了,当然程序没有这么完美,比如中英文混合的网页解析和转换的结果就不怎么理想,但是纯中文的新闻页面效果还是不错的。

源码已上传至 GitHub,欢迎取阅。


hoxis wechat
一个脱离了高级趣味的程序员,关注回复1024有惊喜~
赞赏一杯咖啡
  • 本文作者: hoxis | 微信公众号【不正经程序员】
  • 本文链接: https://hoxis.github.io/python-url-to-voice.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!
0%