测试通过时间: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 批量翻译的更多相关文章

  1. Python 批量翻译 使用有道api;

    妹子是做翻译相关的,遇到个问题,要求得到句子中的所有单词的 音标; 有道翻译只能对单个单词翻译音标,不能对多个单词或者句子段落翻译音标; 手工一个一个翻的话那就要累死人了.....于是就让我写个翻译音 ...

  2. 别开心太早,Python 官方文档的翻译差远了

    近几天,很多公众号发布了 Python 官方文档的消息.然而,一个特别奇怪的现象就发生了,让人啼笑皆非. Python 文档的中文翻译工作一直是“默默无闻”,几个月前,我还吐槽过这件事<再聊聊P ...

  3. selenium批量翻译

    Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...

  4. 【UWP】【新坑】Excel批量翻译工具(1)

    嗯……具体思路是这样的.使用的时候,你导入一个excel,直观地选择某些区域,选择语言点击翻译,就可以对多个单元格进行批量翻译,并且支持多种不同的导出格式(excel副本.txt文件……) 1,多种翻 ...

  5. 纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例

    查看完整文章点击原文链接:纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例 你是否还在用postman\jmeter做接口自动化吗?用python的开源框架[unit ...

  6. django之分页,纯python代码

    Django中分页 py文件代码 """ 自定义分页组件 可以返回分页的数据和分页的HTML代码 """ from django.http ...

  7. 关于python抓取google搜索结果的若干问题

    关于python抓取google搜索结果的若干问题     前一段时间一直在研究如何用python抓取搜索引擎结果,在实现的过程中遇到了很多的问题,我把我遇到的问题都记录下来,希望以后遇到同样问题的童 ...

  8. google谷歌翻译插件-网页一键翻译

    上个月转载的一篇博文,是推荐的四款非常实用的翻译插件,这几天看这个chrome插件网首页有新增了一个google谷歌翻译插件.我能说实话,这款插件比之前推荐的4款翻译插件更好用吗?也不能完全说是更好用 ...

  9. 深入理解Python中协程的应用机制: 使用纯Python来实现一个操作系统吧!!

    本文参考:http://www.dabeaz.com/coroutines/   作者:David Beazley 缘起: 本人最近在学习python的协程.偶然发现了David Beazley的co ...

随机推荐

  1. 个人永久性免费-Excel催化剂功能第60波-数据有效性验证增强版,补足Excel天生不足

    Excel在数据处理.数据分析上已经是公认的最好用的软件之一,其易用性和强大性也吸引无数的初中高级用户每天都在使用Excel.但这些优点的同时,也带出了一些问题,正因为其不同于一般的专业软件,需要专业 ...

  2. oracle总结: INTERVAL DAY TO SECOND, 但却获得 NUMBER

    遇到问题: INTERVAL DAY TO SECOND, 但却获得 NUMBER 操作步骤 在查询oracle数据库的时候使用到了系统提供的函数:trunc ( trunc(tb.safedate ...

  3. 2019暑假集训 BLO

    题目描述 Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通. 输入 输入n<=100000 ...

  4. 黑羽压测 做 API接口功能测试

    做功能测试 黑羽压测 是一款性能测试工具,但是我们也可以使用它,很方便的做 API接口功能测试 . 点击下方链接,观看 讲解视频 https://www.bilibili.com/video/av60 ...

  5. css常用语法续集

    1   设置字体  body{font-familly:“宋体”} 2 可以使用下面代码设置网页中文字的字号为12像素,并把字体颜色设置为#666(灰色): body{font-size:12px;c ...

  6. Reporting报表开发知识合并[个人原创]

    [个人原创] ,转发请声明原文链接 了解 a)      SSRS全称 SQL Server Reporting Services,是依赖于数据库运行的,是微软开发的重量级别的BI产品 b)      ...

  7. javaweb入门--web是什么

    WEB基本概念 首先明白web是什么,人们常说的web其实就是英文中页面的意思,准确来说是Internet主机供给外界访问的资源. 站在用户视角,也就是说,通过你浏览器(客户端)的地址栏输入资源(你要 ...

  8. CDN绕过姿势小结

    公司的各业务主站都挂了CDN,总结一波CDN绕过技巧. 什么是CDN CDN的全称是Content Delivery Network,即内容分发网络. 其基本思路是尽可能避开互联网上有可能影响数据传输 ...

  9. MyBatis在Spring环境下的事务管理

    MyBatis的设计思想很简单,可以看做是对JDBC的一次封装,并提供强大的动态SQL映射功能.但是由于它本身也有一些缓存.事务管理等功能,所以实际使用中还是会碰到一些问题--另外,最近接触了JFin ...

  10. Codeforces Round #192 (Div. 2) (330A) A. Cakeminator

    题意: 如果某一行没有草莓,就可以吃掉这一行,某一列没有也可以吃点这一列,求最多会被吃掉多少块蛋糕. //cf 192 div2 #include <stdio.h> #include & ...