爬虫进阶之路---初识JS***之百度翻译实战(附带源码)
作者:互联网
(目的:实现类似于百度翻译的功能——输入内容,得到对应的翻译之后的内容。)
1.参数构造流程
这种提交数据得到响应的的请求,往往参数比较麻烦,所以参数的构造是得到完整请求的关键.
①以定位到URL为前提,不同参数该在哪里查找!
- 先作对比,找出不同的参数
- 从之前的请求响应中找数据
(1)网页源代码中查找
(2)全局请求搜索 - JS加载调试,查找生成规律
②参数来源(重要)
不变的参数写固定值,变动的参数的来源在JS中寻找,即JS***!
一个动态请求的参数来源只可能有两个:
一是来自之前的请求响应,二是JS生成。
第一步:先作对比,找出不同的参数(即找到需要进一步分析的参数)!
(1)F12开启装逼模式,勾选上红框内选项,在左边那个箭头所示的包含所有过滤之后的请求的地方逐个查看,通过观察右边Prview或者Response,找到我们所需要的能够获取目标数据的URL。观察所有请求后可以看到如图这个请求,通过分析可知这是个可以检测并返回你所需要翻译的语言的URL。
1.Preserve log选项,如果勾选,即代表不清除上一个页面请求的数据。比如:在一个网页里登录,如果不勾选此选项,由于点击
登录之前属于一个请求;点击登录之后属于另外一个请求。所以点击之后是没有你的登录信息的!
2.Disable cache选项,表示清除缓存,一般都要勾选,防止网页操作时由于本地缓存的存在,而导致一些预期之外的错误!
3.Hide data URLs:可以过滤掉data响应,便于观察。
(2)其实我们很容易可以想到,百度翻译靠的是JS动态加载,异步显示翻译结果的(因为每次输入内容,翻译结果直接就会显示而没有整个页面重新加载)!所以,我们直接选择XHL过滤:
通过一个个观察,我们会发现如图所示这个响应中包含了翻译结果,所以目标得到这个响应的URL。接下来,分析URL:
如图所示,可以发现这个URL发送的是POST请求,所以需要携带表单数据。最后一步我们要做的就是:通过一次次翻译,即一次次的发送请求,观察表单参数的变化情况,然后破解它并能够自己构造!
'''
from: zh
to: en
query: 我讨厌你
transtype: realtime
simple_means_flag: 3
sign: 23824.310817
token: 2df0cc9f53be4e351fc34844056f562b
domain: common
'''
'''
from: zh
to: en
query: 我爱你
transtype: enter
simple_means_flag: 3
sign: 47194.285547
token: 2df0cc9f53be4e351fc34844056f562b
domain: common
'''
对比参数分析可知:
from和to参数分别是翻译内容和翻译结果的语言;
query参数是翻译的内容;
transtype参数经多次对比可知有三个值:translang,enter,realtime。分别对应点击页面中翻译按键;按回车键enter;实时。
simple_means_flag参数发现一直没有变化,属于静态参数;
sign参数是签名,值是动态的,需要进一步分析!(需要找来源!)
token参数,可能会因为一些操作而变,需要进一步分析!(需要找来源!)
domain参数一直没有变化,属于静态参数。
第二步:获取第一步分析的动态加载的不同的参数!
获取的一般思路:
一个动态请求的参数来源只可能有两个:
一是来自之前的请求响应,二是JS生成。
(1)获取token值
按照获取的一般思路,首先,复制token到网页源码中查找。会发现token的值就在源码中(但是,请注意:这个token的值不是不变的,而是会改变的!)!
(2)获取sign值
1.按照获取的一般思路,首先,复制sign到网页源码中查找。会发现无论如何都找不到!
**2.所以,需要改变思路, 在全局请求搜索:(在Sources下使用快捷键Ctrl+Shift+f开启全局搜索框Search) **
观察之前sign的格式:sign: 47194.285547,所以我们在搜索栏中输入sign:进行搜索!
然后分析可知第一个的格式跟前面的form表单参数格式相似,所以我们点击进入此JS文件。
此部分数据格式和form表单中参数格式一模一样,唯独sign的值是个y(n)函数,所以,下一步我们要通过打断点进入此生成sign值的函数:
接下来要做的就很简单了。我们只需要将此JS函数拿过来单独运行,一步步调试即可!
本人使用的是python里的PyexeCjs包,此包可以单独运行JS代码!
# 安装
pip install PyexeCjs
编写python脚本文件,注意:刚刚得到的JS函数放到此脚本文件同级目录下,此处取名为sign.js。
import execjs
def make_execjs_object():
with open('sign.js', 'r') as f:
js = f.read()
# .compile()方法将代码编译为JS代码
return execjs.compile(js)
js_object = make_execjs_object()
# .call()函数的第一个参数是JS函数名,第二个参数开始就是JS函数的参数。注意:都是以字符串形式!
js_object.call("e","你好")
运行报错很正常,根据报错改JS代码,直到正确运行!
此处报错i未定义,我们只需要去原JS代码所在的JS文件里分析即可:
会发现,在此JS函数下面就是定义i的代码,拿过来即可 !
再次运行,又报错window未定义,查看sign.js下的JS代码:
分析可知,此处有个window[1],并不知道其来源。所以,继续回到原JS代码所在的JS文件里分析即可
首先,在对应位置打断点。
然后,重新开始调试开始debug模式,一步步调试,直到运行此断点下方:(做的目的:这样可以观察到window[1]的值,借以分析它!)
观察可知,window里有许多键值,[1]对应的是gtk的值。那么,如何获取到gtk对应的值呢?
下面,又回到最开始,获取动态加载的不同的参数(此处为gtk)!
首先,到网页源码中查找:
会发现,可以直接查找到,而且和JS文件中的值一模一样,所以,关于此值的处理方法:我们只需要给此JS函数再传递一个参数,再编写爬虫文件时,使用正则获取到此值传入JS函数即可!
再次运行脚本,会报如下错:
小拓展:关于JS代码运行出现——缺少对象类型报错的原因分析!
在JavaScript中,程序调度过程中经常会出现缺少对象错误,这里的对象有时候是变量,有时候是函数,有时候可能是对象等等。
缺少对象错误经常有以下几种情况
1.变量没有定义
使用没定义的变量时就会出在这种错误,这里经常是我们在写代码的时候不注意,把就是名写错就产生一个新的未定义的变量。
2.函数没有定义
这种错误也经常是因为在调用函数时不小心把函数名写错了,就会产生一个未声明的函数。
3.函数的参数
这个问题可以归结到第一种情况当中,也就是在函数体内使用实参时的书写错误。
4.对象的生成
JavaScript中所操作的一些对象往往是从html文档中提取的,经常会用到document.getElementById(str)这个函数,所以在调用这个函数的时候也可能会因为函数名或者参数的一些书写上的错误而产生对象错误。
按照上面对此类报错的分析,观察JS代码,可知其中有个n()函数是凭空出现的;观察原JS文件会发现有这个函数:
直接将此函数拿过来即可!
再次运行,完全ok,生成的sign值也和正常运行时一模一样!
第三步:所有准备工作都已完美完成,最后我们要做的也就是写爬虫代码了!
import re
import requests
import execjs
headers = {''
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
}
session = requests.Session()
session.headers = headers
def make_execjs_object():
"""
执行js代码,为了下面获得sign值!
:return:
"""
with open('sign.js', 'r') as f:
js = f.read()
return execjs.compile(js)
get_sign = make_execjs_object()
def get_token_gtk():
"""
请求初始URL,从响应中提取token,gtk参数。
注意:token需要获取多次,才能保持稳定不变!
:return:
"""
url='https://fanyi.baidu.com/'
for i in range(3):
response = session.get(url)
token = re.findall("token: '(.*?)'",response.text)[0]
gtk = re.findall("window.gtk = '(.*?)'", response.text)[0]
print('[%s] token: '% i, token)
print('[%s] gtk: '% i, gtk)
return token, gtk
def translate(query):
"""
携带参数请求实现翻译功能的URL,从响应中提取结果!
:param query:
:return:
"""
token, gtk = get_token_gtk()
sign = get_sign.call('e', query, gtk)
url='https://fanyi.baidu.com/v2transapi?from=zh&to=en'
form_data = {
"from": "zh",
"to": "en",
"query": query,
"transtype": "enter",
"simple_means_flag": "3",
"sign": sign,
"token": token,
"domain": "common",
}
response = session.post(url, data=form_data)
result = re.findall('"dst":"(.*?)"', response.text)[0]
return result
if __name__ == '__main__':
input_quety = input('输入中文:')
translate_result = translate(input_quety)
print(input_quety + ':' + translate_result)
完全ok!
代码:
链接:https://pan.baidu.com/s/1GKnot2Uh7Jy7xPXuAnFc4g
提取码:wsee
复制这段内容后打开百度网盘手机App,操作更方便哦
标签:进阶,gtk,sign,token,源码,参数,JS,函数 来源: https://blog.51cto.com/u_15264787/2886542