改変履歴
'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: "
Open Sans", 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