谷歌翻译 翻译文档爬虫

谷歌文档翻译爬虫


1.目标网站

https://translate.google.cn/?sl=auto&tl=zh-CN&op=docs

2. 操作流程 (不想看的可以直接拉到最下面的总代码)
  • 随便上传一个文档 观察接口请求 找到上传文件接口
  • 当按下翻译按钮时 发现了该请求
    在这里插入图片描述
  • 查看接口表单参数
    在这里插入图片描述
  • 后面这一大串应该就是文件内容 点一下查看源 看看有没有关键参数名可以搜索一下 搜不到再用xhr断点
    在这里插入图片描述
  • 搜一下这个f.req看看有没有
    在这里插入图片描述
  • 发现这里有一个 点进去打上断点 再重新上传文件 点击翻译 触发一下断点看看
    在这里插入图片描述
  • 这里就发现这个this.N.kd()就是这个参数 进入kd这个方法 看看是怎么操作的
    在这里插入图片描述
  • 在这个JSON.stringify这里打上断点 再次重新翻译 触发断点 看看是不是需要的
    在这里插入图片描述
  • 可以看到就是在这里生成的 查看一下this.JSON()是什么内容

在这里插入图片描述

  • 可以看出 这个数组就是文件数组 把上传的pdf文件读取为一个字节数组 然后经过JSON.stringify将json数组转为字符串 但是可以看出 我们看到的数组都是数字 是怎么转成字符串呢 这里就要注意传进去JSON.stringify的第二个参数faa 百度搜索一下JSON.stringify第二个参数是什么意思
    在这里插入图片描述
  • 点进faa这个函数
    在这里插入图片描述
  • 点进Nb这个函数
    在这里插入图片描述
  • 这里查看逻辑可以看出 这个判断了传进来的参数a 为数字 为数组等的对应处理 已知要处理的是一个文件的二进制数组 所以只用关心二进制数组是怎么在处理
    在这里插入图片描述
  • 进去这个Kb
    在这里插入图片描述
  • 继续打上断点调试 查看是不是在这里处理的数组
    在这里插入图片描述
  • 可以看到这里首先传入了大数组的第一个参数 里面包含的有这个二进制文件数组 继续下一步
    在这里插入图片描述
    在这里插入图片描述
  • 可以看到这里开始处理文件的二进制数组 进入Kb函数 看看
    在这里插入图片描述
    在这里插入图片描述
  • 看出这个a就是文件的二进制数组 这个b是一个固定的 直接把这一块代码提取出来 把b直接赋值为这个数组
Kb = function (a) {
	var b = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=']
    for (var c = Array(Math.floor(a.length / 3)), d = b[64] || "", e = 0, f = 0; e < a.length - 2; e += 3) {
        var g = a[e]
            , l = a[e + 1]
            , m = a[e + 2]
            , p = b[g >> 2];
        g = b[(g & 3) << 4 | l >> 4];
        l = b[(l & 15) << 2 | m >> 6];
        m = b[m & 63];
        c[f++] = p + g + l + m
    }
    p = 0;
    m = d;
    switch (a.length - e) {
        case 2:
            p = a[e + 1],
                m = b[(p & 15) << 2] || d;
        case 1:
            a = a[e],
                c[f] = b[a >> 2] + b[(a & 3) << 4 | p >> 4] + m + d
    }
    return c.join("")
}
  • 使用node.js运行一下这个代码 传入a参数 也就是文件的数组 看看和网站生成的是否一致
    在这里插入图片描述
    在这里插入图片描述
  • 对比可以看出生成的参数一样 写个python代码再测试一下
import struct

import execjs



js = '''
Kb = function (a) {
var b = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=']
for (var c = Array(Math.floor(a.length / 3)), d = b[64] || "", e = 0, f = 0; e < a.length - 2; e += 3) {
    var g = a[e]
        , l = a[e + 1]
        , m = a[e + 2]
        , p = b[g >> 2];
    g = b[(g & 3) << 4 | l >> 4];
    l = b[(l & 15) << 2 | m >> 6];
    m = b[m & 63];
    c[f++] = p + g + l + m
}
p = 0;
m = d;
switch (a.length - e) {
    case 2:
        p = a[e + 1],
            m = b[(p & 15) << 2] || d;
    case 1:
        a = a[e],
            c[f] = b[a >> 2] + b[(a & 3) << 4 | p >> 4] + m + d
}
return c.join("")
}'''
context = execjs.compile(js)

with open('/Users/blue/Desktop/HB_HelloFree_Foglio_informativo.pdf','rb') as f:

    f_data =f.read()
    # 读取文件为二进制数组
    f_list = struct.unpack(len(f_data)*'B',f_data)
    # 使用pyexecjs执行js代码
    result = context.call('Kb', f_list)
    print(result)
  • 测试查看结果发现一致 这里上传的参数也就完成了

  • 再来看看翻译完成后返回的参数
    在这里插入图片描述

  • 可以看出 这个返回的参数也是加密的 而且和我们之前表单里的文件参数像 那么可以推测 这个返回参数就是请求参数反过来 也就是通过一个解密算法后变成一个二进制的数组 这个二进制的数据也就是文件内容

  • 刷新网页 重新上传一个文件 然后翻译完成后 点击下载来看看
    在这里插入图片描述

  • 点一下复制下载链接 发现是这个一个链接
    blob:null/9a71c663-bcbd-4edd-8e65-93f48831f448

  • 全局搜索一下blob这个关键词 看一看
    在这里插入图片描述

  • 找到了这个地方
    在这里插入图片描述

  • 这个a就是解密过后的数组 往上 找到这个a是怎么出来的
    在这里插入图片描述

  • a是JNa这个方法的一个传参 打上断点 为了防止代码内部已经缓存了解密后的数组 这里重新刷新页面 再上传一个文件 翻译好后 点击下载 进入断点
    在这里插入图片描述
    在这里插入图片描述

  • 往上翻调用堆栈 看到了这一块代码 大胆推测 这个c就是解密算法处理后的数组 这里由于内部可能存重新赋值 所以这里看到的c变量都是经过解密算法之后的数组 打上断点 刷新页面 重新上传文件翻译 下载
    在这里插入图片描述
    在这里插入图片描述

  • 重新进入断点 这个c就是返回的参数
    在这里插入图片描述

  • 一步步向下走 发现在经过xra函数处理后就变成了数组 进入xra查看一下
    在这里插入图片描述
    在这里插入图片描述

  • 又有一个Vx函数 进入查看一下
    在这里插入图片描述

  • 这里有一个三元表达式 打上断点 重新上传 翻译下载看看
    在这里插入图片描述
    在这里插入图片描述

  • 可以看出 经过vra函数处理后 a变成了数组 进入查看
    在这里插入图片描述

  • 这里就是具体的解密代码 抠出代码

Gf = function (a) {
    return /^[\s\xa0]*$/.test(a)
}
Sa = function (a, b) {
    return -1 != a.indexOf(b)
}

Xx = function (a, b) {
    function c(m) {
        var ai = {

            '0': 52,
            '1': 53,
            '2': 54,
            '3': 55,
            '4': 56,
            '5': 57,
            '6': 58,
            '7': 59,
            '8': 60,
            '9': 61,
            '+': 62,
            '-': 62,
            '.': 64,
            '/': 63,
            '=': 64,
            'A': 0,
            'B': 1,
            'C': 2,
            'D': 3,
            'E': 4,
            'F': 5,
            'G': 6,
            'H': 7,
            'I': 8,
            'J': 9,
            'K': 10,
            'L': 11,
            'M': 12,
            'N': 13,
            'O': 14,
            'P': 15,
            'Q': 16,
            'R': 17,
            'S': 18,
            'T': 19,
            'U': 20,
            'V': 21,
            'W': 22,
            'X': 23,
            'Y': 24,
            'Z': 25,
            'a': 26,
            'b': 27,
            'c': 28,
            'd': 29,
            'e': 30,
            'f': 31,
            'g': 32,
            'h': 33,
            'i': 34,
            'j': 35,
            'k': 36,
            'l': 37,
            'm': 38,
            'n': 39,
            'o': 40,
            'p': 41,
            'q': 42,
            'r': 43,
            's': 44,
            't': 45,
            'u': 46,
            'v': 47,
            'w': 48,
            'x': 49,
            'y': 50,
            'z': 51,
            '_': 63
        }
        for (; d < a.length;) {
            var p = a.charAt(d++)
                , q = ai[p];
            if (null != q)
                return q;
            if (!Gf(p))
                throw Error("G`" + p);
        }
        return m
    }

    for (var d = 0; ;) {
        var e = c(-1)
            , f = c(0)
            , g = c(64)
            , l = c(64);
        if (64 === l && -1 === e)
            break;
        b(e << 2 | f >> 4);
        64 != g && (b(f << 4 & 240 | g >> 2),
        64 != l && b(g << 6 & 192 | l))
    }
}

vra = function (a) {
    var b = a.length
        , c = 3 * b / 4;
    c % 3 ? c = Math.floor(c) : Sa("=.", a[b - 1]) && (c = Sa("=.", a[b - 2]) ? c - 2 : c - 1);
    var d = new Uint8Array(c)
        , e = 0;
    Xx(a, function (f) {
        d[e++] = f
    });
    return e !== c ? d.subarray(0, e) : d
}


  • 在抠出和补全时 在_.Xx函数时 里面执行了一行_.tca() 这个方法内部也就是为了将_.ai变量赋值 生成一个对照字典 只需要打上断点 就知道_.ai这个在_.tca执行完成后是一个什么值 直接写死赋值 然后剔除这一行的执行 便可以得到上述代码中的Xx的方法
    在这里插入图片描述

  • 到了这里 再写个python代码测试下 把解密后的数组写入文件看看是不是翻译好的
js = '''
Gf = function (a) {
    return /^[\s\xa0]*$/.test(a)
}
Sa = function (a, b) {
    return -1 != a.indexOf(b)
}

Xx = function (a, b) {
    function c(m) {
        var ai = {

            '0': 52,
            '1': 53,
            '2': 54,
            '3': 55,
            '4': 56,
            '5': 57,
            '6': 58,
            '7': 59,
            '8': 60,
            '9': 61,
            '+': 62,
            '-': 62,
            '.': 64,
            '/': 63,
            '=': 64,
            'A': 0,
            'B': 1,
            'C': 2,
            'D': 3,
            'E': 4,
            'F': 5,
            'G': 6,
            'H': 7,
            'I': 8,
            'J': 9,
            'K': 10,
            'L': 11,
            'M': 12,
            'N': 13,
            'O': 14,
            'P': 15,
            'Q': 16,
            'R': 17,
            'S': 18,
            'T': 19,
            'U': 20,
            'V': 21,
            'W': 22,
            'X': 23,
            'Y': 24,
            'Z': 25,
            'a': 26,
            'b': 27,
            'c': 28,
            'd': 29,
            'e': 30,
            'f': 31,
            'g': 32,
            'h': 33,
            'i': 34,
            'j': 35,
            'k': 36,
            'l': 37,
            'm': 38,
            'n': 39,
            'o': 40,
            'p': 41,
            'q': 42,
            'r': 43,
            's': 44,
            't': 45,
            'u': 46,
            'v': 47,
            'w': 48,
            'x': 49,
            'y': 50,
            'z': 51,
            '_': 63
        }
        for (; d < a.length;) {
            var p = a.charAt(d++)
                , q = ai[p];
            if (null != q)
                return q;
            if (!Gf(p))
                throw Error("G`" + p);
        }
        return m
    }

    for (var d = 0; ;) {
        var e = c(-1)
            , f = c(0)
            , g = c(64)
            , l = c(64);
        if (64 === l && -1 === e)
            break;
        b(e << 2 | f >> 4);
        64 != g && (b(f << 4 & 240 | g >> 2),
        64 != l && b(g << 6 & 192 | l))
    }
}

vra = function (a) {
    var b = a.length
        , c = 3 * b / 4;
    c % 3 ? c = Math.floor(c) : Sa("=.", a[b - 1]) && (c = Sa("=.", a[b - 2]) ? c - 2 : c - 1);
    var d = new Uint8Array(c)
        , e = 0;
    Xx(a, function (f) {
        d[e++] = f
    });
    return e !== c ? d.subarray(0, e) : d
}

'''

# 由于字符串太长就不放上来了 这里的字符串就是翻译后接口返回的那一行文件内容
s = ''
context = execjs.compile(js)
# 执行js的vra方法得到解密后的文件二进制数组
result = context.call('vra', s)
# 将二进制数组写入pdf文件
with open('new.pdf', 'wb') as f:
    for i in result.values():
    
        a = struct.pack('B', i)
        f.write(a)
  • 测试后可以发现该文件就是翻译后的文件 自此上传的文件参数和返回的翻译后的文件参数就都搞定了

  • 其他的那些控制翻译语言的自己控制一下就可以了

3. 总代码
# -*- coding: utf-8 -*-
# @Time: 2022/4/25
# Author: Blue
import re
import struct

import execjs
import requests


class GoogleTranslation:
    def __init__(self):
        self.s = requests.session()

    def encrypt(self, s):
        js = '''
        Kb = function (a) {
        var b = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=']
        for (var c = Array(Math.floor(a.length / 3)), d = b[64] || "", e = 0, f = 0; e < a.length - 2; e += 3) {
            var g = a[e]
                , l = a[e + 1]
                , m = a[e + 2]
                , p = b[g >> 2];
            g = b[(g & 3) << 4 | l >> 4];
            l = b[(l & 15) << 2 | m >> 6];
            m = b[m & 63];
            c[f++] = p + g + l + m
        }
        p = 0;
        m = d;
        switch (a.length - e) {
            case 2:
                p = a[e + 1],
                    m = b[(p & 15) << 2] || d;
            case 1:
                a = a[e],
                    c[f] = b[a >> 2] + b[(a & 3) << 4 | p >> 4] + m + d
        }
        return c.join("")
        }'''
        context = execjs.compile(js)
        return context.call('Kb', s)

    def decrypt(self, s):
        js = '''
        Gf = function (a) {
            return /^[\s\xa0]*$/.test(a)
        }
        Sa = function (a, b) {
            return -1 != a.indexOf(b)
        }

        Xx = function (a, b) {
            function c(m) {
                var ai = {

                    '0': 52,
                    '1': 53,
                    '2': 54,
                    '3': 55,
                    '4': 56,
                    '5': 57,
                    '6': 58,
                    '7': 59,
                    '8': 60,
                    '9': 61,
                    '+': 62,
                    '-': 62,
                    '.': 64,
                    '/': 63,
                    '=': 64,
                    'A': 0,
                    'B': 1,
                    'C': 2,
                    'D': 3,
                    'E': 4,
                    'F': 5,
                    'G': 6,
                    'H': 7,
                    'I': 8,
                    'J': 9,
                    'K': 10,
                    'L': 11,
                    'M': 12,
                    'N': 13,
                    'O': 14,
                    'P': 15,
                    'Q': 16,
                    'R': 17,
                    'S': 18,
                    'T': 19,
                    'U': 20,
                    'V': 21,
                    'W': 22,
                    'X': 23,
                    'Y': 24,
                    'Z': 25,
                    'a': 26,
                    'b': 27,
                    'c': 28,
                    'd': 29,
                    'e': 30,
                    'f': 31,
                    'g': 32,
                    'h': 33,
                    'i': 34,
                    'j': 35,
                    'k': 36,
                    'l': 37,
                    'm': 38,
                    'n': 39,
                    'o': 40,
                    'p': 41,
                    'q': 42,
                    'r': 43,
                    's': 44,
                    't': 45,
                    'u': 46,
                    'v': 47,
                    'w': 48,
                    'x': 49,
                    'y': 50,
                    'z': 51,
                    '_': 63
                }
                for (; d < a.length;) {
                    var p = a.charAt(d++)
                        , q = ai[p];
                    if (null != q)
                        return q;
                    if (!Gf(p))
                        throw Error("G`" + p);
                }
                return m
            }

            for (var d = 0; ;) {
                var e = c(-1)
                    , f = c(0)
                    , g = c(64)
                    , l = c(64);
                if (64 === l && -1 === e)
                    break;
                b(e << 2 | f >> 4);
                64 != g && (b(f << 4 & 240 | g >> 2),
                64 != l && b(g << 6 & 192 | l))
            }
        }

        vra = function (a) {
            var b = a.length
                , c = 3 * b / 4;
            c % 3 ? c = Math.floor(c) : Sa("=.", a[b - 1]) && (c = Sa("=.", a[b - 2]) ? c - 2 : c - 1);
            var d = new Uint8Array(c)
                , e = 0;
            Xx(a, function (f) {
                d[e++] = f
            });
            return e !== c ? d.subarray(0, e) : d
        }

        '''
        context = execjs.compile(js)
        return context.call('vra', s)

    def upload(self, file_path):
        with open(file_path, 'rb') as f:
            f_data = f.read()
            f_list = struct.unpack(len(f_data) * 'B', f_data)
            f_result = self.encrypt(f_list)
		
		# 请求头就自己补一个上去就行
        headers = {}

        params = (
            ('rpcids', 'LBEnTe'),
            ('source-path', '/'),
            ('f.sid', '4751612718593065407'),
            ('bl', 'boq_translate-webserver_20220420.10_p0'),
            ('hl', 'zh-CN'),
            ('soc-app', '1'),
            ('soc-platform', '1'),
            ('soc-device', '1'),
            ('_reqid', '450185'),
            ('rt', 'c'),
        )

        data = {
            'f.req': f'[[["LBEnTe","[[\\"{f_result}\\",\\"application/pdf\\"],\\"auto\\",\\"zh-CN\\"]",null,"generic"]]]',
            '': ''
        }

        response = requests.post('https://translate.google.cn/_/TranslateWebserverUi/data/batchexecute',
                                 headers=headers, params=params, data=data)
        return response.text

    def save(self, s, save_path):
        f_data = re.search('"\[\[\\\\"(.*?)\\\\",', s).group(1)
        f_data = self.decrypt(f_data)
        with open(save_path, 'wb') as f:
            for i in f_data.values():
                a = struct.pack('B', i)
                f.write(a)



if __name__ == '__main__':
    gg = GoogleTranslation()
    r = gg.upload('/Users/Desktop/test.pdf')
    gg.save(r, '/Users/Desktop/new.pdf')

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Python爬虫谷歌翻译是一个功能强大的工具,可以实现对Excel文件和PDF文件中的内容进行翻译。该爬虫支持对Excel文件进行直接翻译,也可以将PDF文件中的表格转化为Excel再进行翻译。支持的Excel文件格式包括xlsx和xlsm等。\[1\] 使用该爬虫的方法及注意事项如下:首先,将需要翻译的PDF文件放入translate_pdf_to_text_to_word.exe相同的路径下。然后双击translate_pdf_to_text_to_word.exe开始运行。在命令窗口中会提示是否开始将PDF文件转化为txt文件,如果没有txt文件的话,需要按下1生成txt文件。生成的txt文件中的每句话可能会被分成几段,需要手动打开txt文件对段落格式进行调整。按下1后,会提示是否开始翻译,按下y后就开始进行爬虫翻译(使用谷歌翻译),按下3后结束程序。\[2\] 该爬虫对大众语言的翻译效果较好,但对一些偏门的语言可能存在问题。爬虫使用字符串截取的方式解析谷歌的返回数据,因此对于某些语言或矩阵可能会存在截取的问题,但只需修改translate方法中的参数n即可解决。\[3\] #### 引用[.reference_title] - *1* *2* [Python实现谷歌翻译爬虫翻译PDF,翻译Excel,支持excel文档打开翻译,支持xlsx,xlsm等格式。](https://blog.csdn.net/weixin_48223757/article/details/126157914)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [使用Python爬虫调用谷歌翻译【21年8月】](https://blog.csdn.net/qq_40846669/article/details/119926079)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值