【Python】英語勉強用アプリに使用する、各単語の音声ファイルを自動ダウンロードするアプリを作成してみた

# あらすじ

下記記事にて作成した英語勉強用アプリでは、英単語の音声ファイルが必要になる。
https://qiita.com/Fuminori_Souma/private/0706716fdebf08572c6c

その音声ファイルを手動でダウンロードするのは時間と手間がかかって大変なので、
webスクレイピングで自動的にダウンロードすることにした。

音声ファイルはweblio様のもの有難くをダウンロードいただきます。

※ weblio様にご迷惑をかけないよう、速度は遅め（恐らく手動以下）に設定しています。


# ソースファイル

```python:get_sound_file.py
import sys
import tkinter
import time
import re
import urllib.request
from tkinter import messagebox
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

class Frame(tkinter.Frame):

    def __init__(self, master=None):
        tkinter.Frame.__init__(self, master)
        self.master.title('音声ファイルをgetくん')
        self.master.geometry("400x300")

        # ラベルの設定
        text_1 = tkinter.Label(self, text=u'音声ファイルを取得したい単語を、下のテキストボックスに入力して下さい。')
        text_1.pack(pady='7')
        text_2 = tkinter.Label(self, text=u'※ 複数の単語を入力する場合は「,」で区切って下さい。')
        text_2.pack()

        # テキスト（エントリーの複数ver.）の設定
        self.ent_words = tkinter.Text(self, height=15)
        self.ent_words.pack(padx='30')

        # プッシュボタンの設定
        bttn_start = tkinter.Button(self, text = u'開始', command=self.start_get_file)
        bttn_start.bind("<Button-1>") #（Button-2でホイールクリック、3で右クリック）
        bttn_start.pack(pady='7')

    def checkAlnum(self, word):  # 入力された単語に不要な記号等が含まれていないかチェック
        alnum = re.compile(r'^[a-zA-Z]+$')  # 正規表現をコンパイル
        result = alnum.match(word) is not None  # matchで条件に合えばSRE_Match objectを、そうでなければNone(False)を返す
        return result

    def delete_symbols(self, word):  # 文字列に含まれた記号等を削除
        # return word.replace(',', '').replace('.', '').replace('-', '').replace(' ', '')
        return word.replace(',', '').replace(' ', '')

    def get_mp3(self, word, driver):  # weblioのページを開いてmp3ファイルを取得

        dir = 'C:/Users/fumin/OneDrive'  # 音声ファイルのダウンロード先

        # 単語検索用のテキストボックスに単語を入力して検索ボタンを押下
        driver.find_element_by_xpath("//*[@id=\"searchWord\"]").clear()  # テキストボックスを初期化
        driver.find_element_by_xpath("//*[@id=\"searchWord\"]").send_keys(word)
        driver.find_element_by_xpath("//*[@id=\"headFixBxTR\"]/input").click()
        time.sleep(5)

        # 音声ファイルが存在する（=「プレーヤー再生」が存在する）場合
        if not driver.find_elements_by_xpath("//*[@id=\"audioDownloadPlayUrl\"]/i") == []:

            # 「プレーヤー再生」を押してmp3ファイルを新しいウィンドウで開く
            driver.find_element_by_xpath("//*[@id=\"audioDownloadPlayUrl\"]/i").click()
            time.sleep(5)

            # 操作対象のウィンドウを、新しく開いたmp3ファイルに変更する
            handles = driver.window_handles
            driver.switch_to.window(handles[1])

            # mp3ファイルをダウンロードする
            urllib.request.urlretrieve(driver.current_url, (dir + '/' + word + '.mp3'))
            driver.close()

            # 操作対象のウィンドウを元のウィンドウに戻す
            driver.switch_to.window(handles[0])

            return 'OK'

        else:  # 音声ファイルが存在しない（=「プレーヤー再生」が存在しない）場合

            return 'NG'


    def start_get_file(self):

        reslist = {}  # 単語の音声ファイルが存在したか否か （空の辞書型で初期化）

        words = self.ent_words.get('1.0', 'end')  # テキストボックスに入力した単語リストを取得

        if self.checkAlnum(self.delete_symbols(words)):  # 正しく入力されている（英字及び「,」以外が入力されていない）場合

            ww = [x.strip() for x in words.split(',')]  # 入力の単語リストをカンマで区切ってlist型として格納

            # ブラウザを開く
            drv = webdriver.Chrome("C:/Users/fumin/pybraries/chromedriver_ver79/chromedriver")
            time.sleep(10)

            # 操作するページ（weblio）を開く
            drv.get("https://ejje.weblio.jp/")
            time.sleep(10)

            j = 0  # NG単語(mp3ファイルが存在しない単語)の数

            for i in range(len(ww)):  # mp3ファイルを取得
                reslist[ww[i]] = self.get_mp3(ww[i], drv)

                if reslist[ww[i]] == 'NG':  # mp3ファイルが存在しない単語をNGリストに追加

                    j += 1  # NG単語数を加算

                    if j <= 1:  # １つ目のNG単語は文字列型として格納
                        nglist = ww[i]

                    elif j == 2:  # 2つ目のNG単語は1つ目とカンマ区切りで繋げてlist型に変換
                        nglist = (nglist + ',' + ww[i]).split(',')

                    else:  # 3つ目以降はlist型に順次追加
                        nglist.append(ww[i])

            drv.close()  # 単語取得処理が終了したらブラウザを閉じる

            if 'nglist' in locals():  # 音声ファイルが存在しなかった単語がある場合

                if j == 1:  # NG単語が１つだけの場合
                    messagebox.showinfo('', '下記を除いた、全単語の音声ファイルをダウンロードしました。\n\n' + nglist)
                else:  # NG単語が2つ以上の場合
                    messagebox.showinfo('', '下記を除いた、全単語の音声ファイルをダウンロードしました。\n\n' + ', '.join(nglist))
            else:
                messagebox.showinfo('', '入力された全単語の音声ファイルをダウンロードしました。')

        else:   # 正しく入力されていない（英字及び「,」以外が入力されている）場合
            messagebox.showinfo('', '英字及び「,」以外が入力されています。削除した後に再度実行して下さい。')


if __name__ == '__main__':

    # フレームの設定
    root = Frame()
    root.pack()
    root.mainloop()
```



# 備考

weblioのサイト負担をかけるのはまずいためかなり速度を遅めにした。。そのため、
ダウンロードする速度は、手動と大して変わりません。
（速度ではなく、あくまで自動化したことに意義があると考えています）


# 課題

1. mp3ファイルを開いた際に、いちいち音声ファイルが再生されてしまう。。そのため、
再生される時だけmp3ファイルの音声を調節して音声が再生されないようにした。。が、mp3ファイルの
音量バーを調節できなかった。PC自体の音量自体を一瞬０にすることも考えたが、音楽を聴きながら
ダウンロードしていた場合、その音楽も途切れてしまう！と考え、止む無く断念した。

2. mp3ファイルのダウンロード方法について、最初は右クリック->名前を付けてオーディオを保存 を
選択しようと思っていたが、右クリックによって出てきたコンテキストメニューは、Seleniumでは
アクセス不可らしい。。そのため、urllibを使用してmp3ファイルをダウンロードした。
結果的にmp3ファイルをダウンロードできたのはいいが、今後右クリックは必要になったときは
困るなあ。。


# その他の参考にさせていただいた情報

大変お世話になりました。誠にありがとうございます。

|内容|リンク先|
|---|---|
|ファイルダウンロードの方法|https://stackoverflow.com/questions/48736437/how-to-download-this-video-using-selenium|
|要素の存在確認|https://ja.stackoverflow.com/questions/30895/xpath%E3%81%A7%E8%A6%81%E7%B4%A0%E3%81%AE%E5%AD%98%E5%9C%A8%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95|
|Seleniumの右クリックについて|https://stackoverflow.com/questions/20316864/how-to-perform-right-click-using-selenium-chromedriver|

# 最後に

ここ間違ってるぞ！ここだめだぞ！ここはこうした方がいいぞ！等ございましたら、
是非ご指摘いただけますと涙を流して喜びます。