大家好,我是我是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,兴许事半功倍呢!