【Python】DeepLを用いた自動翻訳をdiscord botに導入してみた

#目標
PDFなどをコピペした英文を投げたら自動で改行調整をし、日本語訳を返すコードをPythonで書いたのでそれをdiscord botに導入してみる。

:::note warn
注意
seleniumが並列化できていないため、飽くまで個人用です。
(複数人で共有するとタブクラッシュするかも)
:::

#メリット
discord botに翻訳を返してもらえるメリットを挙げると
-記録が残せる
-Shaperを使うのと違ってタブが増殖しない
#翻訳コード
とりあえずコードを載せる。

```Python:translate.py
import time
from selenium import webdriver
import chromedriver_binary
from xml.sax.saxutils import unescape
def deepl(text):
    text = ' '.join(text.splitlines())

    options = webdriver.ChromeOptions()
    options.add_argument('--headless')
    url="https://www.deepl.com/ja/translator"
    driver =webdriver.Chrome(options=options)
    driver.get(url)

    insec="#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea"
    driver.find_element_by_css_selector(insec).send_keys(text)

    outsec="#target-dummydiv"
    while 1:
        Outputtext =  driver.find_element_by_css_selector(outsec).get_attribute("innerHTML")
        if Outputtext != "\r\n" :
            break
        time.sleep(1)
    driver.quit
    Outputtext=Outputtext.rstrip("\r\n")
    Outputtext=unescape(Outputtext)
    return Outputtext
```

#英文の改行コードを空白にする
PDFをコピペすると勝手に改行されてイライラするのでとりあえず改行を消して半角空白にする。

```Python
text = ' '.join(text.splitlines())
```

#seleniumを使ってDeeplにアクセス

```Python
options = webdriver.ChromeOptions() #事前にimportしたchromeのwebdriverを使う
options.add_argument('--headless') #実行の際Chromeを開かないよう指定
url="https://www.deepl.com/ja/translator" #DeepL(ja)のURL
driver =webdriver.Chrome(options=options) #driverを指定
driver.get(url) #DeepLにアクセス
```
webdriver.ChromeOptionsについては@hanzawak様の[この](https://qiita.com/hanzawak/items/2ab4d2a333d6be6ac760)記事を参照するといいかも。

#DeepLに英文を入れる
##手法1
https://www.deepl.com/translator#en/ja/英文
にアクセスすることで翻訳を行う。
###問題点
例えば文章中にX/Yなどがあるとする。そしたらURLにアクセスする際これをスラッシュと認識して英文が/で途切れてしまう。(%2Fでも同じだった)

##手法2
DeepLのサイト内で入力を行う。
まずDeepL内で文入力のsectorを探した。すると


```
#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea
```
にあることが分かった。
ここをfindしてtextを入れる。

```Python
insec="#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea"
driver.find_element_by_css_selector(insec).send_keys(text)
```

#訳文を抽出する
~~上と同様にtextareaを見つけて抽出しようとしたができなかった。
そのため別のところを探すと
 #target-dummydiv のinnerHTMLにあることが分かった。~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1816947/69c3de98-37ef-c0c1-94e9-fc59822451c6.png)
※訂正

```
"#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea"
```
の"value"にありました

あとはここをfindして~~innerHTML~~ valueを取り出す。
ここで翻訳が終わっていないと"\r\n"が取り出されるため、それ以外のものが取れるまで1秒ごとに操作を行う。

```Python
outsec="#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea"
while 1:
    Outputtext =  driver.find_element_by_css_selector(outsec).get_attribute("value")
    if Outputtext != "" :
        break
    time.sleep(1)
driver.quit
```

最後に末尾の"\r\n"を取って訳文Outputtextを獲得...と行きたいところだが、ここでHTML上では特殊文字について、例えば">"は"&gt ;"で書かれているからそれを普通の記号に変換するためにunescapeに通す。

```Python
Outputtext=unescape(Outputtext)
return Outputtext
```

これで翻訳については完了。
あとはこれをBOTに入れるだけである。

#BOT作成
BOTについては基本的な動作を行うだけなため@1ntegrale9様の[この](https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f)記事を参考にするといいかも。
またseleniumとchromedriver_binaryをHerokuで使う際、Setting->Buildpacksに以下の二つのURLを入れることに注意。
https://github.com/heroku/heroku-buildpack-google-chrome.git
https://github.com/heroku/heroku-buildpack-chromedriver.git

```Python:deeplbot.py
import translate.py
import discord
import asyncio
TOKEN = '自分のtokenを入れてね'
client = discord.Client()
@client.event
async def on_message(message):
    author=message.author
    dm=await author.create_dm()
    if message.channel==dm:
        honyaku = translate.deepl(message.content)
        await message.channel.send(honyaku)
client.run(TOKEN)
```

関数内を簡単に説明すると上から
メッセージ(英文)が送ると
まず送ってきた人をauthorとする。
そしてその人とのDMをdmとする。
そしてその人の送ってきた文章がDMからなら実行し、それ以外なら実行しない(大量のグループ内で起動すると迷惑になりそうなので)
あとはtranslate.py内の翻訳関数に通して出力をDMに再び返す。

といった感じである。

```Python
async def on_message(message):
    author=message.author #メッセージの送り主を特定
    dm=await author.create_dm() #送り主のDMを特定
    if message.channel==dm: #送ってきたチャンネルがDMか判断
        if __name__ == '__main__': 
            honyaku = deepl2.deepl(message.content) #翻訳関数実行
            await message.channel.send(honyaku) #送り主のDMに返す
```

#実行してみた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1816947/491e533c-d63b-693e-473e-94ea36f32fc8.png)
15秒ぐらいで返ってきた。（まぁ妥協点?）