python模拟登录12306时必须要有的一个cookie参数就是RAIL_DEVICEID,否则是登录不成功的,那么这个RAIL_DEVICEID是从哪里来的呢?下面我们来一步步分析。
1、RAIL_DEVICEID如何获取
首先可以用chrome浏览器打开12306网站,先清空浏览器的缓存,打开开发者工具,然后刷新,找到RAIL_DEVICEID的值,再全局搜索,可以发现这个值是服务器返回的,所以我们应该分析ajax请求的url是如何生成的。
2、定位ajax请求在JS的位置
在知道这个ajax请求的url之后,可以全局搜索url中的关键字段,例如HttpZF/logdevice这部分(需一点点尝试),此时可以发现在GetJS这个文件中找到了这部分url字段,那么我们就已经定位到js生成url的位置了。
3、分析url请求链接的拼接字段
接下来,我们来分析这个url是如何拼接成的,从js中扒下来的url拼接代码是:
1 | "https://kyfw.12306.cn/otn/HttpZF/logdevice" + ("?algID\x3dBikJbiWz0u\x26hashCode\x3d" + e + a) |
这里总共是四个部分,第一部分是固定不变的,第二部分是每次请求获取GetJS文件时服务器返回的,也就是会变的,可以从GetJS文件中提取(其中\x3d是”=”,\x26是”&”),第三部分和第四部分都是js生成的,可以通过断点调试找到它们的生成过程。
4、url中e和a如何生成
1 | for (var a = "", e = "", g = c.getpackStr(b), k = [], q = [], t = [], l = [], p = 0; p < g.length; p++) |
上面是生成e和a的js代码,其实就在url拼接的上面,这里可以看到,e和a分别对应另一个变量的value和key,而这个变量是调用c.hashAlg方法生成,传入了k,a,e这三个值,通过断点或者观察代码,可以发现其实a和e都是空字符串,所以只要得到k的值即可,这里我们可以将这一段代码扒下来,封装成一个函数,把最终的url赋值给一个变量,然后返回,用python执行这段代码,就可以得到url了。但是,这里有几个外部变量和外部函数,我们要如何去拿到它们呢?
5、分析外部变量和外部函数
在封装成函数后,这里需要得到的是c.getpackStr和c.hashAlg以及n,pb这四个函数和d,Eb,Bb,Db,Cb这五个变量,我们来一一分析它们,先分析n,pb这两个函数,可以通过断点调试找到它们,只要把它们拿出来添加到js代码中即可,可以直接调用,这里不多赘述;d是在前面赋值的,调试后可发现它其实是本地局域网的ip地址,可以写死;Eb,Bb,Db,Cb是不变的,同样可以写死;而c.getpackStr通过断点调试,可以发现它最终返回的是一个数组,其中每个元素都是通过n函数得到的一个实例对象,而且这些都是浏览器的相关信息,因此我们可以猜测它是不变,返回值赋值给g变量,此时我们可以通过断点拿到g的值,然后在代码中写死这个g变量。
注:这里g变量中有一个cookieCode的值是一段字符串,这其实是个坑,这个值就是在第一步中服务器返回的,有可能你看了发现并没有,那是因为只要g变量中有这个值,那么服务器就不返回该值,所以这个在最开始是并没有值的,此时返回的内容就会有这个值,因此我们可以直接将这个值删除。
6、调用hashAlg函数执行加密
1 | hashAlg: function(a, b, c) { |
c.hashAlg才是js加密的重头戏,上面是通过断点调试得到的hashAlg的函数,这里就是生成url加密部分的代码,我们同样把它扒下来封装成函数添加到刚刚的js代码中,老办法,找外部函数或变量,这里传进来的就是第4步中的k,a,e,只要在js代码中调用这个hashAlg即可,然后就是R.SHA256,R.enc.Base64以及gb,Qa,n,gb也是一个固定不变的字典,所以可以写死,Qa可以直接扒下来添加到代码中调用,n在第5步已经添加了;而R.SHA256和R.enc.Base64是两个加密算法,可以下载CryptoJS,然后将这两个算法的代码复制粘贴到我们的js代码中,将这里R替换成CryptoJS即可,这里提供CryptoJS文件的下载地址:
https://code.google.com/archive/p/crypto-js/downloads
注:需要科学上网
7、如何处理返回的动态JS加密算法
这里基本上我们的js代码已经处理完成了,可以直接使用python的execjs执行js语句,调用函数,获得url,再使用url去获取RAIL_DEVICEID的值,但是,这里有一个问题,其它的js代码都是不变,而在第6步中的hashAlg函数中,其中的加密算法是会变的,在for循环结束后到return之间的内容是变化,而且Qa也会随之变化,那么如何根据这个变化来随时变换js代码,我是通过搜索,定位,抠取代码,然后拼接来解决这个问题,但是这里由于某些原因,不便将具体思路分享出来。