在浏览网站的过程中,我们经常会遇到需要登录的情况,有些页面只有登录之后才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就需要重新登录。还有一些网站,在打开浏览器时就自动登录了,而且很长时间都不会失效,这种情况又是为什么?其实这里面就涉及到了会话和cookie的相关知识,本节就来揭开他们的神秘面纱。
HTTP的无状态指的是http协议对事物处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。当我们向服务器发送请求后,服务器解析此请求,然后返回对应的响应,服务器负责完成这个过程,而且这个过程是完全独立的,服务器不会记录前后状态的变化,也就是缺少状态记录。这就意味着如果后续需要处理前面的信息,则必须重传,这导致需要额外传递一些前面的重复请求,才能获取后续响应,然而这种效果显然不是我们想要的。为了保持前后状态,我们肯定不能将前面的请求全部重传一次,这太浪费资源了,对于这种需要用户登录页面来说,更是棘手。
这时两个保持http连接状态的技术出现了,分别是会话和cookie。会话在服务端,用来保存用户的会话信息。cookie在客户端,有了cookie,浏览器在下次访问网页时会自动附带上cookie发送给服务器,服务器识别cookie并鉴定出是哪个用户,然后在判断出用户的相关状态,然后返回对应的响应。
需求:使用requests模块实现github的模拟登录
import requests from lxml import etree headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36' } url = 'https://github.com/session' data = { 'commit': 'Sign in', 'utf8': '✓', 'authenticity_token':'lGnVdPaEf/jfBd6dzkQFpd4idQZxO96HSvRzWIPo5099dvmZTqEx+Q13zLjQzYpE08YmZdJStHd+Vk6Yhz7t/Q==' , 'login': 'bobo328410948@sina.com', 'password': 'bobo@15027900535', 'webauthn-support': 'supported', } page_text = requests.post(url=url,headers=headers,data=data).text with open('./git.html','w',encoding='utf-8') as fp: fp.write(page_text)
查看爬取下来的页面是否为登陆成功后的页面:结果发现该页面不是登陆成功后的页面,则表示模拟登陆失败!
模拟登陆失败原因分析:
1.检查分析抓取的登陆时发起的post请求对应的数据包:
该数据包是携带了cookie进行的请求发送,那么该cookie是什么时候存储到客户端的呢?一定是该请求的前次请求时产生的cookie。
在浏览器进行登陆请求发送之前,我们已经是第二次访问的github服务器端了,因为第一次是在浏览器中访问登陆页面时。
抓取该次请求的数据包,查看响应头信息中是否存在set-cookie,如果有,则证实该次请求时,服务器端给客户端创建了会话对象,且创建了cookie返回给了客户端进行存储。
果然存在set-cookie,因此,我们在使用requests模块进行模拟登陆时,发起的请求也是需要携带cookie的。那么cookie如何被携带到requests的请求中呢?
- requests模块处理cookie的两种方式:
1.将cookie手动从抓包工具中获取,然后封装到requests请求的headers中,将headers作用到请求方法中。(不建议)
headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36', 'Cookie':'xxxxxxxxx' }
2.创建会话对象,使用会话对象进行请求发送。因为会话中会自动携带且处理cookie。(推荐)
- 代码展示:
在使用会话对象发起登陆请求时,需要额外注意该请求参数:
’authenticity_token':'lGnVdPaEf/jfBd6dzkQFpd4idQZxO96HSvRzWIPo5099dvmZTqEx+Q13zLjQzYpE08YmZdJStHd+Vk6Yhz7t/Q==' ,
该参数的value值看起来像是一组密文数据,则该数据很有可能是动态变化的,因此需要动态捕获,动态赋值。如果该参数不处理的话,也是会登陆失败的。那么该值应该从那哪里动态捕获呢?这种值我们可以统一称为动态taken参数,该值一般都会动态存在与该请求对应的前台页面中,因此我们在登陆页面的源码中进行搜索:
因此我们可以通过数据解析,动态解析捕获到该请求参数的值。详细代码如下:
import requests from lxml import etree headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36' } #创建会话对象,该会话对象可以调用get和post发起请求 session = requests.Session() #使用会话对面对登录页面发起请求 page_text = session.get(url='https://github.com/login',headers=headers).text #解析出动态的taken值 tree = etree.HTML(page_text) t = tree.xpath('//*[@id="login"]/form/input[2]/@value')[0] #指定模拟登录请求的url url = 'https://github.com/session' #参数封装(处理动态taken值) data = { 'commit': 'Sign in', 'utf8': '✓', 'authenticity_token': t, 'login': 'bobo328410948@sina.com', 'password': 'bobo@15027900535', 'webauthn-support': 'supported', } #使用会话对象进行模拟登录请求发送(携带cookie) page_text = session.post(url=url,headers=headers,data=data).text #持久化存储 with open('./git.html','w',encoding='utf-8') as fp: fp.write(page_text)
查看git.html:登录成功