洛阳亲友如相问,一片冰心在玉壶。

大家好,我是我是Python进阶者。

一、前言

前几天有个叫【张茜】的粉丝找我看了一个代码,关于电子书中英文自动翻译的,感觉挺有意思,这里拿出来给大家分享下。

二、简介

这个小项目是git上一个叫【xiaolai】的大佬分享的,看上去还是挺新的,发布不太久,14天前发布的。

手把手教你用Python脚本调用 DeepL API Pro 进电子书的行进行中英文自动翻译_java

一本书中文译文大约 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 全都去掉;并在之前加上一个空行;…… 当然,你可以在这里做更多你自己喜欢做的格式清理。为了方便起见,pathsource_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、运行代码之后,会自动读取待翻译的文件,然后进行翻译,如下图所示:


手把手教你用Python脚本调用 DeepL API Pro 进电子书的行进行中英文自动翻译_编程语言_02

2、运行完程序之后,可以得到想要的结果,如下图所示:


手把手教你用Python脚本调用 DeepL API Pro 进电子书的行进行中英文自动翻译_javascript_03

八、总结

大家好,我是Python进阶者。这篇文章主要给大家介绍了使用Python脚本调用DeepL API Pro进电子书的行中英文自动翻译的方法,代码亲测可行,欢迎大家积极尝试,下次再遇到需要自动翻译的时候,不妨调用下这个API,兴许事半功倍呢!

最后感谢粉丝【张茜】提问,感谢【xiaolai】大佬提供的代码和注释,感谢【🌑(这是月亮的背面)】大佬给予的支持和帮助。