天天看點

Pi利用爬蟲打造專屬語音鬧鐘

  • 我有一個小巧的OrangePi Zero,256M的,一直不知道拿來幹些什麼,所幸找到了這篇文章,我覺得挺好的,折騰正一個可以報時于天氣預報的鬧鐘,是以這篇文章被定義為分享.本文的所有權益歸 woodenrobot 所有.

所需材料

  • Pi(OrangePi/RaspberryPi)
  • USB聲霸卡(拓展闆)
  • 小音響

折騰開始

  • 這裡選擇墨迹天氣擷取實時天氣資訊,位址: 墨迹天氣 .
Pi利用爬蟲打造專屬語音鬧鐘

httptianqi.moji.com.png

  • 進入墨迹天氣的頁面,墨迹天氣會根據你的ip加載相應地區的天氣。 這次我們主要抓取溫度、天氣、濕度、風力、空氣品質和天氣提示這幾個資料。

    這種小爬蟲我們就不用Scrap那種重型武器啦,使用requests和BeautifulSoup這兩個超級好用的庫可以快速實作(Ps:這兩個庫是Python的第三方庫,需要自己安裝。pip install requests、pip install BeautifulSoup4分别使用這兩條指令安裝)。

import re
import requests
from datetime import datetime
from bs4 import BeautifulSoup


headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit'
                      '/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safar'
                      'i/537.36',
    }

res2 = requests.get('http://tianqi.moji.com/', headers=headers)

soup = BeautifulSoup(res2.text, "html.parser")
temp = soup.find('div', attrs={'class': 'wea_weather clearfix'}).em.getText()
weather = soup.find('div', attrs={'class': 'wea_weather clearfix'}).b.getText()
sd = soup.find('div', attrs={'class': 'wea_about clearfix'}).span.getText()
sd_num = re.search(r'\d+', sd).group()
sd = sd.replace(sd_num, sd_num_zh)
wind = soup.find('div', attrs={'class': 'wea_about clearfix'}).em.getText()
aqi = soup.find('div', attrs={'class': 'wea_alert clearfix'}).em.getText()
aqi_num = re.search(r'\d+', aqi).group()
aqi = aqi.replace(aqi_num, aqi_num_zh)
info = soup.find('div', attrs={'class': 'wea_tips clearfix'}).em.getText()
sd = sd.replace(' ', '百分之').replace('%', '')
aqi = 'aqi' + aqi

today = datetime.now().date().strftime('%Y年%m月%d日')
text = '早上好!今天是%s,天氣%s,溫度%s攝氏度,%s,%s,%s,%s' % \
       (today, weather, temp, sd, wind, aqi, info)
           

語音轉換

  • 剛開始想通過python的庫實作本地文字轉語音,在windows系統下沒有問題,但樹莓派3上中文無法轉換。後來就找到了百度的文字轉換語音API,位址: 百度語音-永久免費智能語音開放平台 還可以選各種聲音,調節語速。雖然它沒有給出直接的api接口,但是我們利用Chrome浏覽器的開發者模式可以找到api。 打開開發者模式,點選播放的按鈕,在network裡就可以找到剛剛發出的請。
Pi利用爬蟲打造專屬語音鬧鐘

百度語音.png

  • 試聽 就我們要找的百度文字轉語音API,其中per是參數是語音的類型,spd是語速,vol是音量,而tex則是需要轉換的文字。通過以下代碼就可以實作将特定的文字轉換為語音,并以mp3格式儲存到本地。
import requests


headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit'
                      '/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safar'
                      'i/537.36',
    }

url = 'http://tts.baidu.com/text2audio?idx=1&tex={0}&cuid=baidu_speech_' \
      'demo&cod=2&lan=zh&ctp=1&pdt=1&spd=4&per=4&vol=5&pit=5'.format(text)
res = requests.get(url, headers=headers)
with open('1.mp3', 'wb') as f:
    f.write(res.content)
           

實作定時播放語音

# 使用mplayer實作語音播放,通過以下指令安裝mplayer:
sudo apt-get install mplayer
# 用法很簡單輸入以下指令即可播放音樂:
mplayer \xxx\xxx\xxx.mp3(絕對位址)
           
  • 本來是用crontab來實作定時播放的,但是後來發現了一個bug。程式運作的流程是實時下載下傳語音MP3檔案到本地,然後用os.system()直接調用mplayer播放語音。程式本地運作時正常,用crontab定時運作就找不到實時下載下傳的語音檔案。沒有辦法就自己寫了一個每天定時播放的腳本。
import time
from datetime import datetime

def get_seconds(h='07', m='30', s='00'):
    """擷取目前時間與程式啟動時間間隔秒數"""

    # 設定程式啟動的時分秒
    time_pre = '%s:%s:%s' % (h, m, s)
    # 擷取目前時間
    time1 = datetime.now()
    # 擷取程式今天啟動的時間的字元串格式
    time2 = time1.date().strftime('%Y-%m-%d') + ' ' + time_pre
    # 轉換為datetime格式
    time2 = datetime.strptime(time2, '%Y-%m-%d %H:%M:%S')
    # 判斷目前時間是否晚于程式今天啟動時間,若晚于則程式啟動時間增加一天
    if time1 > time2:
        time2 = time2 + timedelta(days=1)

    return time.mktime(time2.timetuple()) - time.mktime(time1.timetuple())
           

該函數預設計算目前事件距上午七點半間隔秒數,需要修改天氣播報事件就自己修改三個預設參數,h是小時,m是分鐘,s是秒。

結尾

  • 中間還遇到了一些小bug,比如說語音轉文字的過程中數字隻能一個一個的念出來,做為一個完美主義者肯定不能忍受這個,12攝氏度給我播報成一二攝氏度怎麼行!!!是以就寫了一個小函數專門轉換數字為中文。最後所有的代碼整合起來就是這樣啦。直接在pi中啟動程式就會自動每天七點半播報語音天氣啦!!!你也可以設定成開機自啟這樣就不用每次重新開機後再去啟動程式了。怎麼設定開機自啟網上有很多教程,請大家自行百度。:)
  • 附上完整的代碼:
# -*- coding: utf-8 -*-
# @Time     : 2017/1/15 15:16
# @Author   : woodenrobot


import os
import re
import time
import requests
from datetime import datetime, timedelta
from bs4 import BeautifulSoup


headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit'
                      '/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safar'
                      'i/537.36',
    }


def numtozh(num):
    num_dict = {1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '七',
                8: '八', 9: '九', 0: '零'}
    num = int(num)
    if 100 <= num < 1000:
        b_num = num // 100
        s_num = (num-b_num*100) // 10
        g_num = (num-b_num*100) % 10
        if g_num == 0 and s_num == 0:
            num = '%s百' % (num_dict[b_num])
        elif s_num == 0:
            num = '%s百%s%s' % (num_dict[b_num], num_dict.get(s_num, ''), num_dict.get(g_num, ''))
        elif g_num == 0:
            num = '%s百%s十' % (num_dict[b_num], num_dict.get(s_num, ''))
        else:
            num = '%s百%s十%s' % (num_dict[b_num], num_dict.get(s_num, ''), num_dict.get(g_num, ''))
    elif 10 <= num < 100:
        s_num = num // 10
        g_num = (num-s_num*10) % 10
        if g_num == 0:
            g_num = ''
        num = '%s十%s' % (num_dict[s_num], num_dict.get(g_num, ''))
    elif 0 <= num < 10:
        g_num = num
        num = '%s' % (num_dict[g_num])
    elif -10 < num < 0:
        g_num = -num
        num = '零下%s' % (num_dict[g_num])
    elif -100 < num <= -10:
        num = -num
        s_num = num // 10
        g_num = (num-s_num*10) % 10
        if g_num == 0:
            g_num = ''
        num = '零下%s十%s' % (num_dict[s_num], num_dict.get(g_num, ''))
    return num


def get_seconds(h='07', m='30', s='00'):
    """擷取目前時間與程式啟動時間間隔秒數"""

    # 設定程式啟動的時分秒
    time_pre = '%s:%s:%s' % (h, m, s)
    # 擷取目前時間
    time1 = datetime.now()
    # 擷取程式今天啟動的時間的字元串格式
    time2 = time1.date().strftime('%Y-%m-%d') + ' ' + time_pre
    # 轉換為datetime格式
    time2 = datetime.strptime(time2, '%Y-%m-%d %H:%M:%S')
    # 判斷目前時間是否晚于程式今天啟動時間,若晚于則程式啟動時間增加一天
    if time1 > time2:
        time2 = time2 + timedelta(days=1)

    return time.mktime(time2.timetuple()) - time.mktime(time1.timetuple())


def get_weather():
    # 下載下傳墨迹天氣首頁源碼
    res = requests.get('http://tianqi.moji.com/', headers=headers)
    # 用BeautifulSoup擷取所需資訊
    soup = BeautifulSoup(res.text, "html.parser")
    temp = soup.find('div', attrs={'class': 'wea_weather clearfix'}).em.getText()
    temp = numtozh(int(temp))
    weather = soup.find('div', attrs={'class': 'wea_weather clearfix'}).b.getText()
    sd = soup.find('div', attrs={'class': 'wea_about clearfix'}).span.getText()
    sd_num = re.search(r'\d+', sd).group()
    sd_num_zh = numtozh(int(sd_num))
    sd = sd.replace(sd_num, sd_num_zh)
    wind = soup.find('div', attrs={'class': 'wea_about clearfix'}).em.getText()
    aqi = soup.find('div', attrs={'class': 'wea_alert clearfix'}).em.getText()
    aqi_num = re.search(r'\d+', aqi).group()
    aqi_num_zh = numtozh(int(aqi_num))
    aqi = aqi.replace(aqi_num, aqi_num_zh).replace(' ', ',空氣品質')
    info = soup.find('div', attrs={'class': 'wea_tips clearfix'}).em.getText()
    sd = sd.replace(' ', '百分之').replace('%', '')
    aqi = 'aqi' + aqi
    info = info.replace(',', ',')
    # 擷取今天的日期
    today = datetime.now().date().strftime('%Y年%m月%d日')
    # 将擷取的資訊拼接成一句話
    text = '早上好!今天是%s,天氣%s,溫度%s攝氏度,%s,%s,%s,%s' % \
           (today, weather, temp, sd, wind, aqi, info)
    return text


def text2voice(text):
    url = 'http://tts.baidu.com/text2audio?idx=1&tex={0}&cuid=baidu_speech_' \
          'demo&cod=2&lan=zh&ctp=1&pdt=1&spd=4&per=4&vol=5&pit=5'.format(text)
    # 下載下傳轉換後的mp3格式語音
    res = requests.get(url, headers=headers)
    # 将MP3存入本地
    with open('1.mp3', 'wb') as f:
        f.write(res.content)


def main():
    while True:
        s = get_seconds()
        time.sleep(s)
        # 擷取需要轉換語音的文字
        text = get_weather()
        print(text)
        # 将文字轉換為語音并存入程式所在檔案夾
        text2voice(text)
        # 擷取音樂檔案絕對位址
        mp3path2 = os.path.join(os.path.dirname(__file__), '2.mp3')
        # 先播放一首音樂做鬧鐘
        os.system('mplayer %s' % mp3path2)
        # 播報語音天氣
        mp3path1 = os.path.join(os.path.dirname(__file__), '1.mp3')
        os.system('mplayer %s' % mp3path1)
        os.remove(mp3path1)

if __name__ == '__main__':
    main()