用 python 调用百度翻译 v2transapi 接口

一、获得接口 url

  1. 打开百度翻译:百度翻译
  2. 右键检查 或者 F12 打开控制台、点击网络这一项:
    网络
  3. 输入文字:哈哈,点击图中的过滤按钮,选择全部
    在这里插入图片描述
    可以看到,v2transapi?from=zh&to=jp 请求的预览中就是我们需要的响应数据:
    响应
  4. 同时看到 headers 中有我们的 Cookie 字段:
    Cookie

二、查看POST表单格式

因为是POST请求,点击载荷看到我们的表单格式:
在这里插入图片描述

三、python 访问百度翻译 v2transapi 接口初试

将之前获得的 cookie、token 填入:

import json
import urllib.request
import urllib.parse

# 第一步中咱们知道接口是这个 url
url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=jp'
# headers 必须带 Cookie,百度翻译的反扒手段之一
headers = {
    'Cookie': 你的 cookie
}
# 要翻译的单词
query = '哈哈'
# 表单数据,将第二步中的kv全部复制过来
data = {
    'from': 'zh',
    'to': 'jp',
    'query': query,
    'simple_means_flag': '3',
    'sign':404035.182642,
    'token': 你的 token,
    'domain': 'common'
}
# 封装 request 对象
data = urllib.parse.urlencode(data).encode('utf-8')
request = urllib.request.Request(url=url, data=data, headers=headers)
# 调用 urlopen,获得返回值
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# 用 json 可以解码返回值中的 Unicode 字符
print(json.loads(content))

运行结果:
在这里插入图片描述
看起来成功了!换个单词翻译试试:将 query 改为 啦啦啦 再运行:
在这里插入图片描述
居然是未知错误,观察我们的表单数据发现有个特殊字段:'sign': ‘404035.182642’,所以我们需要知道这个 sign 字段是如何生成的

四、探究原因

重新查看我们控制台中的接口:
在这里插入图片描述
启动器中可以看到所有的调用栈,点击第一个不是 jquery 的 js:@ translate.js:181
在这里插入图片描述
可以发现该方法正是我们的 $.ajax 方法,我们得知 data 的变量名为 paramData,在该文件内摁 ctrl+f 搜索这个变量:
在这里插入图片描述
发现 sign 是调用了 pGrab( qeury ) 方法:这个 qeury 正是我们的待翻译文字。我们 ctrl+f 搜索 pGrab 发现是导入别的 js 文件中的方法:

在这里插入图片描述

全局搜索该文件:点击控制台左边这三个点 - 打开文件,或者 ctrl+p。
在这里插入图片描述
在这里插入图片描述
翻到文件末尾看到导出的方法是 tl,所以实质 sign = tl( query ),右键该文件点击另存为,将 pGrab.js 文件放入我们的项目路径。

五、python 访问百度翻译 v2transapi 接口再试

现在的思路就是需要在 python 中调用该 js 中的 tl( query ) 方法生成我们的 sign,此时我们需要导入 execjs 模块(需要 nodejs 环境:本人版本 v14.0.0)

  1. 安装

    pip install PyExecJS 
    
  2. 导入:

    import execjs
    
  3. 使用:

    query = '哈哈'
    with open('pGrab.js', mode='r', encoding='utf-8') as f:
    	sign = execjs.compile(f.read()).call("tl", query)
    print(sign)
    
  4. 运行
    在这里插入图片描述
    提示 window 未定义,在 pGrab.js 中搜索发现只有一处使用了该变量:
    在这里插入图片描述
    现在的目标就变成了如何知道这个 window “长什么样”

  5. 此时我们可以利用浏览器的调试功能,直接定位到这行代码查看 window 变量!
    在图示这行打上断点:
    在这里插入图片描述
    然后在翻译框随便输入,js 会暂停在断点这行。鼠标指上 window 变量,在里面寻找 key = gtk
    在这里插入图片描述
    因为我们通过搜索 pGrab.js 知道只用了 window 中的这一个 kv,所以我们在项目中的 pGrap.js 文件首行添加

    window = { 'gtk': '320305.131321201' }
    
  6. 重新运行我们第3步的代码:
    在这里插入图片描述
    对比我们之前的 query = '哈哈' 对应的 sign 值是同一个值:
    在这里插入图片描述
    问题全部解决了,上代码最终版!

六、代码最终版

import json
import urllib.request
import urllib.parse
import execjs

# 第一步中咱们知道接口是这个 url
url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=jp'
# headers 必须带 Cookie,百度翻译的反扒手段之一
headers = {
    'Cookie': 你的 cookie
}
# 要翻译的单词
query = '哈哈'
# 生成 sign
with open('pGrab.js', mode='r', encoding='utf-8') as f:
    sign = execjs.compile(f.read()).call("tl", query)
print(sign)
# 表单数据,将第二步中的kv全部复制过来
data = {
    'from': 'zh',
    'to': 'jp',
    'query': query,
    'simple_means_flag': '3',
    'sign': sign,
    'token': 你的 token,
    'domain': 'common'
}
# 封装 request 对象
data = urllib.parse.urlencode(data).encode('utf-8')
request = urllib.request.Request(url=url, data=data, headers=headers)
# 调用 urlopen,获得返回值
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# 用 json 可以解码返回值中的 Unicode 字符
print(json.loads(content))

运行结果:
在这里插入图片描述
pGrab.js

window = {'gtk': '320305.131321201'};

function rl(num, rule) {
    for (let i = 0; i < rule.length - 2; i += 3) {
        let d = rule.charAt(i + 2);
        d = 'a' <= d ? d.charCodeAt(0) - 87 : Number(d);
        d = '+' === rule.charAt(i + 1) ? num >>> d : num << d;
        num = '+' === rule.charAt(i) ? num + d & 4294967295 : num ^ d;
    }
    return num;
}

let gtk = null;

function tl(query) {
    // 非 BMP 的 unicode 在 js 会以高低位保存在,导致 string.length 返回是 2,与后端不一致
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
    let noBMPChar = query.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g);
    if (noBMPChar === null) {
        let qLen = query.length;
        if (qLen > 30) {
            query = `${query.substr(0, 10)}${query.substr(Math.floor(qLen / 2) - 5, 10)}${query.substr(-10, 10)}`;
        }
    } else {
        let bmpPart = query.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/);
        let i = 0;
        let len = bmpPart.length;
        let qArray = [];
        for (; i < len; i++) {
            if (bmpPart[i] !== '') {
                qArray.push(...bmpPart[i].split(''));
            }
            if (i !== len - 1) {
                qArray.push(noBMPChar[i]);
            }
        }
        let qLen = qArray.length;
        // query 截取
        if (qLen > 30) {
            // query = `${query.substr(0, 10)}${query.substr(Math.floor(qLen / 2) - 5, 10)}${query.substr(-10, 10)}`;
            query = qArray.slice(0, 10).join('')
                + qArray.slice(Math.floor(qLen / 2) - 5, Math.floor(qLen / 2) + 5).join('')
                + qArray.slice(-10).join('');
        }
    }
    let tk;
    // key - gtk
    let key = `${String.fromCharCode(103)}${String.fromCharCode(116)}${String.fromCharCode(107)}`;

    if (gtk !== null) {
        tk = gtk;
    } else {
        tk = (gtk = window[key] || '') || '';
    }

    let tkArr = tk.split('.');
    let tk0 = Number(tkArr[0]) || 0;
    let tk1 = Number(tkArr[1]) || 0;

    let e = [];
    for (let f = 0, g = 0; g < query.length; g++) {
        let ucode = query.charCodeAt(g);
        // 将 unicode 对应 utf8 各字节存入 array e
        if (ucode < 128) {
            // utf 编码为变字节,参考 http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
            // 单字节 操作
            e[f++] = ucode;
        } else {
            if (ucode < 2048) {
                // 双字节 操作
                e[f++] = ucode >> 6 | 192;
            } else {
                // charCodeAt 总是返回一个小于 65,536 的值。
                // 这是因为高位编码单元(higher code point)使用一对(低位编码(lower valued))代理伪字符("surrogate" pseudo-characters)来表示,从而构成一个真正的字符。
                // 因此,为了查看或复制(reproduce)65536 及以上编码字符的完整字符,需要在获取 charCodeAt(i) 的值的同时获取 charCodeAt(i+1) 的值
                // 貌似是因为一个 char 两个字节(16位),支持 unicode \u0000 - \uffff
                if (55296 === (ucode & 64512)
                    && g + 1 < query.length
                    && 56320 === (query.charCodeAt(g + 1) & 64512)
                ) {
                    // 四字节 操作
                    ucode = 65536 + ((ucode & 1023) << 10) + (query.charCodeAt(++g) & 1023);
                    e[f++] = ucode >> 18 | 240;
                    e[f++] = ucode >> 12 & 63 | 128;
                } else {
                    // 三字节 操作
                    e[f++] = ucode >> 12 | 224;
                }
                e[f++] = ucode >> 6 & 63 | 128;
            }
            // 末字节
            e[f++] = ucode & 63 | 128;
        }
    }

    let rlt = tk0;
    // let rule1 = '+-a^+6';
    let rule1 = `${String.fromCharCode(43)}${String.fromCharCode(45)}${String.fromCharCode(97)}`
        + `${String.fromCharCode(94)}${String.fromCharCode(43)}${String.fromCharCode(54)}`;
    // let rule2 = '+-3^+b+-f'
    let rule2 = `${String.fromCharCode(43)}${String.fromCharCode(45)}${String.fromCharCode(51)}`
        + `${String.fromCharCode(94)}${String.fromCharCode(43)}${String.fromCharCode(98)}`
        + `${String.fromCharCode(43)}${String.fromCharCode(45)}${String.fromCharCode(102)}`;
    for (let i = 0; i < e.length; i++) {
        rlt += e[i];
        // +-a^+6
        rlt = rl(rlt, rule1);
    }
    rlt = rl(rlt, rule2);
    rlt ^= tk1;
    rlt < 0 && (rlt = (rlt & 2147483647) + 2147483648);
    rlt %= 1E6;
    return `${rlt.toString()}.${rlt ^ tk0}`;
}

七、补充

scrapy 访问百度翻译 v2transapi 接口

settings.py 中注释掉:

# ROBOTSTXT_OBEY = True

新建 spider:

import scrapy
import execjs


class BaiduFanyiSpider(scrapy.Spider):
    name = 'baidu_fanyi'
    allowed_domains = ['fanyi.baidu.com']
	# 必须注释
    # start_urls = ['https://fanyi.baidu.com/v2transapi']
	# 必须改名
    def parse_second(self, response):
        print(response.json())
        pass

    def start_requests(self):
        url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=jp'
        query = '哈哈'

        with open('pGrab.js', mode='r', encoding='utf-8') as f:
            sign = execjs.compile(f.read()).call("tl", query)

        # print(sign)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
        }
        cookies = {
            将 cookies 按分号拆开,等号改写成 kv 形式,例如:
            'BIDUPSID': xxx,
            ...
        }

        data = {
            'from': 'zh',
            'to': 'jp',
            'query': query,
            'simple_means_flag': '3',
            'sign': sign,
            'token': 你的token,
            'domain': 'common'
        }

        yield scrapy.FormRequest(url=url, headers=headers, formdata=data, cookies=cookies, callback=self.parse_second)

运行结果:
在这里插入图片描述

完结撒花

  • 16
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
可以使用百度翻译API提供的HTTP接口实现Python调用百度翻译的翻译接口。具体步骤如下: 1. 首先,需要在百度翻译API官网申请API Key和Secret Key,获取访问API的权限。 2. 在Python中使用requests库向API发送HTTP请求,并传入必要的参数和密钥信息。具体参数包括: - q:待翻译的文本 - from:源语言,可选,默认为auto - to:目标语言,必填 - appid:API Key - salt:随机数 - sign:签名,由app id + 待翻译文本 + salt + secret key进行MD5加密后得到 3. 解析API返回的JSON格式结果,获取翻译结果。 下面是一个简单的示例代码: ```python import requests import hashlib import random import json def translate(text, from_lang='auto', to_lang='en'): appid = 'your app id' secret_key = 'your secret key' salt = str(random.randint(32768, 65536)) sign = hashlib.md5((appid + text + salt + secret_key).encode('utf-8')).hexdigest() url = 'http://api.fanyi.baidu.com/api/trans/vip/translate' params = { 'q': text, 'from': from_lang, 'to': to_lang, 'appid': appid, 'salt': salt, 'sign': sign } response = requests.get(url, params=params) result = json.loads(response.text) return result['trans_result'][0]['dst'] ``` 在上面的代码中,我们定义了一个translate函数,它接受三个参数:待翻译的文本、源语言和目标语言。我们先计算出sign值,再将参数传入requests库的get方法中,向API发送HTTP请求。最后,我们解析API返回的JSON结果,并返回翻译结果。 注意:百度翻译API的免费使用有次数限制,如果需要更多的翻译次数,需要付费购买。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值