今
日
鸡
汤
洛阳亲友如相问,一片冰心在玉壶。
大家好,我是我是Python进阶者。
一、前言
前几天有个叫【张茜】的粉丝找我看了一个代码,关于电子书中英文自动翻译的,感觉挺有意思,这里拿出来给大家分享下。
二、简介
这个小项目是git上一个叫【xiaolai】的大佬分享的,看上去还是挺新的,发布不太久,14天前发布的。
一本书中文译文大约 39 万字的书,差不多用 1.5 小时就可以处理完毕(包括基本的格式编辑),这速度恐怖如斯!下面一起来看看这款神器的使用方法吧!
三、电子书格式转换路径
首先,需要将电子书从 Kindle 中导出来,并用 ePubor 进行 deDRM,而后将电子书转换成 epub 文件。
我都是在 Amazon 上直接买,而后在电脑上安装一个老版本的 Kindle App,用鼠标右键点击书名,下载,并不打开该电子书,而后退出 Kindle。
ePubor Ultimate 也是个收费软件,能把旧版 Kindle 下载的电子书的 DRM 去掉;将 awz 文件转换成 epub 文件。(可参考这个网页)
然后,再用免费软件 Calibre 将 epub 转换成 htmlz 文件(一个压缩包)。(我尝试过使用命令行工具包 pandoc,但,比较之后,发现 Calibre 在保留样式方面可能更好一点……)
在 Terminal 里用 unzip 命令解开 htmlz 压缩包。
四、选择 html 格式作为翻译格式的原因
可以保留书中大量的脚注、尾注及其链接;DeepL 有专门的 API 参数处理 xml tag,tag_handling="xml";
可以通过 css 文件随意设置显示样式,比较灵活;
可以通过插入 javascript 函数指定某种特定语言的显示(比如,只显示中文);
可以用来作为源文件转换成任意格式的电子书……
另外,在调用 tag_handling="xml" 之后,DeepL API 返回的译文非常规整,能够保留所有 html tag;并且,“返回字符串” 与 “原字符串” 相同,可以作为一个判断依据 —— 该行有没有被翻译,如果没有,在生成的译文 html 文件中,该行没必要重复出现……
五、清理 html
html 文件整理起来比较麻烦,一个比较方便的手段是使用 BeautifulSoup 模块。BeautifulSoup 本来是爬虫工具,但,它又很方便的手段可以清理 html 文件。
以下脚本主要完成以下工作:
首先将 html 文件里的所有 \n 去掉;将所有
单独放在一行;将所有
也单独放在一行;将
内部的所有 \n 全都去掉;并在之前加上一个空行;…… 当然,你可以在这里做更多你自己喜欢做的格式清理。为了方便起见,path
和 source_filename
以及 target_filename
都单独指定。代码如下:
import bs4
import re
path = "John Law/" # 文件夹名称末尾得有 /
source_filename = "index.html"
target_filename = "index2.html"
html = open(path+source_filename)
htmltext = html.read()
soup = bs4.BeautifulSoup(htmltext)
# 将所有的 \n 去掉……
htmltext = str(bs4.BeautifulSoup(htmltext)).replace("\n", "")
# <h... 之前添加空行
pttn = r'<h'
rpl = r'\n\n<h'
re.findall(pttn, htmltext)
htmltext = re.sub(pttn, rpl, htmltext)
# <div... 之前添加空行
pttn = r'<div'
rpl = r'\n\n<div'
re.findall(pttn, htmltext)
htmltext = re.sub(pttn, rpl, htmltext)
# </div> 之前添加空行
pttn = r'</div>'
rpl = r'\n\n</div>'
re.findall(pttn, htmltext)
htmltext = re.sub(pttn, rpl, htmltext)
# <p... 之前添加空行
pttn = r'<p'
rpl = r'\n\n<p'
re.findall(pttn, htmltext)
htmltext = re.sub(pttn, rpl, htmltext)
fileSave = open(path+target_filename, "w")
fileSave.write(htmltext)
print(htmltext)
六、逐行提交 DeepL API Pro 进行翻译
将清理过的 html 交给以下脚本,逐行提交给 DeepL 翻译,并返回。
为了方便起见,path 和 source_filename 以及 target_filename 都单独指定。
lines 是 source_filename 的内容 new_lines 是将要放到 target_filename 中的内容 startline 是 “从哪一行开始提交 DeepL 翻译” endline 是 “到哪一行开始结束提交 DeepL 翻译”。代码如下:
import re
import requests
auth_key = "<your DeepL API Pro authentication key>" # 注意,要订阅的是 DeepL API Pro
target_language = "ZH" ## 当然,你可以将目标语言设置成任何 DeepL 支持的语言
path = "John Law/" # 文件夹名称末尾得有 /
source_filename = "index2.html" # 上一步生成的文件,成为这一步的 “源文件”
target_filename = "index3.html"
def translate(text):
result = requests.get(
"https://api.deepl.com/v2/translate",
params={
"auth_key": auth_key,
"target_lang": target_language,
"text": text,
"tag_handling": "xml", # 这个参数确保 DeepL 正确处理 html tags
},
)
return result.json()["translations"][0]["text"]
def add_language_tag_en(html):
pttn = re.compile(r'^<(.*?) class="(.*?)">', re.M)
rpl = r'<\1 class="\2 en">'
re.findall(pttn, html)
html = re.sub(pttn, rpl, html)
return html
def add_language_tag_cn(html):
pttn = re.compile(r'^<(.*?) class="(.*?)">', re.M)
rpl = r'<\1 class="\2 cn">'
re.findall(pttn, html)
html = re.sub(pttn, rpl, html)
return html
lines = open(path+source_filename, "r").readlines()
new_lines = []
line_count = 0
startline = 16
endline = 4032
for line in lines:
line_count += 1
if line_count < startline or line_count > endline or line.strip() == '':
new_lines.append(line)
print(line)
continue
succeeded = False
while not succeeded:
# 以下比较粗暴的 try... except,用来防止执行过程中出现 DeepL 连接错误而导致翻译任务中断……
try:
line_translated = translate(line)
# 以下一行确保将返回的字符串转换成一整行,而非含有 \n 的多行文本
line_translated = line_translated.replace("\n", "")
succeeded = True
except:
succeeded = False
if line.strip() == line_translated.strip():
#返回的字符串与原字符串相同,说明 html tag 之间的内容无需翻译
new_lines.append(line)
print(line)
else:
line = add_language_tag_en(line)
line_translated = add_language_tag_cn(line_translated)
new_lines.append(line)
print(line)
new_lines.append(line_translated)
print(line_translated)
with open(path+target_filename, 'w') as f:
f.write("\n".join(new_lines))
七、结果展示
1、运行代码之后,会自动读取待翻译的文件,然后进行翻译,如下图所示:
2、运行完程序之后,可以得到想要的结果,如下图所示:
八、总结
大家好,我是Python进阶者。这篇文章主要给大家介绍了使用Python脚本调用DeepL API Pro进电子书的行中英文自动翻译的方法,代码亲测可行,欢迎大家积极尝试,下次再遇到需要自动翻译的时候,不妨调用下这个API,兴许事半功倍呢!
最后感谢粉丝【张茜】提问,感谢【xiaolai】大佬提供的代码和注释,感谢【🌑(这是月亮的背面)】大佬给予的支持和帮助。