咪咕登录
特点:
1.查看有些网站的数据,需要登录状态
2.登录状态通过cookie保持
操作:
1.得到cookie需要账号密码以及其他参数,通过post请求与后端交互
2.网站后台为防止爬虫,可能会检验HTTP请求提交过来的参数
3.爬虫登录可以通过手动保存cookie,每次请求时带上cookie;缺点是容易过期,小任务爬虫可手动保存cookie
4.长期任务,最好模拟登录所需的参数和多次请求,得到cookie
参数构造流程
这种提交数据得到响应的的请求,往往参数比较麻烦,所以参数的构造是得到完整请求的关键.
以定位到URL为前提
- 先作对比,找出不同的参数
- 从之前的请求响应中找数据
(1)网页源代码中查找
(2)全局请求搜索 - js加载调试,查找生成规律
参数对比
之前的请求中找数据:
-
上级页面搜索: 静态页面搜索参数内容
-
本级页面:Network里搜索内容
复杂登录特点:
- 参数多,
- 加密参数来源复杂
- 多次请求
在模拟登录的时候需要注意一点,我们需要用错误的账号和密码进行登录,这样才能更清晰的看到请求,而且也利于我们反复实验和调试。如果使用真实的账号密码登录的话,一般登录成功后会自动进行重定向,不利于找到正确的登录URL,也不利于多次调试,并且多次登录和登出的操作也有可能会增加你账号的风险,可能会限制账号的登录甚至封号之类的情况。
captcha参数
这个值对应的是验证码的数据,第一次登录时并没有这个验证码,提交空值给表单就可以了,验证码是账号或密码输错导致登录失败的时候会出现。这个时候会给服务器发送一条请求,返回值就是验证码经过base64编码后的数据。
如果我们需要进行多次调试的话就需要正确输入验证码才能继续进入登录的URL,但是我们用代码模拟登陆的时候其实不需要关注这个字段,因为第一次对服务器发送请求的时候是没有验证码这个字段的,而我们的模拟登陆是单次进行的,所以说这个字段在提交表单的时候留空即可。
enpassword参数
通过观察两次请求的参数可以看出来enpassword参数是不相同的,所以这个参数显然是动态变化生成的,再仔细观察一下发现这个值每个字符的范围都是0-9和a-f,通过我们的经验可以猜想这个参数是16进制的字符串,而且很有可能是经过加密的。
现在有了这些初步的信息,我们就继续往下寻找这个参数的生成方式吧
搜索enpassword参数
可以按快捷键crtl+shift+F打开全局搜索器,可以在全部文件中搜索关键字。这里我们看到只有一个文件有关联,而且是html文件,对我们没有帮助,但是看到这个标签前面有个class属性,标签既然也可以通过class操作和定位,那就再搜索一下这个属性看一下。
搜索J_RsaPsd参数
搜索J_RsaPsd参数时又多了两个文件相关,根据这个两个js文件的文件名判断有login的那个文件应该更有用一点,下面我们就进入这个文件继续进行分析。
分析JS文件
–
搜索这个JS文件发现一共有3个地方有关键词,我们也不清楚发送登录请求时会调用哪个函数,所以就先把这3个地方全打上断点,再发送请求来确定。
执行过程
把鼠标移动到this上面,发现html页面上密码框被高亮了,那么这个值就应该和密码是相关的,this又被赋值给了b,也就是b和密码是有关联的。
下面是new了一个RSAKey,移上去看到是一个db函数,进入db函数看一下具体实现。
进入db函数
这里看不出有什么东西,先跳过不管。
这里是db函数的定义,但是全都是一些变量的定义,看不出来有什么可分析的,可能在其他地方还有修改,不过没关系,暂时先不看,先接着往下分析。
setPublic函数
这里需要传入两个参数,这两个值在上一个请求的response中可以得到,而且都是固定值。
这两个参数可以作为固定值写在代码里。
到这里我们知道了JS代码对这两个参数进行了一定的处理,一个赋值给变量n作为公模,另一个赋值给e作为指数。接下来分析encrypy函数,这个是RSA加密的主要函数。
encrypt加密函数
传进去的参数b.val()在console中执行发现就是我们在密码框中输入的明文。
encrypt函数实际就是gb函数
这里是将gb函数赋值给db对象的原型对象,下面又把db对象赋值给c.RSAKey,这就是最开始定义c的语句。
gb函数执行到c.toString(16)的时候就得到了加密结果
事实上这里加密的基本逻辑很清晰了,只是里面具体的实现方法比较多,不过我们可以直接把JS代码拉下来,用execjs执行
改写JS加密基本逻辑
function getEncryptedPwd(pwd, modulus, publicExponent) {
c = new RSAKey;
c.setPublic(modulus, publicExponent);
var d = c.encrypt(pwd);
return d;
}
pwd传入明文密码,modulus和publicExponent是固定值,现在只要把里面有关联的函数复制出来再进行调试就可以了。
这里是RSA加密函数定义的起止位置
Python调用JS加密函数
def make_encrypt_password(self, password, modulus, publicExponent):
"""生成加密后的密码
Args:
password (str): 输入框内输入的原始密码
modulus (str): rsa加密参数,两个质数的乘积,固定值,前一个请求获得
publicExponent (str): rsa加密参数,大于1的奇数,固定值,前一个请求获得
Returns:
str: 加密后的密码
"""
return self.js_object.call('getEncryptedPwd', password, modulus, publicExponent)
这个起止位置包含的一大段代码基本上全都是RSA加密需要用到的,先全部复制出来保存到一个新的js文件中,运行时可能会有一些问题,再慢慢调整。
运行时可能会遇到的问题
-
遇到的错误1
-
报错
-
解决方法 把alert中的中文删去
-
-
遇到的错误2
-
报错
-
解决方法 删掉define语句后尾部的括号
-
-
遇到的错误3
-
报错
-
解决方法 定义navigator和window
-
-
遇到的错误4
-
报错
-
解决方法 修改如下代码
-
运行结果
loginID 参数
搜索下面的J_RsaAccout,这个就是loginID参数的加密
稍微看一下就发现,这里对账号的加密方式和密码是完全一样的,只要像密码处理一样照猫画虎就可以了。
JS加密账号函数
function getEncryptedAccount(account, modulus, publicExponent) {
c = new RSAKey;
c.setPublic(modulus, publicExponent);
var d = c.encrypt(account);
return d;
}
Python调用JS加密函数
def make_encrypt_account(self, account, modulus, publicExp