2022年了还不知道怎么比价,快来试试用Python自己爬取历史低价

图片
前话:
买东西大家都喜欢看商品的历史价格看现在是否低价,作者平时购物喜欢使用慢慢买查看历史价格,但其实别的平台也有比价软件,如果都能一次性下载下来,然后相互参考就可以得到全面的最低价格,下面作者就用Python爬取慢慢买的商品历史价格,大家可以触类旁通进行横向拓展。
正文
首先使用谷歌浏览器打开慢慢买网页,F12然后随意点击一个商品,查看接口:
图片
发现有我们需要的价格,然后再看看参数:
图片
请求头:
图片
emmm,参数挺简单的,只有一个cxid咱不知道,于是我看了一下网址:http://cu.manmanbuy.com/discuxiao_4076382.aspx。emmm,参数这就齐全了?我马上打开Pycharm编写了如下代码:
输出:
因内容太长截取的时候省略了很多,对比了一下页面的信息可以发现,返回的dataPrice字段里的列表第一个是日期时间戳,第二个是价格,第三个是满减信息
我心想这就爬下来了?于是我就去搜索多找了几个商品实验了一下:
图片
这是一个京东商品参数,emmm好像就是把cxid换成了bjid?于是我把bjid参数带回刚刚的代码测试发现也是能正常跑通的,查看了页面一下bjid能不能拿到
图片
emmm,能拿到,没问题。但是前面两个bjid是0的是什么鬼?我又接着看bjid为0的两个商品
竟然加载不出来?跳转过去看看
图片
竟然还有这样的页面,而且调用的接口不是同一个?WF?还有这种操作,于是我又看了看参数
图片
竟然给多出了个token。emmm,这TM坑爹,不用想肯定是前端js生成的,只能开始慢慢debug js吧。
首先在左侧F12按下CTRL+F打开搜索框,搜索token(毕竟是token参数,js里总不能没有这个字符串吧)
图片
观察了一下可以看到后面几个token都是在URL里的,这种肯定是先获取到token才会传参带上token的,就不用看了,主要浏览前面几个js文件。经过我的不懈观察,最终发现token是在cutomRequest.js文件里的,为什么呢?看下图:
图片
里面有给token赋值的操作,而且仔细去看还有ajaxPost操作,这里暂不截图看下面,既然找到了token,那就好办了,点击Source进入源码(查看接口的上面的tab是network,source一般在network左侧)
图片
这时候我们在source下打开刚刚的文件(谷歌浏览器打开源文件会有个pretty-print提示,要点确认啊,这是格式化js代码,不然眼镜看瞎了也照不出来),在刚刚token的位置打上断点,什么?断点也不会打?看下图
图片
颜色变蓝了就是打上断点了,这时候再刷新页面就可以debug停顿和看到数据信息了
图片
现在仔细看看参数,WF?都是什么鬼变量?不用看肯定是js混淆搞的鬼,既然这样从这个方法第一行开始debug(毕竟要知道入参才能知道token是怎么生成的嘛)
图片
鼠标选中悬停一会儿能够显示整个块运行的结果如上图,emmm,我js虽然不太好,但是大致意思应该是先new一个Date对象再调用valueOf方法吧,继续去下一行
图片
emmm,看debug信息上面那行应该是生成一个当前时刻的毫秒级时间戳,可以拿去时间戳解析平台对比下,这里就不对比了。从上面的debug可以看出来,先要把传入的数据加上t参数存入当前时间戳,继续往下:
图片
foreach应该是循环吧,上面把数据的key提了并进行排序(按字符从小到大,这里就不截图了)出来,这里应该是遍历遍历key,继续往下:
图片
如果是方法遍历的话点一下f这一行是可以跳转的,这一整块循环的逻辑大概是把之前参数的json对象复制到一个新的json对象,感兴趣的小伙伴可以自己去debug试试,这里就不占位置了,接着我们继续往下:
图片
WF?secret又是啥子东东?我这里直接用快速探测法探测这个secret是不是固定的。WHAT?怎么探测?刷新几次页面等debug到这里对比几个值是不是一样的,如果是一样的大概率就是固定值。这里我测试了一下secret是固定的,所以直接记下来就可以了,再看之后的循环,简单debug了下,都是判断两个不等于的,而且都是成立的,所以咱直接看里层的逻辑就可以了:
图片
又是嵌套函数,debug跳过去看看发现是求和函数,那就是求两个参数的和了,再看看两个参数:
图片
求和函数中又嵌套了一个调用函数,这破函数就是就是让xxx(a, b)变成a(b)函数调用,所以一个个参数和第二个参数都是要被encodeURIComponent调用,再看看俩参数是个啥:
图片
发现第一个参数是key,第二个参数的链接,这。。。不就是刚刚json的key和value吗?那这个循环的逻辑应该就是对key和value进行urlencode编码再拼接?继续往下看:
图片
果然如此,这一行又+=了一个secret,所以最后还会有一个secret,前面部分和我们分析的一致,继续往下走:
图片
emmm,要转一次大写,再往下:
图片
emmm,hex_md5大家应该都能猜到是md5,md5完之后还要再转一次大写,这样token的生成就生成完了,我在这里把整个逻辑用代码写一遍就是:
到这里就把整个token获取流程搞定了,激动的我赶紧写了代码去测试一下:
没想到竟然给我返回这个
WHAT?难道还有隐藏参数?我又去翻了一遍请求头,发现竟然还有个Authorization参数
图片
emmm,这么坑的吗?没办法,只能继续研究呗,再回到刚刚的customRequest里面,我们可以看到有一个ajaxPost的方法,整个逻辑调用就是从这里进入的。纳尼?你问我怎么知道从这里?ajaxGet和其他的我都debug过了,流程没有走到那边就不会停顿。所以现在我们要再看看ajaxPost整块的处理逻辑
图片
可以看到这里就是调用前端ajax的方法了,我们去看看里面,url,type,data肯定都没办法设置请求头的,所以不出意外应该就是在beforeSend里面了(额外提一下data的参数值就是调用的getParam方法,就是刚刚咱们获取t和token的方法,感兴趣的小伙伴可以自己debug看看)
图片
在beforeSenc方法里面,第一个是创建对象不用管,直接debug到if里面,debug了下if条件一定为真,所以直接看里面的逻辑就可以了(接下来就是本文的重点了
图片
先看第一个变量是setRequestHeader函数,有戏,继续往下
图片
这不就是咱要的参数吗?不容易TAT,继续看后面值是怎么弄的
图片
本地函数,跳转过去是求和函数(个人习惯把function(a,b) return a+b;的函数叫做求和函数,实际上可能是个字符串拼接,刚刚上面token也是同理),继续往后:
图片
BasicAuth,说明前面先添加一个BasicAuth加一个空格的字符串,继续往下:
图片
再拼一个getTicket()方法,emmm,那咱再去看看这个方法:
图片
同理第一个定义对象咱不看,这里调用了本地函数的函数引用,大致应该就是这样(function(a,b) return a(b))
图片
什么鬼,引用 #ticket ?还好懂一丢丢jquery,知道jquery里面能直接()引用dom,不然就凉凉了,那这里的逻辑大胆猜测应该就是 $("#ticket"),再往后看
图片
val方法?估计应该就是 $("#ticket").val() ?好久没写jquery不知道是不是这样写的,大概意思应该就是获取页面id为ticket的value值?WHAT?还能这样玩?这不debug过来谁能知道,还加过混淆了根本搜索不出来。。内心只有一个字:牛逼。我们再去页面搜索下ticket:
图片
好家伙,还偷偷在这埋了一手,咱先继续看刚刚js之后的逻辑,ticket之后咱可以调一次页面拿到:
图片
if条件是永远为真的,总是判断176 > 4 ?这不是废话吗,if里面的逻辑大家可以自己去debug下,手快敲断了敲不动了这里就直接说下逻辑,里面又是调substr又是调substring,核心逻辑就是实现从ticket中拿到的字符串从176往前取4个放在前面,再把第一个到第172个放在后面。???我当时就想黑人问号,这么个逻辑能混淆的这么复杂?用python描述下就是ticket[:176][-4:] + ticket[:172],脑阔痛。所以Auth这个参数的值大概就是BasicAuth xxxxxxxxxxxx这样,于是我又改了下写了如下代码
这下调用接口终于能拿到正常结果了
为了方便也只复制了一部分,想要尝试效果的小伙伴可以去试试。