写这篇文章也是因为踩坑浪费了不少时间,难度一星,坑爹五星,把这个网站拎出来给大家避个雷,来自工作中需要抓取的一个网站
https://www.gdtv.cn/search?key=珠江新闻眼
找请求
没啥好说的,这个接口:https://gdtv-api.gdtv.cn/api/search/v1/news
python模拟请求
老爬虫操作都是从浏览器控制台copy as url 到 postman 再生成 python-requests 代码
nm的诡异现象来了,postman发出的请求永远正常,复制代码发出的请求永远401
我的噩梦来了,先是拿出charles抓包看两次请求不一样的地方,再就是使用文本对比看两次curl不一样的地方,最后甚至手写原生的urllib请求,tm的都没找到原因,此时到这里我已经去解决headers里面加密的字段了,实在不行python调用shell脚本执行curl拿数据
在解决完x-itouchtv-ca-signature这玩意之后,我找到了401的原因,前面的signature是由request-body生成的,而json.dumps之后的body是加了空格美化的,当然和signature对不上了
还有一个地方是body需要encode才行,这里我没去细究原因了,各位看官可以给我留言
这里我必须吐槽下,我是吐槽postman还是这个网站的后端
signature逆向
定位加密位置以及后续打断点还是非常easy的,直接看图
加密参数生成步骤
a 为 JSON.stringify(body)
p = (0, r.default)(a)
_ = l.default.stringify(p)
m = "".concat(t, "\n").concat(n, "\n").concat(d, "\n").concat(_)
signature = l.default.stringify((0, o.default)(m, "dfkcY1c3sfuw0Cii9DWjOUO3iQy2hqlDxyvDXd1oVMxwYAJSgeB6phO8eW1dfuwX"))
等价于,其中变量d为13位时间戳,a为body
l.default.stringify(o.default(["POST", "https://gdtv-api.gdtv.cn/api/search/v1/news", d, l.default.stringify(r.default(a))].join("\n"), "dfkcY1c3sfuw0Cii9DWjOUO3iQy2hqlDxyvDXd1oVMxwYAJSgeB6phO8eW1dfuwX"))
加密方法,到这里我们需要关注的也就是l.default.stringify、o.default、r.default三个方法
我们跟进去看一下l.default.stringify,这不就是个base64吗
o.default,hmac + hash,加盐的hash,挨个测试一下,是hmacsha256
r.default,hash,挨个测试一下,是md5
写个代码测试下,就几行,主要想让你们看下我背景图,哈哈哈
获取数据
nice,可以拿到数据,下班回家
怎么才50啊,再呆10min混个25餐补吧,哈哈哈