前言
这是我爬虫的通用思路,每一个网页都可以这样套公式分析,无非就三步
- 理思路
- 抓包分析
- 复现代码
具体分析
理思路
打个比方,以我们学校的电费登陆作为例子
这里选择统一身份认证点击由此登入
跳转到网页登陆
输入账号密码
跳转回业务网页
结束
理一下逻辑图
抓包分析
我使用的抓包工具是charles
关于charles的配置可以看另外一篇文章
首先判断登陆以后的状态
因为我抓得多了,很快就能得出结论。
很明显登陆以后是由cookie来保持登陆状态的,那么我们只要直接分析跳转后登陆的逻辑,然后再获取跳转回来的链接,访问这个链接即可获得cookie实现逻辑
如果是完全不知道什么逻辑,那首先可以从点击按钮跳转的那一刻开始抓包
点击按钮以后按时间顺序排列的抓包的请求如下
首先是访问了
这个http返回状态为302
重定向位置为
Location: http://zhlgd.whut.edu.cn/tpass/login?service=http%3A%2F%2Fcwsf.whut.edu.cn%2FcasLogin
再从时间顺序可以看到下一个正好对得上就是302的链接
那这里跟进去,随便翻翻
很明显是登陆的页面
再看刚才的流程图,下一步就是输入账号密码登陆,那么我们就抓包这个登陆的请求
随便输然后观察产生的http请求记录
这里产生了两个请求
第一个请求暂且不管
第二个是post请求,可以推断他就是将账号密码发送到后端的请求
我们再想想思路,前端我们输入了账号密码,按下了登陆按钮
我们登陆需要的数据就是
- 账号
- 密码
仔细观察一下第二个post请求
表单中有5个值,如果解决了这5个值,将请求POST到后端,我们是不是就可以实现登陆了呢,那么我们这里分析一下这5个值
ps:为什么是表单呢?因为http请求头中的Content-type已经规定了发送数据的格式为application/x-www-form-urlencoded,有兴趣可以自己看一下http协议
接着说这5个值,我第一次看的时候也看不出来这些是什么,那么我们回到浏览器中去看看
按F12进入浏览器的调试模式
点击这个选择器
把鼠标放到账号密码上看看
按左键
这里调试器跳转到html对应的html语句
换位思考一下如果你是前端,你要实现登陆,是不是在登录按钮写js函数,填入账号密码应该是没有任何的js逻辑的,所以我们这里用选择器点击登陆按钮找到登陆的js源代码
这里跟到了js代码,稍微上下看一下,就能发现了
这里有个我们感兴趣的post请求,他出现了ul,pl,上面还定义了lt,这里就是我们要找的参数,对应我们刚才找到的post请求的参数
上面这个lt是使用id选择器查找的html元素,那么我们回到html中找到这个元素
现在找到了其中一个元素lt,我们还要考虑他是否是变化的,我们刷新一下网页,看他是否是变化的,如果是,那么我们直接引用;如果不是,那么我们每次都取他的lt值作为参数
很明显他是会变化的,实际上不刷新也很好判断他是变化的,如果判断不出来,后面跳转他就是靠这个返回到原来的页面实现cookie登陆的,就可以知道只能是变化的
然后接下来ul和pl
我们在这里下断点,然后再点击登陆,看看他是如何运作的
点击登陆,调试器会在这里中断
记住要断那个encrypt函数,点encrypt函数下断
分析这段js
$.ajax({
url : "rsa",
dataType : "json",
type : "POST",
success:function(data){
var encrypt = new JSEncrypt();
encrypt.setPublicKey(data.publicKey);
$("#ul").val(encrypt.encrypt(u));
$("#pl").val(encrypt.encrypt(p));
$("#loginForm")[0].submit();
}
});
这里是向/rsa接口发送了一个请求,在返回数据后将data.publicKey传入到encrypt.setPublicKey()中,再调用了encrypt.encrypt(u),使用id选择器将这个值存入了页面的ul中
rsa接口很明显是刚才的第一个http请求
{
"publicKey": "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJdvbyudr+Od9CoAuh46D6DjLgZ5DL9iVNTZK4cAVgaQjmvvC0ASGA/URgnSfyswgdI1/9LsNDPmYi2Xdrxrn7UCAwEAAQ=="
}
然后再看看这个u
123是从哪来的呢,这不就是用户名吗
总结一下,得出结论ul就是u经过rsa加密得来的加密username,u即明文用户名,下面的pl就是同理的密码
既然公钥有了,那登陆部分就可以直接实现了
至于接下来的部分,只要分析正确登陆后的跳转就可以知道
他就是一个302跳转到底就直接能得到cookie
复现代码
代码我就不写了,请看我的项目
位于第30行的whut_login函数
def whut_login(self, service, username, password): # service是跳转过来时候的params
self.sessions.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35",
}) # 更换请求头的User-Agent,隐藏爬虫身份
html = self.sessions.get("http://zhlgd.whut.edu.cn/tpass/login", params={
"service": service
}) # 获取html,以便接下来获取tpass
etree.HTMLParser(encoding="utf-8")
# tree = etree.parse(local_file_path)
tree = etree.HTML(html._content.decode("utf-8"))
tpass = dict(tree.xpath('//*[@id="lt"]')[0].attrib)["value"] # 获取tpass
# des = strEnc(username + password + tpass, "1", "2", "3") # 这个是之前的加密逻辑,最近刚更新的加密
self.sessions.headers.update({})
self.sessions.cookies.set(domain="whut.edu.cn", path="/", name="cas_hash", value=""),设置cookie
# print(tpass)
result = self.sessions.post(
url="http://zhlgd.whut.edu.cn/tpass/login",
params={
"service": service
},
# 下面对应参数
data={
"rsa": "",
"ul": encrypt(username), # 请看下面的encrypt函数定义
"pl": encrypt(password),
"lt": tpass,
"execution": "e1s1",
"_eventId": "submit",
}, verify=False, allow_redirects=False) # allow_redirects设为False,无论登陆成功与否先不跳转
if result.headers.get("location") is None: # 判断是否有location,来看登陆是否成功,如果失败返回False
return False
return result.headers["location"] # 成功返回登陆成功后的跳转链接
利用公钥加密
def encrypt(plain): # 你不会以为加个js我就不会写了吧 这个注释是我github里面写的,在我发布这个以后登录加密逻辑就改了,不知道是不是巧合,我就在项目这写下这个注释
rsakey = RSA.importKey('''-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJdvbyudr+Od9CoAuh46D6DjLgZ5DL9i
VNTZK4cAVgaQjmvvC0ASGA/URgnSfyswgdI1/9LsNDPmYi2Xdrxrn7UCAwEAAQ==
-----END PUBLIC KEY-----''')
cipher = Cipher_pkcs1_v1_5.new(rsakey)
cipher_text = b64encode(cipher.encrypt(plain.encode('utf-8')))
# print(cipher_text.decode('utf-8'))
return cipher_text.decode('utf-8')