Python 实现的 Google 批量翻译功能

yipeiwu_com6年前Python基础

首先声明,没有什么不良动机,因为经常会用 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))

总结

以上所述是小编给大家介绍的Python 实现的 Google 批量翻译功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对【听图阁-专注于Python设计】网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

相关文章

Python3基于sax解析xml操作示例

Python3基于sax解析xml操作示例

本文实例讲述了Python3基于sax解析xml操作。分享给大家供大家参考,具体如下: python使用SAX解析xml SAX是一种基于事件驱动的API。 利用SAX解析XML文档牵涉...

Python3之手动创建迭代器的实例代码

迭代器即可以遍历诸如列表,字典及字符串等序列对象甚至自定义对象的对象,其本质就是记录迭代对象中每个元素的位置。迭代过程从第一个元素至最后一个元素,且过程不能回滚或反方向迭代。 两个基本方...

Python统计单词出现的次数

题目: 统计一个文件中每个单词出现的次数,列出出现频率最多的5个单词。 前言: 这道题在实际应用场景中使用比较广泛,比如统计历年来四六级考试中出现的高频词汇,记得李笑来就利用他的编程技能...

关于Python形参打包与解包小技巧分享

Python中的函数调用与c++不同的是将this指针直接作为self当作第一个形参进行处理,从而将静态函数与实例方法的调用形式统一了起来。在实际编程过程中,可以通过传递函数的地址、函数...

对Python3中dict.keys()转换成list类型的方法详解

对Python3中dict.keys()转换成list类型的方法详解

在python3中使用dict.keys()返回的不在是list类型了,也不支持索引,我们可以看一下下面这张图片 那么我们应该怎么办呢,其实解决的方法也是非常简单的,只需要使用list...