1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Python调用有道 百度 彩云API实现自然语言翻译

Python调用有道 百度 彩云API实现自然语言翻译

时间:2021-05-05 11:23:53

相关推荐

Python调用有道 百度 彩云API实现自然语言翻译

最近做的某个项目中需要用到中英文之间的翻译,故使用 Python 编写MachineTranslation包,调用有道智云、彩云小译、百度的自然语言通用翻译 API。

需求

在一个 Python 包(具有__init__.py的目录)中实现对三种 API 的调用,且调用方式统一。出于安全与便于编辑考虑,将所有 key/token 保存至一个 json 文件中。

目录结构

目录结构如下:

MachineTranslation│├── __init__.py├── CaiYun.py├── Baidu.py├── YouDao.py└── KeySecret.json

其中CaiYun.pyBaidu.pyYouDao.py中实现调用 API 的Translator类。KeySecret.json中保存密钥。__init__.py实现 import 与归一化。

通用调用(多态与归一化)

封装与统一接口

三种调用不同服务商的Translator类中有许多不同的属性、方法,然而这些不重要细节对本包的调用者来说应是完全透明的——只需要保证提供一个参数、返回值类型完全相同的translate方法即可。不同的具体实现,封装,提供统一的接口。

对于C++面向对象编程,可以使用抽象基类多态技术来完成这样的归一化:

创建一个Translator抽象基类定义Translator中的抽象成员函数translate,所有子类都必须实现该方法在三个子类中分别实现translate方法

Python 标准库中的确有 abc 这个抽象基类模块,可以实现类似上述C++多态的方式来解决问题。

鸭子类型

而以灵活著称的 Python,在实现多态时,更崇尚鸭子类型(duck typing):

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

只关心某个对象是否能实现 walk quack 这样的方法,实现即为鸭子,不能实现就不是,不关心该对象的类型。

某个类实现了.translate(q, mode)方法,即认为它是一个Translator类。

具体实现

最终__init__.py中代码如下:

# __init__.pydef machine_translator(provider: str):"""返回指定服务商的机器翻译类的实例:param provider: API提供商:return: 翻译类的实例"""if provider == "YouDao" or provider in {"youdao", "you_dao", "YOUDAO", "You_Dao"}:from MachineTranslation.YouDao import YouDaoTranslatorreturn YouDaoTranslator()elif provider == "CaiYun" or provider in {"CaiYunXiaoYi", "caiyun", "CAIYUN"}:from MachineTranslation.CaiYun import CaiYunTranslatorreturn CaiYunTranslator()elif provider == "Baidu" or provider in {"BaiDu", "baidu", "Bai_du", "BAIDU"}:from MachineTranslation.Baidu import BaiduTranslatorreturn BaiduTranslator()

machine_translator函数会根据provider参数返回一个指定服务商类的实例化对象。

判断条件中or右侧的条件方便调用者使用服务商别名(类似 Python 内置类型中对utf-8UTF-8等别名的处理)。当然,得益于布尔运算中的短路运算,理论上调用时使用or左侧准确的名称时会有微小的速度优势。

将 import 语句写到条件分支中,只有调用该服务商时才 import 对应代码。若用户只调用一两个服务商的 Translator 类,可略微减小参与运行的代码量。

而在外部调用MachineTranslation包时的代码如下:

# main.pyfrom MachineTranslation import machine_translatorm_translator = machine_translator("YouDao") # 看似为实例化m_t类,其实是函数调用# 无论真正实例化的是哪个Translator类,使用方式都完全一致print(m_translator.translate("Hello World.", "en2zh"))print(m_translator.translate("人生苦短,我用Python。", "zh2en"))

密钥 json 文件

KeySecret.json文件内容如下:

{"YouDao": {"APP_KEY": "Your APP_KEY here","APP_SECRET": "Your APP_SECRET here"},"CaiYun": {"Token": "Your Token here"},"Baidu": {"appid": "Your appid here","appkey": "Your appkey here"}}

用户使用前需要自行前往对应官网申请密钥,链接详见下文。

有道翻译

官方文档:有道智云-自然语言翻译服务-API文档

参考 文档化 Python 代码:完全指南(翻译),此次编写代码时加入了较丰富的__doc__与 Type Hint,在 PyCharm 等 IDE 中有很好的提示。

模块代码全文如下:

# YouDao.pyimport hashlibimport timeimport uuidfrom json import loads as json_loadsimport requestsYOUDAO_URL = "/api"KEY_FILE = "./KeySecret.json" # 存储key与secret的json文件路径MAX_LENGTH = 1500 # 限制翻译输入的最大长度def load_key_secret(key_file: str) -> tuple[str, str]:"""读取json文件中保存的API key:param key_file:存储key与secret的json文件:return:(key, secret)"""with open(key_file, "r", encoding="utf-8") as f:data = json_loads(f.read())["YouDao"]app_key = data["APP_KEY"]app_secret = data["APP_SECRET"]return app_key, app_secretclass YouDaoTranslator:"""调用有道翻译API实现机器翻译"""def __init__(self):self.q = "" # 待翻译内容self._request_data = {}self._APP_KEY, self._APP_SECRET = load_key_secret(KEY_FILE)def _gen_sign(self, current_time: str, salt: str) -> str:"""生成签名:param current_time: 当前UTC时间戳(秒):param salt: UUID:return: sign"""q = self.qq_size = len(q)if q_size <= 20:sign_input = qelse:sign_input = q[0:10] + str(q_size) + q[-10:]sign_str = self._APP_KEY + sign_input + salt + current_time + self._APP_SECREThash_algorithm = hashlib.sha256()hash_algorithm.update(sign_str.encode("utf-8"))return hash_algorithm.hexdigest()def _package_data(self, current_time: str, salt: str) -> None:"""设置接口调用参数:param current_time: 当前UTC时间戳(秒):param salt: UUID:return: None"""request_data = self._request_datarequest_data["q"] = self.q # 待翻译内容request_data["appKey"] = self._APP_KEYrequest_data["salt"] = saltrequest_data["sign"] = self._gen_sign(current_time, salt)request_data["signType"] = "v3"request_data["curtime"] = current_time# _request_data["ext"] = "mp3" # 翻译结果音频格式# _request_data["voice"] = "0" # 翻译结果发音选择,0为女声,1为男声request_data["strict"] = "true" # 是否严格按照指定from和to进行翻译# _request_data["vocabId"] = "out_Id" # 用户上传的词典,详见文档def _set_trs_mode(self, mode: str) -> None:"""设置翻译语言模式:param mode: 语言模式,en2zh或zh2en:return: None"""if mode == "en2zh":self._request_data["from"] = "en"self._request_data["to"] = "zh-CHS"elif mode == "zh2en":self._request_data["from"] = "zh-CHS"self._request_data["to"] = "en"else:# 处理中英互译之外的异常翻译模式self._request_data["from"] = "auto"self._request_data["to"] = "auto"def _do_request(self) -> requests.Response:"""发送请求并获取Response:return: Response"""current_time = str(int(time.time()))salt = str(uuid.uuid1())self._package_data(current_time, salt)headers = {"Content-Type": "application/x-www-form-urlencoded"}return requests.post(YOUDAO_URL, data=self._request_data, headers=headers)def translate(self, q: str, mode: str) -> str:"""翻译:param q: 待翻译文本:param mode: 翻译语言模式,en2zh或zh2en:return: 翻译结果"""if not q:return "q is empty!"if len(q) > MAX_LENGTH:return "q is too long!"self.q = qself._set_trs_mode(mode)response = self._do_request()content_type = response.headers["Content-Type"]if content_type == "audio/mp3":# 返回mp3格式的音频结果millis = int(round(time.time() * 1000))file_path = "合成的音频存储路径" + str(millis) + ".mp3"with open(file_path, "wb") as fo:fo.write(response.content)trans_result = file_pathelse:# 返回json格式的文本结果error_code = json_loads(response.content)["errorCode"] # 有道API的错误码if error_code == "0":trans_result = json_loads(response.content)["translation"]else:trans_result = f"ErrorCode {error_code}, check YouDao's API doc plz."return trans_resultif __name__ == "__main__":translator = YouDaoTranslator()print(translator.translate("So we beat on, boats against the current, borne back ceaselessly into the past.","en2zh",))print(translator.translate("一个人只拥有此生此世是不够的,他还应该拥有诗意的世界。", "zh2en"))

YouDaoTranslator类中的部分属性与方法名是以单下划线开头,表明是不希望被外部修改和调用的。

百度翻译

官方文档:百度通用翻译API文档

代码:

# Baidu.pyimport jsonfrom hashlib import md5from random import randintimport requestsBAIDU_URL = "https://fanyi-/api/trans/vip/translate"MAX_LENGTH = 1800 # 限制翻译输入的最大长度def load_appid(appid_file: str = "./KeySecret.json") -> tuple[str, str]:"""读取json文件中保存的appid与appkey:param appid_file: 存储appid与appkey的json文件:return: (appid, appkey)"""with open(appid_file, "r", encoding="utf-8") as f:data = json.loads(f.read())["Baidu"]app_id = data["appid"]app_key = data["appkey"]return app_id, app_keyclass BaiduTranslator:"""调用百度通用翻译API实现机器翻译"""def __init__(self):self.q = "" # 待翻译内容self._payload = {}self._appid, self._appkey = load_appid()def _gen_salt_sign(self) -> tuple[int, str]:"""生成salt与签名:return: (salt, sign)"""salt = randint(32768, 65536)tmp_str = self._appid + self.q + str(salt) + self._appkeysign = md5(tmp_str.encode("utf-8")).hexdigest()return salt, signdef _package_data(self) -> None:"""设置接口调用参数:return: None"""salt, sign = self._gen_salt_sign()payload = self._payloadpayload["q"] = self.qpayload["appid"] = self._appidpayload["salt"] = saltpayload["sign"] = sign # 签名def _set_trs_mode(self, mode: str):"""设置翻译语言模式:param mode: 语言模式,en2zh或zh2en:return: None"""if mode == "en2zh":self._payload["from"] = "en"self._payload["to"] = "zh"elif mode == "zh2en":self._payload["from"] = "zh"self._payload["to"] = "en"else:# 处理中英互译之外的异常翻译模式self._payload["from"] = "auto"self._payload["to"] = "zh"def _do_request(self) -> requests.Response:"""发送请求并获取Response:return: Response"""self._package_data()headers = {"Content-Type": "application/x-www-form-urlencoded"}return requests.post(BAIDU_URL, params=self._payload, headers=headers)def translate(self, q: str, mode: str) -> str:"""翻译:param q: 待翻译文本:param mode: 翻译语言模式,en2zh或zh2en:return: 翻译结果"""if not q:return "q is empty!"if len(q) > MAX_LENGTH:return "q is too long!"self.q = qself._set_trs_mode(mode)response = self._do_request()trans_result = json.loads(response.content)# TODO 获取翻译结果return trans_resultif __name__ == "__main__":translator = BaiduTranslator()print(translator.translate("So we beat on, boats against the current, borne back ceaselessly into the past.","en2zh",))print(translator.translate("一个人只拥有此生此世是不够的,他还应该拥有诗意的世界。", "zh2en"))

彩云小译

官方文档:彩云小译API文档

代码:

# CaiYun.pyimport jsonimport requestsCAIYUN_URL = "http://api./v1/translator"MAX_LENGTH = 1500 # 限制翻译输入的最大长度def load_token(token_file: str = "MachineTranslation/KeySecret.json") -> str:"""读取json文件中保存的API token:param token_file: 存储key的json文件:return: token"""with open(token_file, "r", encoding="utf-8") as f:data = json.loads(f.read())["CaiYun"]token = data["Token"]return tokenclass CaiYunTranslator:"""调用彩云小译API实现机器翻译"""def __init__(self):self.q = "" # 待翻译内容self._payload = {}self._token = load_token("./KeySecret.json")def _package_data(self) -> None:"""设置接口调用参数:return: None"""self._payload["source"] = self.qself._payload["request_id"] = "demo"self._payload["detect"] = Truedef _set_trs_mode(self, mode: str) -> None:"""设置翻译语言模式:param mode: 语言模式,en2zh或zh2en:return: None"""if mode in {"en2zh", "zh2en"}:self._payload["trans_type"] = modeelse:self._payload["trans_type"] = "auto2zh"def translate(self, q: str, mode: str) -> str:"""翻译:param q: 待翻译文本:param mode: 翻译语言模式,en2zh或zh2en:return: 翻译结果"""if not q:return "q is empty!"if len(q) > MAX_LENGTH:return "q is too long!"self.q = qself._set_trs_mode(mode)self._package_data()headers = {"content-type": "application/json","x-authorization": "token " + self._token,}response = requests.request("POST", CAIYUN_URL, data=json.dumps(self._payload), headers=headers)return json.loads(response.text)["target"]if __name__ == "__main__":translator = CaiYunTranslator()print(translator.translate("So we beat on, boats against the current, borne back ceaselessly into the past.","en2zh",))print(translator.translate("人生苦短,我用Python。", "zh2en"))

扩展阅读

一段关于面向对象编程的探讨 - invalid s

Python中下划线的5种含义

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。