纯 Python 实现的 Google 批量翻译
测试通过时间:2019-8-20
参阅:C#实现谷歌翻译API、Python之Google翻译爬虫
首先声明,没有什么不良动机,因为经常会用 translate.google.cn,就想着用 Python 模拟网页提交实现文档的批量翻译。据说有 API,可是要收费。
生成 Token
Google 为防爬虫而生成 token 的代码是 Javascript 的,且是根据网站的 TKK 值和提交的文本动态生成。更新规律未知,只好定时去取一下了。
网上能找到的 Python 代码大部分是去调用 PyExecJS 库,先不说执行效率的高低(大概是差一个数量级),首先是舍近求远,不纯粹,本人不喜欢。
好不容易找到了一段 Python 代码还有点小 Bug,且缺少动态获取 TKK 的步骤。最后还是对照 Javascript 代码自己改成 Python 了。方法很简单,先转成易懂的 Javascript,再转成 Python。Javascript 代码来自C#实现谷歌翻译API。
原始(晦涩) Javascript 代码
var b = function (a, b) {
for (var d = 0; d < b.length - 2; d += 3) {
var c = b.charAt(d + 2),
c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c),
c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c
}
return a
}
var tk = function (a,TKK) {
for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {
var c = a.charCodeAt(f);
128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ? (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240, g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)
}
a = h;
for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6");
a = b(a, "+-3^+b+-f");
a ^= Number(e[1]) || 0;
0 > a && (a = (a & 2147483647) + 2147483648);
a %= 1E6;
return a.toString() + "." + (a ^ h)
}
易懂的 Javascript 代码
function RL(a, b) {
for (var d = 0; d < b.length - 2; d += 3) {
var c = b.charAt(d + 2);
c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c);
c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c;
}
return a
}
function TL(a,TKK) {
var e = TKK.split(".");
var h = Number(e[0]) || 0;
var g = [];
var d = 0;
for (var f = 0; f < a.length; f++) {
var c = a.charCodeAt(f);
if (128 > c)
{
g[d++] = c;
}
else
{
if (2048 > c)
{
g[d++] = c >> 6 | 192;
}
else
{
if (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512))
{
c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023);
g[d++] = c >> 18 | 240;
g[d++] = c >> 12 & 63 | 128;
}
else
{
g[d++] = c >> 12 | 224;
g[d++] = c >> 6 & 63 | 128;
}
}
g[d++] = c & 63 | 128;
}
}
a = h;
for (var d = 0; d < g.length; d++) {
a += g[d];
a = b(a, "+-a^+6");
}
a = b(a, "+-3^+b+-f");
a ^= Number(e[1]) || 0;
0 > a && (a = (a & 2147483647) + 2147483648);
a %= 1E6;
return a.toString() + "." + (a ^ h)
}
Python 代码
def getGoogleToken(a, TKK):
def RL(a, b):
for d in range(0, len(b)-2, 3):
c = b[d + 2]
c = ord(c[0]) - 87 if 'a' <= c else int(c)
c = a >> c if '+' == b[d + 1] else a << c
a = a + c & 4294967295 if '+' == b[d] else a ^ c
return a
g = []
f = 0
while f < len(a):
c = ord(a[f])
if 128 > c:
g.append(c)
else:
if 2048 > c:
g.append((c >> 6) | 192)
else:
if (55296 == (c & 64512)) and (f + 1 < len(a)) and (56320 == (ord(a[f+1]) & 64512)):
f += 1
c = 65536 + ((c & 1023) << 10) + (ord(a[f]) & 1023)
g.append((c >> 18) | 240)
g.append((c >> 12) & 63 | 128)
else:
g.append((c >> 12) | 224)
g.append((c >> 6) & 63 | 128)
g.append((c & 63) | 128)
f += 1
e = TKK.split('.')
h = int(e[0]) or 0
t = h
for item in g:
t += item
t = RL(t, '+-a^+6')
t = RL(t, '+-3^+b+-f')
t ^= int(e[1]) or 0
if 0 > t:
t = (t & 2147483647) + 2147483648
result = t % 1000000
return str(result) + '.' + str(result ^ h)
获取 Token Key
Google 的 TKK 可以通过访问网站 https://translate.google.cn 获取,里面有段脚本里包含了“tkk:('xxxxxx.xxxxxx')”,用正则表达式截取即可。
res = requests.get('https://translate.google.cn', timeout = 3)
res.raise_for_status()
result = re.search(r'tkk\:\'(\d+\.\d+)?\'', res.text).group(1)
划分文章段落
因为常从 PDF 里复制文本翻译,这样就不能依赖换行符来划分段落了。只能判断空行,作为段落的分界。
另外 Google 返回的结果 Json 里,会以英文句点作为分隔符,每一句译文均作为数组的一项分开。所以最后得合并一下,成为一个段落。
完整代码
代码不长,全文黏贴如下。
GoogleTranslator.py:
import requests
import re
import json
import time
class GoogleTranslator ():
_host = 'translate.google.cn'
_headers = {
'Host': _host,
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Mobile Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
'Referer': 'https://' + _host,
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0'
}
_language = {
'afrikaans': 'af',
'arabic': 'ar',
'belarusian': 'be',
'bulgarian': 'bg',
'catalan': 'ca',
'czech': 'cs',
'welsh': 'cy',
'danish': 'da',
'german': 'de',
'greek': 'el',
'english': 'en',
'esperanto': 'eo',
'spanish': 'es',
'estonian': 'et',
'persian': 'fa',
'finnish': 'fi',
'french': 'fr',
'irish': 'ga',
'galician': 'gl',
'hindi': 'hi',
'croatian': 'hr',
'hungarian': 'hu',
'indonesian': 'id',
'icelandic': 'is',
'italian': 'it',
'hebrew': 'iw',
'japanese': 'ja',
'korean': 'ko',
'latin': 'la',
'lithuanian': 'lt',
'latvian': 'lv',
'macedonian': 'mk',
'malay': 'ms',
'maltese': 'mt',
'dutch': 'nl',
'norwegian': 'no',
'polish': 'pl',
'portuguese': 'pt',
'romanian': 'ro',
'russian': 'ru',
'slovak': 'sk',
'slovenian': 'sl',
'albanian': 'sq',
'serbian': 'sr',
'swedish': 'sv',
'swahili': 'sw',
'thai': 'th',
'filipino': 'tl',
'turkish': 'tr',
'ukrainian': 'uk',
'vietnamese': 'vi',
'yiddish': 'yi',
'chinese_simplified': 'zh-CN',
'chinese_traditional': 'zh-TW',
'auto': 'auto'
}
_url = 'https://' + _host + '/translate_a/single'
_params = {
'client': 'webapp',
'sl': 'en',
'tl': 'zh-CN',
'hl': 'zh-CN',
'dt': 'at',
'dt': 'bd',
'dt': 'ex',
'dt': 'ld',
'dt': 'md',
'dt': 'qca',
'dt': 'rw',
'dt': 'rm',
'dt': 'ss',
'dt': 't',
'otf': '1',
'ssel': '0',
'tsel': '0',
'kc': '1'
}
__cookies = None
__googleTokenKey = '376032.257956'
__googleTokenKeyUpdataTime = 600.0
__googleTokenKeyRetireTime = time.time() + 600.0
def __init__(self, src = 'en', dest = 'zh-CN', tkkUpdataTime = 600.0):
if src not in self._language and src not in self._language.values():
src = 'auto'
if dest not in self._language and dest not in self._language.values():
dest = 'auto'
self._params['sl'] = src
self._params['tl'] = dest
self.googleTokenKeyUpdataTime = tkkUpdataTime
self.__updateGoogleTokenKey()
def __updateGoogleTokenKey(self):
self.__googleTokenKey = self.__getGoogleTokenKey()
self.__googleTokenKeyRetireTime = time.time() + self.__googleTokenKeyUpdataTime
def __getGoogleTokenKey(self):
"""Get the Google TKK from https://translate.google.cn"""
# TKK example: '435075.3634891900'
result = ''
try:
res = requests.get('https://' + self._host, timeout = 3)
res.raise_for_status()
self.__cookies = res.cookies
result = re.search(r'tkk\:\'(\d+\.\d+)?\'', res.text).group(1)
except requests.exceptions.ReadTimeout as ex:
print('ERROR: ' + str(ex))
time.sleep(1)
return result
def __getGoogleToken(self, a, TKK):
"""Calculate Google tk from TKK """
# https://www.cnblogs.com/chicsky/p/7443830.html
# if text = 'Tablet Developer' and TKK = '435102.3120524463', then tk = '315066.159012'
def RL(a, b):
for d in range(0, len(b)-2, 3):
c = b[d + 2]
c = ord(c[0]) - 87 if 'a' <= c else int(c)
c = a >> c if '+' == b[d + 1] else a << c
a = a + c & 4294967295 if '+' == b[d] else a ^ c
return a
g = []
f = 0
while f < len(a):
c = ord(a[f])
if 128 > c:
g.append(c)
else:
if 2048 > c:
g.append((c >> 6) | 192)
else:
if (55296 == (c & 64512)) and (f + 1 < len(a)) and (56320 == (ord(a[f+1]) & 64512)):
f += 1
c = 65536 + ((c & 1023) << 10) + (ord(a[f]) & 1023)
g.append((c >> 18) | 240)
g.append((c >> 12) & 63 | 128)
else:
g.append((c >> 12) | 224)
g.append((c >> 6) & 63 | 128)
g.append((c & 63) | 128)
f += 1
e = TKK.split('.')
h = int(e[0]) or 0
t = h
for item in g:
t += item
t = RL(t, '+-a^+6')
t = RL(t, '+-3^+b+-f')
t ^= int(e[1]) or 0
if 0 > t:
t = (t & 2147483647) + 2147483648
result = t % 1000000
return str(result) + '.' + str(result ^ h)
def translate(self, text):
if time.time() > self.__googleTokenKeyRetireTime:
self.__updateGoogleTokenKey()
data = {'q': text}
self._params['tk'] = self.__getGoogleToken(text, self.__googleTokenKey)
result = ''
try:
res = requests.post(self._url,
headers = self._headers,
cookies = self.__cookies,
data = data,
params = self._params,
timeout = 6)
res.raise_for_status()
jsonText = res.text
if len(jsonText)>0:
jsonResult = json.loads(jsonText)
if len(jsonResult[0])>0:
for item in jsonResult[0]:
result += item[0]
return result
except Exception as ex:
print('ERROR: ' + str(ex))
return ''
import time
from GoogleTranslator import GoogleTranslator
def readFile(fileName):
with open(fileName, 'r') as f:
paragraph = ''
for line in f:
if line[0]!='\n':
paragraph += line.strip('\n')
else:
if len(paragraph)>0:
yield paragraph
paragraph = ''
if len(paragraph)>0:
yield paragraph
main.py:
def main():
translator = GoogleTranslator()
count = 0
with open('C:\\dx\\python\\d.txt', 'w', encoding='utf-8') as df:
for line in readFile('C:\\dx\\python\\s.txt'):
if len(line) > 1:
count += 1
print('\r' + str(count), end = '', flush = True)
df.write(line.strip() + "\n")
result = translator.translate(line)
df.write(result.strip() + "\n\n")
if __name__ == "__main__":
startTime = time.time()
main()
print()
print('%.2f seconds' % (time.time() - startTime))
结束语
求人不如求己。不能怕烦,代码都是人敲出来的,找不到现成的还得靠自己编。
补充
高版本的word(至少2016可行)能直接打开PDF文件并自动转换格式,多个换行能够自动识别合并为段落。所以以上代码可以自行修改简化。
纯 Python 实现的 Google 批量翻译的更多相关文章
- Python 批量翻译 使用有道api;
妹子是做翻译相关的,遇到个问题,要求得到句子中的所有单词的 音标; 有道翻译只能对单个单词翻译音标,不能对多个单词或者句子段落翻译音标; 手工一个一个翻的话那就要累死人了.....于是就让我写个翻译音 ...
- 别开心太早,Python 官方文档的翻译差远了
近几天,很多公众号发布了 Python 官方文档的消息.然而,一个特别奇怪的现象就发生了,让人啼笑皆非. Python 文档的中文翻译工作一直是“默默无闻”,几个月前,我还吐槽过这件事<再聊聊P ...
- selenium批量翻译
Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...
- 【UWP】【新坑】Excel批量翻译工具(1)
嗯……具体思路是这样的.使用的时候,你导入一个excel,直观地选择某些区域,选择语言点击翻译,就可以对多个单元格进行批量翻译,并且支持多种不同的导出格式(excel副本.txt文件……) 1,多种翻 ...
- 纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例
查看完整文章点击原文链接:纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例 你是否还在用postman\jmeter做接口自动化吗?用python的开源框架[unit ...
- django之分页,纯python代码
Django中分页 py文件代码 """ 自定义分页组件 可以返回分页的数据和分页的HTML代码 """ from django.http ...
- 关于python抓取google搜索结果的若干问题
关于python抓取google搜索结果的若干问题 前一段时间一直在研究如何用python抓取搜索引擎结果,在实现的过程中遇到了很多的问题,我把我遇到的问题都记录下来,希望以后遇到同样问题的童 ...
- google谷歌翻译插件-网页一键翻译
上个月转载的一篇博文,是推荐的四款非常实用的翻译插件,这几天看这个chrome插件网首页有新增了一个google谷歌翻译插件.我能说实话,这款插件比之前推荐的4款翻译插件更好用吗?也不能完全说是更好用 ...
- 深入理解Python中协程的应用机制: 使用纯Python来实现一个操作系统吧!!
本文参考:http://www.dabeaz.com/coroutines/ 作者:David Beazley 缘起: 本人最近在学习python的协程.偶然发现了David Beazley的co ...
随机推荐
- 《VR入门系列教程》之5---应用方向
VR应用方向 面向消费者的虚拟现实才发展了几年,就出现了大量应用程序,虚拟现实抓住了人们对未来的渴望.开发者甚至想要把整个现实世界都做成虚拟现实,这些都是可以理解的. 但是,现在仍然没 ...
- 优化 Ubuntu
优化Ubuntu 1. 更换 apt 源 echo 'deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe mul ...
- win10下nodejs的安装及配置
这里主要引用两篇文章,写的非常详细,也能解决你可能出现的问题 nodejs安装及配置 如何删除之前nodejs设置的 npm config set prefix .....
- 【Android Studio】Gradle DSL method not found:'android()'
如图所示: 参考:http://www.jianshu.com/p/d370d41fb7da 又遇到了这个问题: 参考:http://stackoverflow.com/questions/24204 ...
- IDEA 控制台输出日志无法grep
不知从何时开始,我的IDEA控制台无法直接使用Grep插件来过滤输出日志了,这个插件真的挺好用的,不知道是升级后造成的还是我自己设置错误,反正在控制台右键无法打开grep来过滤: 在我开发过程中需要这 ...
- 03-Django模型类
ORM框架:对象-关系-映射 将面向对象语言程序中的对象自动持久化到关系数据库中.本质就是将数据从一种形式转换到另外一种形式O表示Object 对象类R表示Relations 关系,关系数据库中的表M ...
- UE4 打包详细流程
这两天试着把之前做的一个UE4项目在安卓机上运行下,于是乎有了下面的一个打包血泪史. 首先呢,肯定是下载好了UE的源码了,我用的是4.18. 安装步骤可以先参考下官方的教程http://api.unr ...
- apache bench的简单使用
ApacheBench是 Apache 附带的一个小工具,专门用于 HTTP Server 的benchmark testing,可以同时模拟多个并发请求. 需要针对web做压力测试,所以简单学习了一 ...
- UEM“探针”技术及用户体验管理
随着互联网产品越来越多,用户群体越来越庞大以及用户品位的多样性增加,我们会发现这样的一个规律,就是相同类型的产品,比如播放器中的QQ影音和暴风影音,再比如小游戏平台中的腾讯游戏和联众等等,他们的功能是 ...
- Python 使用k-means方法将列表中相似的句子聚为一类
由于今年暑假在学习一些自然语言处理的东西,发现网上对k-means的讲解不是很清楚,网上大多数代码只是将聚类结果以图片的形式呈现,而不是将聚类的结果表示出来,一下代码将老师给的代码和网上的代码结合了一 ...