python requests 抓取百度翻译 js破解 最新版
上一篇文章写了有道翻译抓取,朋友圈的大佬说试试抓取百度翻译!,于是我就抱着试一试的心态开始写了
目标网站:https://fanyi.baidu.com/?aldtype=16047
1.寻找数据
F12进去NetWork 调试多次,发现翻译后的数据放在json里
2.找出接口区别
下图(中翻英)接口下
下图是(日翻英)的接口
接口为:
https://fanyi.baidu.com/v2transapi?from={需要翻译的文字语言}&to={翻译成什么语言}
这两个参数的值会变化
3.分析表单
很明显该请求是POST类型,
发现 sign是随着输入不同而变化,相同输入生成的值相同(重点)。
token值一直不变,先找出来,全局搜索
观察发现,下面的langList不就是语言代码的一个映射吗。将他保存下来方便使用。
from和to很明显,跟你输入和想要翻译的结果语言对应。
query是需要翻译的内容,
其他几个参数都是不变的。
4. 破解js加密找出sign值
1.全局搜索sign
2.打断点-跟踪函数
可以看出来这是应该ajax请求,w里面的参数就是我们要找的参数。
在Sourth中找到刚才全局搜索到的js文件,然后搜索界面、打断点
鼠标悬停在f(n)上,会出现一个跳转,点击可以跳转到他的函数那里。
跳转后的函数为:
下一步:
扣代码生成sign值
function e(r){
var o=r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g);
if(null===o){
var t=r.length;t>30&&(r=""+r.substr(0,10)+r.substr(Math.floor(t/2)-5,10)+r.substr(-10,10))
}else{
for(var e=r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/),C=0,h=e.length,f=[];
h>C;C++)""!==e[C]&&f.push.apply(f,a(e[C].split(""))),C!==h-1&&f.push(o[C]);
var g=f.length;
g>30&&(r=f.slice(0,10).join("")+f.slice(Math.floor(g/2)-5,Math.floor(g/2)+5).join("")+f.slice(-10).join(""))
}
var u=void 0,
l=""+String.fromCharCode(103)+String.fromCharCode(116)+String.fromCharCode(107);u=null!==i?i:(i=window[l]||"")||"";
for(var d=u.split("."),m=Number(d[0])||0,s=Number(d[1])||0,S=[],c=0,v=0;v<r.length;v++){
var A=r.charCodeAt(v);
128>A?S[c++]=A:(2048>A?S[c++]=A>>6|192:(55296===(64512&A)&&v+1<r.length&&56320===(64512&r.charCodeAt(v+1))?(A=65536+((1023&A)<<10)+(1023&r.charCodeAt(++v)),S[c++]=A>>18|240,
S[c++]=A>>12&63|128):S[c++]=A>>12|224,
S[c++]=A>>6&63|128),S[c++]=63&A|128)
}
for(var p=m,F=""+String.fromCharCode(43)+String.fromCharCode(45)+String.fromCharCode(97)+(""+String.fromCharCode(94)+String.fromCharCode(43)+String.fromCharCode(54)),D=""+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)),b=0;b<S.length;b++)
p+=S[b],p=n(p,F);
return p=n(p,D),p^=s,0>p&&(p=(2147483647&p)+2147483648),
p%=1e6,p.toString()+"."+(p^m)
}
通过Python中的 PyExecJS 模块执行 js代码
首先安装:
执行命令:pip install PyExecJS
如果下载超时,执行pip install -i https://pypi.douban.com/simple PyExecJS
执行代码:
with open('bd_sign.js', 'r', encoding='utf-8') as f:
ctx = execjs.compile(f.read())
sign = ctx.call('e', 'query')
print(sign)
报错了 显示’i’未定义
这时我们返回浏览器中继续加断点调试 查看 i 值 是怎么生成的,在289行发发现i u = null !== i ? i : (i = window[l] || “”) || “”; 的,经过过多次尝试发现这里的i值是不变的,声明一下 i
再一次执行代码,有出现类似的错误,
检查js代码发现 变量n 未声明, 继续返回断点调试的地方,在function e®{}的前面发现function n(r,o){},将其加到代码顶端,
5.大致构建请求
想必逻辑已经很明了吧,难点就是js破解,已经解决。
百度翻译和有道翻译请求头headers都要传个Cookie ,可能是客户端的标识,不添加的话请求失败。
6.实现代码
import requests
import execjs
class baiDuFy(object):
def __init__(self, msg, comef, to):
self.initial_url = 'https://fanyi.baidu.com/?aldtype=16047'
# 从'comef'语言翻译为'to'语言
self.comef = comef
self.to = to
self.url = 'https://fanyi.baidu.com/v2transapi?from={comef}&to={to}'.format(comef=self.comef, to=self.to)
self.msg = msg
def get_sign(self):
with open('bd_sign.js', 'r', encoding='utf-8') as f:
ctx = execjs.compile(f.read())
sign = ctx.call('e', self.msg)
return sign
def get_result(self):
data = {
# 翻译内容语言
'from': self.comef,
# 翻译后的语言
'to': self.to,
# 翻译内容
'query': self.msg,
# 固定值
'transtype': 'translang',
# 固定值
'simple_means_flag': 3,
# js生成(难点)
'sign': self.get_sign(),
# 固定值
'token': '2446d432cd4cc5a879d46fd54300e38c',
# 固定值
'domain': 'common'
}
# X-Requested-With': 'XMLHttpRequest' 该请求是AJAX 异步请求,否则为同步请求, Cookie可以理解为客户端的标识,有效时间应该很长
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/88.0.4324.150 Safari/537.36',
'Referer': self.initial_url,
'X-Requested-With': 'XMLHttpRequest',
'Cookie': 'BIDUPSID=CA299524D71B0671D693C1DFA2B437CA; PSTM=1601733661;'
' BAIDUID=CA299524D71B0671AE76F7C420FBBDE7:FG=1',
}
resp = requests.post(self.url, data=data, headers=headers)
return resp.json()['trans_result']['data'][0]['dst']
# 语言代码
lang_dict = {
'zh': '中文', 'jp': '日语', 'jpka': '日语假名', 'th': '泰语', 'fra': '法语', 'en': '英语', 'spa': '西班牙语', 'kor': '韩语',
'tr': '土耳其语', 'vie': '越南语', 'ms': '马来语', 'de': '德语', 'ru': '俄语', 'ir': '伊朗语', 'ara': '阿拉伯语', 'est': '爱沙尼亚语',
'be': '白俄罗斯语', 'bul': '保加利亚语', 'hi': '印地语', 'is': '冰岛语', 'pl': '波兰语', 'fa': '波斯语', 'dan': '丹麦语', 'tl': '菲律宾语',
'fin': '芬兰语', 'nl': '荷兰语', 'ca': '加泰罗尼亚语', 'cs': '捷克语', 'hr': '克罗地亚语', 'lv': '拉脱维亚语', 'lt': '立陶宛语', 'rom': '罗马尼亚语',
'af': '南非语', 'no': '挪威语', 'pt_BR': '巴西语', 'pt': '葡萄牙语', 'swe': '瑞典语', 'sr': '塞尔维亚语', 'eo': '世界语', 'sk': '斯洛伐克语',
'slo': '斯洛文尼亚语', 'sw': '斯瓦希里语', 'uk': '乌克兰语', 'iw': '希伯来语', 'el': '希腊语', 'hu': '匈牙利语', 'hy': '亚美尼亚语', 'it': '意大利语',
'id': '印尼语', 'sq': '阿尔巴尼亚语', 'am': '阿姆哈拉语', 'as': '阿萨姆语', 'az': '阿塞拜疆语', 'eu': '巴斯克语', 'bn': '孟加拉语', 'bs': '波斯尼亚语',
'gl': '加利西亚语', 'ka': '格鲁吉亚语', 'gu': '古吉拉特语', 'ha': '豪萨语', 'ig': '伊博语', 'iu': '因纽特语', 'ga': '爱尔兰语', 'zu':
'祖鲁语', 'kn': '卡纳达语', 'kk': '哈萨克语', 'ky': '吉尔吉斯语', 'lb': '卢森堡语', 'mk': '马其顿语', 'mt': '马耳他语', 'mi': '毛利语',
'mr': '马拉提语', 'ne': '尼泊尔语', 'or': '奥利亚语', 'pa': '旁遮普语', 'qu': '凯楚亚语', 'tn': '塞茨瓦纳语', 'si': '僧加罗语',
'ta': '泰米尔语', 'tt': '塔塔尔语', 'te': '泰卢固语', 'ur': '乌尔都语', 'uz': '乌兹别克语', 'cy': '威尔士语', 'yo': '约鲁巴语', 'yue':
'粤语', 'wyw': '文言文', 'cht': '中文繁体'}
bd = baiDuFy('おはようございます', comef='jp', to='en')
res = bd.get_result()
print(res)