###9/9追記
クリップボード経由でのDeepLへの入力を廃止し、Javascriptを用いた方式に変更。
それに伴いSeleniumのヘッドレスモードでの使用に対応。
###9/14追記
表周りでレイアウトが崩れる問題に対応。
文章と同じparagraph中に画像が埋め込まれている場合(画像化された数式など)、文字を置き換えると画像が消えることが判明、解決策が見つかるまで翻訳対象から除外。
アップデート版
import win32com.client
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time
import re
from math import ceil
from threading import Thread
DRIVER_PATH = 'chromedriver.exe'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'
options = Options()
options.add_argument(f'--user-agent={user_agent}')
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--proxy-server="direct://"')
options.add_argument('--proxy-bypass-list=*')
options.add_argument('--start-maximized')
options.add_argument('--headless') #コメントアウトでヘッドレスモード解除(Chromeが表示される)
def Deeptrans(t, driver):
global translated_texts
stextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__source_textarea.lmt__textarea_base_style')
ttextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style')
for i in range(t * unit, min((t + 1) * unit, length)):
if sourse_texts[i]: sourse_text = sourse_texts[i]
else: continue
if not sourse_text.strip():
continue
driver.execute_script(
f'$(".lmt__source_textarea").val({repr(sourse_text)});')
stextarea.send_keys(Keys.RIGHT)
translated_text = ""
while not translated_text:
time.sleep(1)
translated_text = ttextarea.get_property("value")
stextarea.send_keys(Keys.CONTROL, "a")
stextarea.send_keys(Keys.BACKSPACE)
translated_texts.append({"index": i + 1, "text": translated_text})
def runDriver(t):
global options
driver = webdriver.Chrome(executable_path=DRIVER_PATH, options=options)
url = 'https://www.deepl.com/ja/translator'
driver.get(url)
Deeptrans(t, driver)
driver.quit()
def multiThreadTranslate(file_path, font):
global length, unit, sourse_texts, translated_texts
app = win32com.client.Dispatch("Word.Application")
#app.Visible = True
doc = app.Documents.Open(file_path)
try:
doc.Paragraphs(1).Range.Font.Name = font
except:
print('指定されたフォントは存在しません')
return
length = doc.Paragraphs.Count
n = 9
unit = ceil(length / n)
sourse_texts = [
doc.Paragraphs(i + 1).Range.Text if
(str(doc.Paragraphs(i + 1).Range.Style) != "TableGrid" and
str(doc.Paragraphs(min(length, i + 2)).Range.Style) != "TableGrid" and doc.Paragraphs(i + 1).Range.InlineShapes.Count)
else None for i in range(length)
]
translated_texts = []
threads = []
for t in range(n):
thread = Thread(target=runDriver, args=(t, ))
thread.setDaemon(True)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
for translated_text in sorted(translated_texts, key=lambda i: i["index"]):
doc.Paragraphs(translated_text["index"]
).Range.Text = translated_text["text"].replace(
'\n', '\r')
doc.Paragraphs(translated_text["index"]).Range.Font.Name = font
doc.SaveAs2(FileName=re.sub("(.+)(\.pdf)", r"\1_jp.pdf", file_path),
FileFormat=17)
doc.Close(SaveChanges=0)
app.Quit()
print('Process is completed.')
if __name__ == '__main__':
file_path = input('PDFの絶対パスを入力してください(ドラッグアンドドロップでも可): ')
print('フォントを選択してください')
fonts = {'1': '游明朝', '2': 'メイリオ', '3': 'BIZ UDP明朝 Medium', '4': 'その他'}
font = fonts[input(' '.join(
[", ".join(list(fonts.items())[i])
for i in range(len(fonts))]) + ": ")]
if font == 'その他': font = input('フォント名を入力してください: ')
multiThreadTranslate(file_path, font=font)
###9/15追記
・1スレッド版で上記問題をとりあえず解消しました。
・勝手にフォントサイズが調節されてガタガタになる問題、変なインデントが入る問題を解消しました。
とりあえず解決版(シングルスレッド)
import win32com.client
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time
import re
from tqdm import tqdm
DRIVER_PATH = 'chromedriver.exe'
options = Options()
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--proxy-server="direct://"')
options.add_argument('--proxy-bypass-list=*')
options.add_argument('--start-maximized')
def Deeptrans(file_path, font):
app = win32com.client.Dispatch("Word.Application")
app.Visible = True
doc = app.Documents.Open(file_path)
driver = webdriver.Chrome(executable_path=DRIVER_PATH,
chrome_options=options)
url = 'https://www.deepl.com/ja/translator#en/ja'
driver.get(url)
stextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__source_textarea.lmt__textarea_base_style')
ttextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style')
length = doc.Paragraphs.Count
for i in tqdm(range(length)):
if str(doc.Paragraphs(i + 1).Range.Style) == "TableGrid":
continue
sourse_text = doc.Paragraphs(i + 1).Range.Text
fs = doc.Paragraphs(i + 1).Range.Font.Size
alignment = doc.Paragraphs(i + 1).Alignment
lindent = doc.Paragraphs(i + 1).LeftIndent
rindent = doc.Paragraphs(i + 1).RightIndent
if doc.Paragraphs(i + 1).Range.InlineShapes.Count:
if sourse_text.strip() == "/": continue
doc.Paragraphs(i + 1).Range.Font.Name = font
t = ""
te = []
cnt = 0
for j in range(doc.Paragraphs(i + 1).Range.Words.Count):
if "/" not in doc.Paragraphs(i + 1).Range.Words(j + 1).Text:
t += doc.Paragraphs(i + 1).Range.Words(j + 1).Text
doc.Paragraphs(i + 1).Range.Words(j + 1).Text = "'' "
else:
te.append(t)
t = ""
cnt += 1
if t: te.append(t)
for j, sourse_text in enumerate(te):
if len(sourse_text.strip()) > 5:
driver.execute_script(
f'$(".lmt__source_textarea").val({repr(sourse_text)});'
)
stextarea.send_keys(Keys.RIGHT)
translated_text = ""
while not translated_text:
time.sleep(1)
translated_text = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style'
).get_property("value")
stextarea.send_keys(Keys.CONTROL, "a")
stextarea.send_keys(Keys.BACKSPACE)
te[j] = translated_text
g = (j for j in te)
c = 0
for j in doc.Paragraphs(i + 1).Range.Words:
if j.Text == "'' ":
j.Text = ""
elif "/" in j.Text:
try:
j.InsertBefore(g.__next__())
c += 1
if c == cnt:
j.InsertAfter(g.__next__())
except:
pass
doc.Paragraphs(i + 1).Alignment = alignment
doc.Paragraphs(i + 1).Range.Font.Size = fs
doc.Paragraphs(i + 1).LeftIndent = lindent
doc.Paragraphs(i + 1).RightIndent = rindent
continue
if re.search(r"[\x00-\x1F\x7F]",
sourse_text.strip()) or len(sourse_text.strip()) < 5:
continue
driver.execute_script(
f'$(".lmt__source_textarea").val({repr(sourse_text)});')
stextarea.send_keys(Keys.RIGHT)
translated_text = ""
while not translated_text:
time.sleep(1)
translated_text = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style'
).get_property("value")
stextarea.send_keys(Keys.CONTROL, "a")
stextarea.send_keys(Keys.BACKSPACE)
doc.Paragraphs(i + 1).Range.Text = translated_text
doc.Paragraphs(i + 1).Range.Font.Name = font
doc.Paragraphs(i + 1).Alignment = alignment
doc.Paragraphs(i + 1).Range.Font.Size = fs
doc.Paragraphs(i + 1).LeftIndent = lindent
doc.Paragraphs(i + 1).RightIndent = rindent
driver.quit()
doc.SaveAs2(FileName=re.sub("(.+)(\.pdf)", r"\1_jp.pdf", file_path),
FileFormat=17)
doc.Close(SaveChanges=0)
app.Quit()
print('Process is completed.')
if __name__ == '__main__':
file_path = input('PDFの絶対パスを入力してください: ')
print('フォントを選択してください')
fonts = {'1': '游明朝', '2': 'メイリオ', '3': 'BIZ UDP明朝 Medium', '4': 'その他'}
font = fonts[input(' '.join(
[", ".join(list(fonts.items())[i])
for i in range(len(fonts))]) + ": ")]
if font == 'その他': font = input('フォント名を入力してください: ')
Deeptrans(file_path, font)
#はじめに
以前、PDFの自動翻訳についての記事を書きましたが、
やっぱり画像や数式、段組みなど、元の形を保ちたい!
という心残りがあったため、手段を探したところ、Wordを利用した方法にたどり着いたので紹介したいと思います。
ただし、PDFとWordとの相性によってはあまりきれいに加工できない場合もあります。
#使用例
PDFはこちらからお借りしました→https://mirela.net.technion.ac.il/publications/
文字幅・文字数の関係で位置がずれています。
$EN→JA$
#必要なもの
・Windows PC
・Microsoft Word
・ChromeDriver(下記プログラムをそのまま実行する場合は実行ディレクトリ下に保存してください)
#流れ
プログラム開始
↓
Wordで対象PDFをdocxとして開く
↓
Paragraphごとに文章を取得
↓
SeleniumでDeepL翻訳
↓
Word経由で書き換え
↓
PDFとして保存
↓
プログラム終了
#実装
1段落ごとにやってると時間がかかるのでマルチスレッドで実行するようにしました。
何らかの理由で段落の位置を管理している番号がズレてしまうようでしたら、時間はかかりますが1段落ごとに翻訳するバージョンも載せておくのでお試しください。
import win32com.client
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time
import re
import pyperclip as ppc
from math import ceil
from threading import Thread, Lock
DRIVER_PATH = 'chromedriver.exe'
options = Options()
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--proxy-server="direct://"')
options.add_argument('--proxy-bypass-list=*')
options.add_argument('--start-maximized')
def Deeptrans(t, driver):
global translated_texts
stextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__source_textarea.lmt__textarea_base_style')
ttextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style')
for i in range(t * unit, min((t + 1) * unit, length)):
sourse_text = sourse_texts[i]
if re.search(r"[\x00-\x1F\x7F]",
sourse_text.strip()) or len(sourse_text.strip()) < 5:
continue
lock.acquire()
ppc.copy(sourse_text)
stextarea.send_keys(Keys.CONTROL, "v")
lock.release()
translated_text = ""
while not translated_text:
time.sleep(1)
translated_text = ttextarea.get_property("value")
stextarea.send_keys(Keys.CONTROL, "a")
stextarea.send_keys(Keys.BACKSPACE)
translated_texts[str(i + 1)] = translated_text
def runDriver(t):
driver = webdriver.Chrome(DRIVER_PATH)
url = 'https://www.deepl.com/ja/translator'
driver.get(url)
Deeptrans(t, driver)
driver.quit()
def multiThreadTranslate(file_path, font):
global lock, length, unit, sourse_texts, translated_texts
app = win32com.client.Dispatch("Word.Application")
app.Visible = True #コメントアウトでWord非表示
doc = app.Documents.Open(file_path)
try:
doc.Paragraphs(1).Range.Font.Name = font
except:
print('指定されたフォントは存在しません')
doc.Close(SaveChanges=0)
app.Quit()
return
length = doc.Paragraphs.Count
n = 9 #Chromeを9つ開いて同時実行
unit = ceil(length / n)
lock = Lock()
clipboard = ppc.paste()
sourse_texts = [doc.Paragraphs(i + 1).Range.Text for i in range(length)]
translated_texts = {}
threads = []
for t in range(n):
thread = Thread(target=runDriver, args=(t, ))
thread.setDaemon(True)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
for k, v in translated_texts.items():
doc.Paragraphs(int(k)).Range.Text = v.replace('\n', '\r')
doc.Paragraphs(int(k)).Range.Font.Name = font
doc.SaveAs2(FileName=re.sub("(.+)(\.pdf)", r"\1_jp.pdf", file_path),
FileFormat=17)
doc.Close(SaveChanges=0)
app.Quit()
print('Process is completed.')
ppc.copy(clipboard)
if __name__ == '__main__':
file_path = input('PDFの絶対パスを入力してください: ')
print('フォントを選択してください')
fonts = {'1': '游明朝', '2': 'メイリオ', '3': 'BIZ UDP明朝 Medium', '4': 'その他'}
font = fonts[input(' '.join(
[", ".join(list(fonts.items())[i])
for i in range(len(fonts))]) + ": ")]
if font == 'その他': font = input('フォント名を入力してください: ')
multiThreadTranslate(file_path, font=font)
1段落ずつver.(プログレスバーのおまけ付き)
import win32com.client
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time
import re
import pyperclip as ppc
from tqdm import tqdm
DRIVER_PATH = 'chromedriver.exe'
options = Options()
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--proxy-server="direct://"')
options.add_argument('--proxy-bypass-list=*')
options.add_argument('--start-maximized')
def Deeptrans(file_path, font):
clipboard = ppc.paste()
app = win32com.client.Dispatch("Word.Application")
app.Visible = True
doc = app.Documents.Open(file_path)
driver = webdriver.Chrome(executable_path=DRIVER_PATH,
chrome_options=options)
url = 'https://www.deepl.com/ja/translator#en/ja'
driver.get(url)
stextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__source_textarea.lmt__textarea_base_style')
ttextarea = driver.find_element_by_css_selector(
'.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style')
for i in tqdm(range(doc.Paragraphs.Count)):
sourse_text = doc.Paragraphs(i + 1).Range.Text
if re.search(r"[\x00-\x1F\x7F]",
sourse_text.strip()) or len(sourse_text.strip()) < 5:
continue
ppc.copy(sourse_text)
stextarea.send_keys(Keys.CONTROL, "v")
translated_text = ""
while not translated_text:
time.sleep(1)
translated_text = ttextarea.get_property("value")
stextarea.send_keys(Keys.CONTROL, "a")
stextarea.send_keys(Keys.BACKSPACE)
doc.Paragraphs(i + 1).Range.Text = translated_text
doc.Paragraphs(i + 1).Range.Font.Name = font
driver.quit()
doc.SaveAs2(FileName=re.sub("(.+)(\.pdf)", r"\1_jp.pdf", file_path),
FileFormat=17)
doc.Close(SaveChanges=0)
app.Quit()
print('Process is completed.')
ppc.copy(clipboard)
if __name__ == '__main__':
file_path = input('PDFの絶対パスを入力してください: ')
print('フォントを選択してください')
fonts = {'1': '游明朝', '2': 'メイリオ', '3': 'BIZ UDP明朝 Medium', '4': 'その他'}
font = fonts[input(' '.join(
[", ".join(list(fonts.items())[i])
for i in range(len(fonts))]) + ": ")]
if font == 'その他': font = input('フォント名を入力してください: ')
Deeptrans(file_path, font)
#使い方
使い方は保存してコマンドラインから実行するだけです(必要なライブラリは別途インストールしてください)。
しばらくすると元ファイルと同じディレクトリに元の名前_jp.pdf
というファイルが出力されます。
#課題
数式などと地の文の区別が難しく、形が崩れたり消えてしまうことがある。
付け焼き刃で対応したが、逆に翻訳されない文などが生じてしまっている。
いい方法求む。
#まとめ
Word様様です。
興味がありましたらどうぞお試しください。