88
88

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PythonとSeleniumでDeepLに英文流して自動翻訳させる

Last updated at Posted at 2020-08-05

改変履歴

'21/1/26 DeepLのテキストエリアのCSSセレクタが変更になったようなので、関数の方も修正。
`21/5/18 またまたCSSセレクタが変更になったようなので、修正

やりたいこと

とりあえず、Science Directから書誌情報+アブストをゲットした。次は、これをDeepLに流し込んで翻訳させていく、っていう処理をしたい。有料プランに契約すれば、ファイルを一気に翻訳させることが可能になるけど、まあ、ものは試しということでselenimuとchromedriverでやってみようということでチャレンジ。

準備

##とりあえずCSVファイルをpythonに読み込む。

import pandas as pd

df = pd.read_csv("DB.csv",header=None, delimiter=",", quoting=1)
print(df.at[0,1])  #   タイトル
print(df.at[0,9])  #   アブスト
print(df.at[0,10]) #  キーワード

for title in df[1]:
    print(title)

まあ、これはすんなり。

SeleniumとChromDriverでDeepLにアクセス

from selenium import webdriver  

load_url = "https://www.deepl.com/ja/translator"
driver = webdriver.Chrome(executable_path='c:/work/chromedriver.exe')  #  driver = webdriver.Chrome()
driver.get(load_url)

ここもまあすんなりいく。

英文をDeepLに送る

テキストを送り込んで、翻訳をゲットする、ということで、英文を入力するtextareaと翻訳を出力してくるtextareaのcss selectorをChromeのdevelopper画面から探し出す。
で、入力についてはすんなり、

#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div > textarea

だということがわかった。
ということで、

title = df.at[0,1]
input_selector = "#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div > textarea"
driver.find_element_by_css_selector(input_selector).send_keys(title)

すなおに、英語はDeepLに送りこめた。

2021/5/18
久しぶりに確認してみたら、DeepLの仕様変更したっぽくて、CSS Selectorが変わってた。
ということで、以下が2021年5月18日時点のiput部のSelector

input = "#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"

日本語訳の取得 その1

一方で、出力のほうが嵌まった。
とりあえず、一回適当な英文で翻訳を作らせて出力させて、その出力テキストを使ってDevelopper画面で検索して探してみたら、なんとtextareaにそのまま入力されているのではなく、buttonタグが付けられて表示されている。
へー、と思いながら、button要素を取得して、テキストを取ろうとしたのだが、なぜか取れない。
VSCodeのデバッガで変数の状態を見ても、要素がもつtextは空文字になっている。え?なんで??
Chrome上では見えてるし、Developper画面ではちゃんとbuttonタグにはさまれて訳が表示されているのに、なんで空文字なん??

とりあえず、対処として、DeepLにはクリップボードに訳をコピーしてくれる便利なボタンがあるので、それをSeleniumでクリックしてクリップボードにコピーさせ、クリップボードから訳を取得することにする。
ってことで、クリップボードを扱うためのパッケージpyperclipをインストールして、クリップボードにコピーしてくれるボタンのCSS Selectorを見つけ出して、以下のようなスクリプトをかいて実行。

import pyperclip

title = df.at[0,1]
input_selector = "#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div > textarea"
driver.find_element_by_css_selector(input_selector).send_keys(title)

time.sleep(5)

OutputCopyBtn = "#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container.lmt__textarea_container_no_shadow > div.lmt__target_toolbar.lmt__target_toolbar--visible > div.lmt__target_toolbar__copy > button"
#"#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__target_toolbar.lmt__target_toolbar--visible > div.lmt__target_toolbar__copy > button"
driver.find_element_by_css_selector(OutputCopyBtn).click()

print(pyperclip.paste())

ふむ、これで取るのは取れた。

日本語訳の取得 その2

なんか負けた感があるので、button要素から直接テキストを取れないかリトライ。
ググってると、行きついたのが@riikunn1004さんの記事
なるほど。.getAttribute("textContent")というのでとれるのか。
画面上でも見えてるし、Developper画面でも見えてるのに取れない理由はよくわからないけど、これを参考に、以下のようなスクリプトにしてみた。

#2021,5,18更新
Output_selector = "#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container.lmt__textarea_container_no_shadow > div.lmt__translations_as_text > p.lmt__translations_as_text__item.lmt__translations_as_text__main_translation > button.lmt__translations_as_text__text_btn"
# "#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__translations_as_text > p > button.lmt__translations_as_text__text_btn"
Outputtext = driver.find_element_by_css_selector(Output_selector).get_attribute("textContent")
print(Outputtext)

これで取れることを確認。ふう。

おまけ

もうすこしDeepLのページの造りをしらべてみた。
入力のテキストエリアと同様に出力のテキストエリアも

#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea

で指定されている。ただ、CSSみてみると:disabledにがついていて操作が無効化されている。
実際に、英文を入力するまえは出力エリアは選択できない。
で、英語を入力すると、ここのエリアのクラス名が変わり、disableが消える。(css selectorは変わらない。)

ただ、このエリアに訳が入っているのか?というと入っていない。
画面では訳が入ってるように見えるが、現時点では空のまま。

このエリアをマウスでクリックして、テキストエリアにフォーカスをあてると、以下のような要素がポンっと表れて訳が表示される。(ちなみに英語としてtestを入力してる。なので訳はしけん)

<div class="lmt__textarea_base_style" 
  style="
    position: absolute; transform: translate(-500%, -500%); 
    padding: 16px 32px 80px 24px; margin: 0px; overflow: hidden; font-family: &quot;
    Open Sans&quot;, sans-serif; font-size: 24px; font-stretch: 100%; font-weight: 400;
    line-height: 36px; height: 468.5px; width: 448.5px;">
  <span style="outline: green solid 1px;">しけん</span>
  <span style="outline: red solid 1px; display: inline-block; position: relative; height: 1em;"></span>
  <span style="outline: blue solid 1px;"></span>
</div>

こいつのCSS Selectorは

#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__inner_textarea_container > div

んー、何となくわかってきた。
つまり、DeepLさんのページは、英文が入力されると、画面上に訳は表示されるけど、テキストエリアに表示させているわけではない。

実は下の方に、こういう形で

<button class="lmt__translations_as_text__text_btn">しけん</button>

で隠し持っている。
で、画面上で出力の「テキストエリア」っぽく見えるところにカーソルを持ってきてクリックを押すと上のようなdiv要素が展開し、訳が表示される、というようになっている。この切り替えをするために、button要素にしてる、ということかな?

随分ややこしい造りにしてる。なんでこんな造りにしてるんやろ??
ただ、なるほど。こういう造りであれば、「テキストエリア」っぽく見えるところを一回クリックしない限り、テキストエリアの回りを探しても訳にはたどり着けない。
また、buttonも基本的にはdisableかinvisibleかとにかく見えないようにしているんだろうから、.textでは取れない。
さらには、フォーカスをあてたら現れる要素のCSS Selectorを予め取得して、訳を作らせた後にアクセスしようとしても、一回テキストエリアにフォーカスをあてない限りは、「そんな要素はない」と弾かれる。ただ1回フォーカスをあててみたら、要素が見つかるので取れるんだろと思う。

ほんと、なんでこんなややこしいことしてるんだ??

最終的なソースコード 

とりあえず、DeepLに流して英文を自動翻訳させる部分は以下のような形。
途中のwhile文は、要するに英文の翻訳に時間がかかるから、その待ち時間のため。
1秒おきに翻訳できたかどうかをチェックしに行き、翻訳が完成していたらブレークする。

import pandas as pd
import time
from selenium import webdriver  #  
import chromedriver_binary

df = pd.read_csv("Reliablity EngineeringDB.csv",header=None, delimiter=",", quoting=1)

df.columns=["Authors", "Title", "jTitle", "VolIssue","Year", "Pages", "ISSN","DOI","URL","Abst","Keywords"]
print(df)

Title.df.at[0,1]

load_url = "https://www.deepl.com/ja/translator"
driver = webdriver.Chrome()  #  driver = webdriver.Chrome("c:/work/chromedriver.exe")
driver.get(load_url)

input_selector = "#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div > textarea"
driver.find_element_by_css_selector(input_selector).send_keys(Title)
while 1:
    Output_selector = "#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__translations_as_text > p > button.lmt__translations_as_text__text_btn"
    Outputtext = driver.find_element_by_css_selector(Output_selector).get_attribute("textContent")
    if Outputtext != "" :
        break
    time.sleep(1)
print(Outputtext)

関数化

上記で最終にするつもりだったけど、実際にやりたいことをやるにあたり関数にしり、待機処理の部分をいじったりしたので、そちらも掲載する。

'''
DeepLを使った翻訳を行う関数
入力 翻訳したい英語
出力 翻訳された日本語
例外 入力が文字列でない場合
'''
import time
from selenium import webdriver  
import chromedriver_binary

def TranslationByDeepL( mytext ):
    if mytext =="":
        return ""
    if type(mytext) is not str:
        raise   Exception("文字列ではありません")

    #DeeLのページのURLとCSS Selector
    load_url = "https://www.deepl.com/ja/translator"

    #'21/1/26 Selectorが変更になったっぽくて、要素にアクセスできなくなってたので確認して修正
    input_selector = "#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container.halfViewHeight > div > textarea"
                    #"#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div > textarea"
    Output_selector = "#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container.lmt__textarea_container_no_shadow > div.lmt__translations_as_text > p.lmt__translations_as_text__item.lmt__translations_as_text__main_translation > button.lmt__translations_as_text__text_btn"
                    # "#target-dummydiv"
                    #"#dl_translator > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--target > div.lmt__textarea_container > div.lmt__translations_as_text > p > button.lmt__translations_as_text__text_btn"
 

    '''
    WebDriverの処理がうまくいかなかったら1秒待機して再度WebDriverの処理を行う
    ただ、10回トライしてダメだったらエラーを返して関数処理終
    以下、WebDriver使うところでは同様の処理
    '''
    errCount=0
    f_succsess=False
    while not f_succsess:
        try: # DeepLにアクセス
            options = Options()
            options.add_argument('--headless')
            driver = webdriver.Chrome(options=options)  #  driver = webdriver.Chrome()
            driver.get(load_url)
            f_succsess = True
        except Exception  as identifier:
            errCount=errCount+1
            if errCount >=10:
                raise identifier
    
    #DeepLに英文を送る
    errCount=0
    f_succsess=False
    while not f_succsess:
        try: #DeepLに英文を送る
            driver.find_element_by_css_selector(input_selector).send_keys(mytext)
            f_succsess = True
        except Exception  as identifier:              
            errCount=errCount+1
            if errCount >=10:
                raise identifier
            time.sleep(1)

    #フラグ用
    Output_before = ""
    while 1:
        errCount=0
        f_succsess=False
        while not f_succsess:
            try:# DeepLの出力を取得する
                Output = driver.find_element_by_css_selector(Output_selector).get_attribute("textContent")
                f_succsess = True
            except Exception  as identifier:               
                errCount=errCount+1
                if errCount >=10:
                    raise identifier
                time.sleep(1) 
        '''
        取得したoutputが空文字なら、まだ翻訳が終了してないということで、1秒後に再チェック。
        取得したoutputが空文字でない場合、1つ前のoutputと比べて違う内容になってるなら、
        まだ翻訳が終わり切ってないということで1秒後に再チェック。
        取得したoutputが空文字でない場合、1つ前のoutputと同じ内容なら、翻訳終了ということで出力。
        '''        
        if Output != "" : #出力が空文字でないとすれば結果の出力が始まった
            if Output_before == Output:#出力が1つ前の出力と同じなら、出力が完了したってこと
                break
            Output_before = Output            
        time.sleep(1)

    #chromeを閉じる
    driver.close()
 
    #結果出力
    return Output
88
88
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
88
88

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?