python执行javascript网页_Python 爬虫如何优雅地执行 javascript 函数

最近想买内存条,京东上看了价格一直没降,于是找了个可以看历史价格的网站:查询商品历史价格走势(支持京东,天猫,淘宝等)APP - 慢慢买​tool.manmanbuy.com

这个网站功能挺强大,可以查到各大电商几乎所有商品近两年的价格走势:

你看,还贴心地提供了降价提醒。

不过点击「设置降价提醒」,会让你下载个 APP

像我这么懒的人,会拿起手机扫描二维码下载 APP 吗?不,我 Command+Option+I 打开了网页开发者工具 ...

选中 Preserve log,然后刷新页面,接着过滤出 XHR 请求:

可以看到就一条 XHR 请求,返回 JSON 中的 datePrice 就是我们需要的内容了。

再来看下这个 XHR 的具体 URI:

http://tool.manmanbuy.com/history.aspx?DA=1&action=gethistory&url=http%253A%2F%2Fitem.jd.com%2F1099630.html&bjid=&spbh=&cxid=&zkid=&w=951&token=qgta6a0724e5e87cd45124d68c8876c69869et

参数中的 url 是我们需要查询的商品链接,这是我们已知并主动提供给接口的。DA、action、w 这三个参数对于这个接口来说是可以固定,关键的就是这个 token 参数了。

尝试只改了下 token 参数,它总是给我返回这个结果:我要的是内存不是增高鞋啊,orz ...

再来看下开发者工具。

过滤工具栏里,重新选择 All,然后 Command+F 呼出搜索框搜索 token:

很容易就找到了对应的代码:

if ('【金士顿Fury系列】金士顿(Kingston)骇客神条 Fury系列 DDR3 1600 8GB台式机内存(HX316C10F/8)蓝色【行情 报价 价格 评测】-京东' != "") {

$("#iframeId").attr("src", "history2018.aspx?w=951&h=780&h2=420&m=1&e=1&browes=1&url=" + escape('【金士顿Fury系列】金士顿(Kingston)骇客神条 Fury系列 DDR3 1600 8GB台式机内存(HX316C10F/8)蓝色【行情 报价 价格 评测】-京东') + "&token=" + d.encrypt('【金士顿Fury系列】金士顿(Kingston)骇客神条 Fury系列 DDR3 1600 8GB台式机内存(HX316C10F/8)蓝色【行情 报价 价格 评测】-京东', 2, true));

historyouhui('【金士顿Fury系列】金士顿(Kingston)骇客神条 Fury系列 DDR3 1600 8GB台式机内存(HX316C10F/8)蓝色【行情 报价 价格 评测】-京东', 6);

}

这里先去请求 history2018.aspx,然后在这个页面里通过 AJAX 来访问我们上面找到的 XHR:

所以这个 token 参数是这么加密来的:

d.encrypt('【金士顿Fury系列】金士顿(Kingston)骇客神条 Fury系列 DDR3 1600 8GB台式机内存(HX316C10F/8)蓝色【行情 报价 价格 评测】-京东', 2, true)

但是这个 d.encrypt 在这个 XHR 上却是没有找到。

搜下 encrypt,找到了对应的加密脚本:

这里有两个 shenqing.js (真烦这种用拼音来作为文件名变量名的),但是版本不同,看了其实两个版本在加密方法上并没有改变,先看下较新的版本好了。

http://include.manmanbuy.com/js/shenqing.js?v=20172

函数 1:

函数 2:

这是经过打包混淆的代码,稍微 Google 了一下,就算是没什么前端经验的我,也可以发现这些代码是用 JsPacker 来压缩的:

根据网上的信息,来到一个在线 JsPacker 解压的网站,将两段函数分别贴进去,解压:

反正是挺长的两段 JS 代码,要想翻译成 Python 谈何容易。所以我们需要一个工具,能直接执行 JS 脚本返回结果。

登登登登,本文主角隆重出场,它就是 PyV8:

PyV8 的安装略蛋疼,建议直接使用编译好的二进制包,可到以下的地址来获取各平台上对应的二进制包:emmetio/pyv8-binaries​github.comv2-41b18c6f815d9536378857126cf6db1a_ipico.jpg

接着我们来写段 Demo 代码来验证下:

from pyv8 import PyV8

f1 = '''解压后的 js 函数 1'''

f2 = '''解压后的 js 函数 2'''

ctxt = PyV8.JSContext()

ctxt.enter()

ctxt.eval(f1)

ctxt.eval(f2)

print(ctxt.eval('''d.encrypt('【金士顿低电压版】金士顿(Kingston)低电压版 DDR3 1600 8GB 笔记本内存【行情 报价 价格 评测】-京东', 2, true)'''))

ctxt.leave()

运行结果:

出来了加密后的 token。用这个 token 确认下是否能得到真实的数据:

curl 'http://tool.manmanbuy.com/history.aspx?DA=1&action=gethistory&url=http%253A%2F%2Fitem.jd.com%2F1066754.html&bjid=&spbh=&cxid=&zkid=&w=951&token=x6mwf2d62fc1bac4b61462942033d429fe79m4vzg' | iconv -f gbk -t utf8

这就对了!

不过仔细一看,对比网页上绘图的结果,日期其实刚好相差了一个月。不清楚为何会这么处理,不过不管它了,直接加上一个月就好。

整合下代码,最后可以将数据打印出来了:

还可以用 pyecharts 打印出漂亮的图表:

最后当然是回到我们的目的了:设置降价提醒。这里我使用了倍洽的 incoming 机器人:

完成!

嗯,为了不拿出手机扫描二维码,我花了几个小时时间配置环境写了个脚本 ...

完整代码:https://gist.github.com/jackhuntcn/c017b658d0e1c728e1cd5ced6f9fd0f6​gist.github.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值