【月更第20篇】签名验证反爬,反反爬第二篇博客,Python爬虫120例
知识铺垫
本篇博客继续为大家带来爬虫反爬技术学习,签名验证。
在上一篇博客,咱们学习的反爬验证信息,是存储在 请求头域 中的信息,签名验证一般在请求体(请求正文)中,服务器接收到对应的字段,并对其来源和合理性进行验证,然后判断是否返回正确数据。
如何判断签名验证
关键字 sign
,一般在请求中发现有这个参数,或者与其相关的参数,都可以猜测其为签名验证,即服务器端验证该参数传递到后台的值的合法性,决定是否返回结果。
关于签名验证反爬网站,有一个非常经典的入门案例,有道翻译
在爬虫圈,学习反爬,有道翻译一般作为第一个上手案例,从这个案例中,你将逐步学习如何使用开发者工具,一步一步的解开其反爬手段。
反爬时间
本次目标站点:https://fanyi.youdao.com/
任意输入待翻译内容,捕获到如下接口。
得到的接口信息与参数如下
接口地址:https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule
请求方式:POST
请求表单数据:
i=%E5%A5%BD&from=AUTO&to=AUTO&smartresult=dict&client=fanyideskweb&salt=16371882722519&sign=af5fc63544d0dc644ac5f7043ba13d0d<s=1637188272251&bv=635c26103d3f5e2d37d43c98780a06b2&doctype=json&version=2.1&keyfrom=fanyi.web&action=FY_BY_CLICKBUTTION
其中出现了 sign
关键字参数,可以猜测其为签名验证。
这里还有一个技巧,你可以依据加密字符串,猜测其加密手段。
常见的加密手段
md 系列:
例如 md5
,md2
,md4
,目前最常用的是 md5
,这种加密方式,最后得到的字符串一般是 16 位的,也有 32 位和 40 位,不过是在 16 位的基础上前后增加了一些数据。
md5
加密出的字符串里面的数字的是 16进制
的,并且是小写,所以上文的 sign
加密串,af5fc63544d0dc644ac5f7043ba13d0d
就复合该指标,大概率是 md5
加密。
sha 系列
例如 sha1
sha256
sha512
,这种加密手段也是不可逆的,并且基于 md5
实现,数字的进制也是 16进制
, sha1
加密后的字符串长度为 40,sha256
加密之后的长度为 64,sha512
加密之后的长度为 128。
对称加密与非对称加密
这部分内容后续碰到的时候,在给大家说明其加密方式与判断技巧,本文了解上述 2 种即可。
接口抓包
得到加密请求之后,就要判断其加密手法,也就是要找到加密位置,该加密发生在请求前,所以一定能找到 sign
值的生成位置。
这里用到一个了检索功能,后续也可以使用断点调试。
点击开发者工具右侧的搜索框,按照下图 1,2,3 步骤唤醒搜索框,也可以直接按快捷键 Ctrl+Shift+F
唤醒。
在 3 的位置输入请求接口中的部分关键词,例如 translate_o ,出现下图内容。
通过上图可以发现,检索到了一个 JS 文件 fanyi.min.js
,该文件大概率是翻译逻辑所在的代码文件,双击 fanyi.min.js
下面的代码块,即可跳转到文件代码内容。
最新版本的谷歌浏览器,直接会提示是否对压缩后的 JS 文件进行 美观输出 ,点击按钮即可,如果没有提示,点击左下角的 {}
符号。
格式化之后的代码,会自动定位到你检索的内容,下图中可以看到 JS 发起发落请求部分的代码。
接下来,就可以进行断点调试了,在本段代码的上面,寻找一个合适的位置,打上断点,点击前面的代码行号即可,效果如下:
回到翻译窗口,点击翻译按钮,如果页面停止,被断点断住,表示打断点成功,否则的话,需要上下调整断点位置。
此时可以发现,在开发者工具的视图中,出现了下图所示内容,右侧的窗口中也出现了变量 e
的相关值。
下面重点查阅 调用堆栈,这里是 JS 代码在执行过程中,各种方法被调用的顺序。
从上到下依次点击方法名,直到找到 JS 请求参数中 e
的加密位置。
点击 t.translate
,定位到如下代码段。
发现上图的 r.sign
已经存在值,在上部分代码中,找到 generateSaltSign
函数,从命名可以猜测出,该函数是生成加盐密钥的函数,所以直接跳转到函数体。
在 JS 窗口中,点击 Ctrl+F,唤醒代码检索框。
依次查阅找到的 4 个函数,发现只有一个是赋值操作,即下述代码:
t.generateSaltSign = r;
将 r
赋值给了该函数,所以找到 r
对应的内容,就表示 JS 加密分析完毕。
继续向上查找代码,找到如下 r
的赋值部分。
var r = function(e) {
var t = n.md5(navigator.appVersion)
, r = "" + (new Date).getTime()
, i = r + parseInt(10 * Math.random(), 10);
return {
ts: r,
bv: t,
salt: i,
sign: n.md5("fanyideskweb" + e + i + "Y2FYu%TNSbMCxc3t2u^XT")
}
};
发现,此时已经得到了答案,其中 r
是一个匿名函数,返回一个对象,里面包含如下参数:
ts
:时间戳字符串;bv
:navigator.appVersion
进行md5
加密的结果字符串;salt
:时间戳+一个随机数字;sign
:n.md5("fanyideskweb" + e + i + "Y2FYu%TNSbMCxc3t2u^XT")
,其中e
是查询字符串。
为了让数据更加准确,你可以在这里在增加一个断点,便于查看其参数值。
编码时间
整体内容分析完毕之后,编码环节就变得非常容易了。
import requests
import time
import hashlib
import random
def translate_o(e):
post_url = "https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Host": "fanyi.youdao.com",
"Origin": "https://fanyi.youdao.com",
"Referer": "https://fanyi.youdao.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",
"Cookie": 'OUTFOX_SEARCH_USER_ID_NCOO=239450857.32821953; OUTFOX_SEARCH_USER_ID="1028070866@10.108.160.17"; _ga=GA1.2.1678241630.1620569274; P_INFO=null; JSESSIONID=aaaqVTKWakJYYgS9kwb1x; ___rl__test__cookies=1637483520729'
}
time_stamp = int(time.time() * 1000)
data = {
"i": e,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": "fanyideskweb",
"salt": "",
"sign": "",
"lts": str(time_stamp),
"bv": "",
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_CLICKBUTTION"
}
# bv 将浏览器版本进行 md5 加密
data["bv"] = hashlib.md5(headers["User-Agent"].encode(encoding='UTF-8')).hexdigest()
# salt 参数为时间戳+随机数字
data["salt"] = str(time_stamp) + str(random.randint(0, 10))
# 签名格式
sign_origin = ("fanyideskweb" + e + data["salt"] + "Y2FYu%TNSbMCxc3t2u^XT").encode("utf-8")
data["sign"] = hashlib.md5(sign_origin).hexdigest()
res = requests.post(post_url, headers=headers, data=data)
print(res.json())
if __name__ == '__main__':
translate_o("hello")
其中对比 JS 编写的 Python 代码如下所示:
# bv 将浏览器版本进行 md5 加密
data["bv"] = hashlib.md5(headers["User-Agent"].encode(encoding='UTF-8')).hexdigest()
# salt 参数为时间戳+随机数字
data["salt"] = str(time_stamp) + str(random.randint(0, 10))
# 签名格式
sign_origin = ("fanyideskweb" + e + data["salt"] + "Y2FYu%TNSbMCxc3t2u^XT").encode("utf-8")
data["sign"] = hashlib.md5(sign_origin).hexdigest()
在实际测试的时候,发现一个小坑,就是需要在请求头 headers
中增加 Cookie
值,否则会返回异常状态码 50
。
{'errorCode': 50}
不知从何时起,JS 逆向的第一课,都已经锁定为有道了,感谢有道为反爬事业做出的贡献。
其实将接口地址 https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule 中 translate_o
的 o
去掉,就不需要 JS 加密了,无加密接口为:http://m.youdao.com/translate
订阅时间
今天是持续写作的第 270 / 365 天。
可以关注我,点赞我、评论我、收藏我啦。
- 点赞
- 收藏
- 关注作者
评论(0)