本篇推文会涉及到requests和lxml库的使用,同时需要读者对于cookie和session有一定的了解(没有了解也不影响本文的阅读),虽然我没有用到"re"模块,但是我用到了lxml中的xpath,因此若读者能对正则表达式有一定的了解,可能会更加随心应手,我会在文章最后对相关知识做以补充.
1.怎么理解模拟登陆?
把这句话补全就是 "怎么(让机器人)模拟(人在浏览器上的行为)登陆(指定网站)"
2.人在登陆时做了哪些事情呢?
I.打开登陆页面
II.输入用户名和密码(有时可能还有验证码)
III.点击登陆
IV.等待浏览器自动跳转
3.那么机器怎么做呢?
方法一:
使用外部Cookies(Cookies与Session相关知识会在下面做以介绍)
思路提供:
通过获取已经的登陆过的网站Cookie,将其保存在本地,通过代码的形式将Cookie在发送至服务端,服务端会查找相关该用户的记录.(当然这个完美的臆想是建立在Cookie是在有效期之内的).
由于网上的源码及分析已经非常完美了,所以我就直接copy了过来(没错,就是这么懒
)<https://blog.csdn.net/c406495762/article/details/69817490>,哪里不会了,随时找我哟
方法二:
需要使用虚拟的浏览器引擎(本文中的代码示例就是使用此方法)
优点:几乎适合所有网站登陆,可以人为输入验证码
缺点:速度较慢
思路提供:
分析豆瓣网登陆界面(https://accounts.douban.com/login),可以发现,用户若想实现成功登陆,只需输入正确的账号、密码(有时可能还有验证码),这简直是废话
.填入正确的表单信息之后,打开开发者工具,点击Networkd项,并点击提交,可以看到控制台中第一项名为"login"的数据包,点击打开,可以看到如下信息,会看到刚刚填写的表单信息都在"Form Data"中出现了,还多余了几项.
Form Data信息分析:
source:index_nav
form_email:1306846969@qq.com
form_password:*********
captcha-solution:"这是图片验证码"
captcha-id:"这是每次提交的令牌"
由以上分析可知,对于本项目来说,由于账号和密码的提交已经在代码中实现,所以现在每次提交只需要手动输入"图片验证码"即可实现登陆,因此现在的需求已经很明确了,我们需要用代码实现的就是:请求豆瓣网登陆界面(用户状态为未登陆),获取"每次提交的令牌",即控制台中"captacha-id"一项,并保存验证码图片到本地,然后再将所有数据通过请求登陆界面url的方式实现数据的提交(若登陆成功,会跳转至主页面<https://www.douban.com/>).
相关知识补充:
4.Session管理及Cookie应用:
由于HTTP是无状态协议,之前已认证成功的用户状态无法通过协议层面保存下来,即无法实现状态管理,因此即使当用户下一次继续访问,也无法区分他与其他的用户.于是我们会使用Cookie来管理Session,以弥补HTTP协议中不存在的状态管理功能.步骤:
(1)客户端把用户ID和密码登陆信息放入报文的实体部分,通常是以POST方法把请求发送给服务器.
(2)服务器会发放用以识别的Session ID.通过验证从客户端发送过来的登陆信息进行身份认证,然后把用户的认证状态与Session ID绑定后记录在服务器端.向客户端返回响应时,会在首部字段Set-Cookie内写入Session ID(为了防止Session ID被盗,Session ID应使用难以推测的字符串,且服务端也应进行有效期的管理)
(3)客户端接收到服务端的Session ID后,会将其作为Cookie保存在本地(第5部分介绍了查看浏览器Cookie的方法).下次向服务端发送请求时,浏览器会自动发送Cookie,所以Session ID也随之发送到服务器.服务端可通过验证接收到的Session ID识别用户和其认证状态.
总而言之,客户端请求服务端时其Session与Cookie的大致传递过程如下,用人话来讲就是:用户按要求填写完表单信息后,点击提交按钮,即发送请求至服务端,服务端会首先查找有没有与此用户相关的记录,如果有,直接返回之前的登陆信息即可;如果没有,服务端会创建一个Session ID(用来标记用户信息),并将Session ID以写入Set-Cookie的方式发送给客户端,客户端将其保存再Cookie里面,再下一次请求时,又发送给服务端.
第一次登陆"码云"官网:(服务端将Session ID通过写入Set-Cookie的方法传递给客户端,客户端将其保存在浏览器Cookie中)
第二次请求"码云"官网:(客户端携带首次请求的Session ID<Cookie>发送至服务端,服务端查找相关的记录)
如果上面的内容对于聪明的你来说是a piece of cake的话,那么我在提出一个无聊的问题,大家耐着性子思考一下.废话不多说,大家上车坐稳,我要开车了
问:默认我是在Google上登陆的我的码云账号,如果我将登陆成功页面的URL复制并将其再Firefox浏览器中打开,你们猜会有什么结果呢?
答:随便猜,反正浏览器不会突然弹出一句"你好帅",这是肯定的了
,好,排除一个错误答案,与正确答案还相差100%的距离.好了,言归正传,告诉你们结果吧,在Firefox打开的页面显示用户登陆信息为未登录,请看图片:
在Google上登陆码云账号:
在Firefox上登陆效果展示:
聪明的你肯定想问了,请求相同的网址,却有不同的展示效果,这是再逗我吗?
答案其实很简单,由于博主默认是在"Google"进行的登陆,所以服务端保存的是Google的相关Session,Google的Cookie也是服务端响应的结果.而通过"Firefox"打开相同的网址,由于Firefox之前并没有码云的服务器有过会话连接(没有请求过码云的服务器),因此Firefox并不会保存在Google登陆时的状态.
5.如何再浏览器查看Cookie:
谷歌浏览器:
火狐浏览器:
6.举个栗子
(1)想必见多识广的你一定见过如下的网页吧:
我的问题是:用户点击"下次自动登陆"与没有点击有啥区别呢?
如果你勾选了它之后再进行登陆,那么在下一次访问该网站的时候就不需要进行重复而繁琐的登陆动作了,而这个功能就是通过cookie实现的.此时,客户端或者服务端会设置一个Cookie的有效时间,只要再有效时间内登陆网站都不用进行繁琐的信息输入(只要Cookie没有过期)
(2)在举个大蒜,你就知道Cookies和Session对于我们有多重要了.在问你一个无聊透顶的问题,没错我就是这么无聊
,某宝的购物车大家都用过吧,想象一个场景,假设我现在看中了一款戒指?,可是我还得征求girlfriend的意见,看她喜欢吗,所以先将其加入购物车,等到一个月之后(这里只是做个假设,某宝的cookie有效期好长的
),见到了girlfriend,她说喜欢,于是我就买了这个过程大概是怎样的,第二波无聊的问题
,为什么将商品添加至购物车是添加到我的账号中(默认现在登陆的是我的状态)而不是隔壁老东的购物车呢?
很简单,购物车功能的实现一般是依托Cookie和Session及数据库,通过Session和Cookie实现用户的跟踪,通过数据库来记录用户的信息.先解释第一个问题,当我点击戒指这款商品将其加入购物车,某宝的数据库会添加这项商品的信息,并且其服务端生成一个Session ID并颁发给客户端(即服务端不用存储数据,减轻了压力),客户端将其保存在Cookie中,并且给Cookie设置一个好长好长的有效时间,由于数据库amazing的存储能力,所以在相当一段时间内这个戒指都会在我的购物车内.再看第二个问题,默认每一个用户都有一个唯一的某宝账号(我们要访问外国网站哟
),所以服务端会根据每个用户的信息(包括账号、密码等)绑定不同的Session ID,因此就不会出现本来是你想添加的商品却添加到@老东的购物车里来的窘境.
下面来看一波教科书级别的讲解(直接copy的
)
下面介绍三种实现购物车功能的方法(当然此购物车并不是沃尔玛里面的实体购物车哟):
(1)通过Cookie
聪明的你一定知道,Cookie是由服务端产生,并存储在客户端的一段信息.Cookie文件包含域、路径、生存期和由服务器设置的变量值等内容.当用户以后访问同一个Web服务器时,客户端会把Cookie原样发送给服务器,通过让服务器读取原先保存到客户端的信息,网站能够为浏览者提供一系列的方便,例如在线交易过程中标识用户身份、安全要求不高的场合避免用户重复输入名字和密码、门户网站的定制、有针对性的投放广告等等.由于Cookie可以由服务端定制,因此可以将购物信息生成Cookie并保存在客户端,从而实现购物车的功能
特点分析:
Cookie是不可执行文件,因此不会带来任何病毒或攻击用户的系统
Cookie为浏览器内置,使用方便.只要在Cookie定义的有效期内访问,购物车的信息都不会丢失
Cookie存贮在客户端,且占用很少的资源(浏览器允许存放300个Cookie,每个Cookie的大小为4KB,足以满足购物车的要求,同时也减轻了服务端的负荷)
先决条件:客户端浏览器必须启用Cookie,否则一切都是浪费感情
(2)通过Session:
与Cookie最大的区别是:session将用户在会话期间的私有信息存储在服务端,提高了安全性.在服务端生成Session后,服务端会颁发一个SessionID号给客户端并保存在Cookie中,如果客户端禁止Cookie功能,Session会通过在URL中附加参数或隐含在表单中提交等其他方式在页面中传送.
特点分析:
时刻保持与客户端的同步,不依赖于客户端的设置
会占用服务端资源,尤其当并发用户很多时,影响服务器的性能.
(3)结合数据库:
利用Cookie和Session跟踪用户,数据库用来存储信息.
毫无疑问,每一步操作都涉及对于数据库的操作,因此就要求数据库的性能很牛了.
详情参考
<购物车的实现>
http://blog.51cto.com/rndn11/1158712
重磅炸弹,上车坐稳,代码奉上
7.源码分享:
import requests
from lxml import etree
#协议头的伪造
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'
}
#模拟登陆的实现
def login():
res = requests.session().get("https://accounts.douban.com/login",
headers=headers)
selector = etree.HTML(res.text)
#获取图片地址
codeImgUrl = selector.xpath('//div//img[1]/@src')[1]
#获取图片ID
captchaId = codeImgUrl.split("?id=")[1].split('&')[0]
#获取验证码图片并存入本地文件
response1 = requests.get(codeImgUrl,headers=headers)
codeImg = response1.content
with open('img.png','wb') as f:
f.write(codeImg)
#表单验证时,需要提交数据的伪造
data = {
'source': 'index_nav',
'form_email': '1306846969@qq.com',
'form_password': 'spd981002',
'captcha-solution': input("请输入验证码:"),
'captcha-id': captchaId
}
response = requests.post("https://www.douban.com/accounts/login",
data=data, headers=headers)
print(response.text)
'''
当登陆成功时,会跳转到主页面,由于此时已经处于登陆状态,所以页面中势必会有
用户的信息.以我的账号登陆为例,当页面中出现"Stone"字符
(或者其它标识性的字符也可以)的时候,代表登陆成功.
'''
if "Stone" in response.text:
print("登陆成功")
else:
print("登陆失败,正在重新登陆")
#若登陆失败,则无限次递归调用登陆函数
login()
return
if __name__=="__main__"
login()