————————————————————————————————
一、项目回顾
在上一篇文章中《【网站密码管理不用愁】基础篇 • 利用selenium构建网站密码管理和自动登录神器》
我分享了如何通过python+selenium来构建网站密码管理和自动登录神器。
用来解决我们在平时工作生活中遇到的各种网站太多、账号密码难管理的问题。
我们只需要打开运行窗口,输入我们设定的关键词,就能一键启动网站并自动输入账号和密码,实在方便得不要不要的。
以后麻麻再也不用担心我记不住账号、找不到密码啦!
同时,也有小伙伴在评论区留言道
对吼!
现在我们有很多网站登录不仅是要输入账号密码,还要输入验证码。
而且还是多种款式的验证码各不相同。
——
二、问题分析
作为稀里糊涂自学python三个月,编程知识渊博(qian bo)的我,看到这个问题自然是一脸懵逼。
所以恣意地在网上搜索,发现这个小儿科的问题早已被各路网友大神分分钟钟解决掉过。
大神们的骚操作极多,什么验证码识别训练模型、图像识别计算滑块验证码的距离等等。
溜了溜了,反正我是学不来。
从我们个人的实际需求出发,我们是希望建立一种能够快速登录网站的方式,从而方便我们的工作生活,解决时间,提高效率。
我们的核心目的是实现网站的快速登录,而非正面硬刚验证码环节,所以我们可以采用绕过验证码的方式登录网站。
所以经过我细致探索,发现利用selenium本身具有的add_cookie方法就能够成功解决需要验证码才能登录的问题。
那么首先让我们来搞清楚通过cookie免密免验证码登录的原理是什么。
简单来说,cookie就是网站存储在我们浏览器上的一小段数据,里面有我们个人用户的信息。
如果我们首次登录网站后,浏览器记住了我们登录后的信息(也就是cookie),当下一次请求时会把该 cookie 发送给网站,之后网站服务器发现客户端发送过来的 cookie 后,会检查是哪个客户端发送过来的请求,然后对比服务器上的记录,最后得到了之前的状态信息。
这也是我们在登录网站时候勾选【下次自动登录】后,以后不用输入账号和密码自动登录网站的原因。
再通俗点讲,cookie记住了我们登录网站后的状态,我们下次登录网站的时候浏览器会把这些cookie发送给网站,从而直接进入到登录后的状态,也就不需要输入账号密码,更不用什么验证码之类的呢。
——
三、利用cookie绕过验证码环节
百度在输入账号和密码之后,会出现图形验证码,要求拖动滑块使图片角度为正,因为每次需要拖动的距离不一样,所以不能像以前那样控制鼠标事件一拖到底(以前百度的验证码是直接拖到底那种)。
本次我们以实战登录百度为例,看看如何通过cookie绕过百度的图形滑块验证码,从而强上百度。
【01】获取cookie
首先我们明确下获取cookie的流程
- 通过python+selenium进行到自动输入账号密码后的流程(为了练习)
- 手动滑动图形验证码并实现成功登录
- 利用get_cookie()方法获取我们登录后的cookie
简单三步走,完美!
我们先来编写第一步的代码,再来练习一下如何定位目标元素、如何控制键盘事件。
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver= webdriver.Chrome()
driver.get('https://www.baidu.com/')
driver.maximize_window() # 浏览器最大化
driver.find_element_by_css_selector('#u1 .lb').click() # 点击登录
time.sleep(2) # 需要加载一段时间,否则找不到页面元素
driver.find_element_by_css_selector('#TANGRAM__PSP_10__footerULoginBtn').click() # 切换到使用账号密码登录
time.sleep(1) # 等页面加载一段时间
driver.find_element_by_id('TANGRAM__PSP_10__userName').send_keys('******') # 输入自己的账号
driver.find_element_by_id('TANGRAM__PSP_10__password').send_keys('******') # 输入自己的密码
driver.find_element_by_id('TANGRAM__PSP_10__password').send_keys(Keys.ENTER) # 回车确定登录
time.sleep(20) # 等待较长时间,确保成功登录(因为需要获得登录后的cookie)
cookies = driver.get_cookies() # 获取cookie
print(cookie) # 打印cookie
其中对部分代码进行重点说明
- 我们导入了time库,在适当位置进行强制等待,确保页面元素都加载出来,尤其在最后我们进行手动拖动滑块进行验证的环节,一定要留出充足的时间等待进入登录成功后的页面再获取其cookie
然后第二步我们运行程序,selenium帮我们自动登录网站并输入账号密码,进入到验证码环节,这时候我们手动拖动验证码进行登录。
最后就让我们静静等待程序获取cookie。
最后的cookie是这样子的
这一大团密密麻麻的是什么玩意?
我们仔细看一下,其实就是有多个字典组成的列表,每个字典中又有多个键值对。
【02】优化cookie
首先我们要搞明白为什么要对获取到的cookie,因为上面我们得到那一串cookie的列表是无法直接通过add_cookie()方法添加进去的。
会出现两个问题:
- add_cookie()方法中只能传入字典,所以我们需要先将cookie列表中的每一个字典单独提出来。
- 计算将单个字典提出来传入到方法add_cookies()中,在运行程序的时候会报错: selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: invalid ‘expiry’ 。最简单有效的方法就是直接将“expiry”这对键值对删除掉。
既然明确了以上两个问题,那么我就知道该如何对获取的cookie进行优化
- 用for循环将单个的字典从列表中提取出来
- 同时检查字典中是否存在键名为“expiry”的键值对,如果有就删除掉
- 打印构造的字符串,方便等会直接复制粘贴
for i in cookies:
if 'expiry' in i: # 判断"expiry"是否在字典中
del i['expiry'] # 如果就删除该键值对
print('driver.add_cookie('+str(i)+')') #打印构造的字符串,方便等会直接复制粘贴
其中注意因为 i 是一个一个的字典,所以需要强制转换为字符串类型。
最终我们就获取了一条条已经传入到add_cookie()方法里的字符串了。(为了防止信息泄露,我部分进行了马赛克处理)
【03】添加cookie
因为我们在上一步已经做好了cookie优化工作,所以我们直接对其进行复制粘贴到代码中即可。
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver= webdriver.Chrome()
driver.get('https://www.baidu.com/')
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '1454_21122_30211_20697_30347_30327_30283_28701'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': True, 'name': 'BDUSS', 'path': '/', 'secure': False, 'value': ''})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': ''})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': 'F'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'delPer', 'path': '/', 'secure': False, 'value': '0'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': ''})
driver.add_cookie({'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '1'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'BDORZ', 'path': '/', 'secure': False, 'value': 'B490B5EBF6F3CD402E515D22BCDA1598'})
driver.add_cookie({'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': ''})
driver.refresh() #刷新页面
driver.maximize_window()
(为了防止信息泄露,我对部分关键信息代码进行了删除)
我们最后看下程序运行的结果
注意看右上角,你会发现直接由未登录状态“唰”地一下跳到了已登录账号的状态,中间没有输入账号、密码以及烦人的验证码过程。
是不是感觉超棒!
——
四、复盘总结
在最后列出完整的代码
一个是获取并优化cookie的代码
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver= webdriver.Chrome()
driver.get('https://www.baidu.com/')
driver.maximize_window() # 浏览器最大化
driver.find_element_by_css_selector('#u1 .lb').click() # 点击登录
time.sleep(2) # 需要加载一段时间,否则找不到页面元素
driver.find_element_by_css_selector('#TANGRAM__PSP_10__footerULoginBtn').click() # 切换到使用账号密码登录
time.sleep(1) # 等页面加载一段时间
driver.find_element_by_id('TANGRAM__PSP_10__userName').send_keys('******') # 输入自己的账号
driver.find_element_by_id('TANGRAM__PSP_10__password').send_keys('******') # 输入自己的密码
driver.find_element_by_id('TANGRAM__PSP_10__password').send_keys(Keys.ENTER) # 回车确定登录
time.sleep(20) # 等待较长时间,确保成功登录(因为需要获得登录后的cookie)
cookies = driver.get_cookies() # 获取cookie
print(cookie) # 打印cookie
print('\n')
for i in cookies:
if 'expiry' in i: # 判断"expiry"是否在字典中
del i['expiry'] # 如果就删除该键值对
print('driver.add_cookie('+str(i)+')') #打印构造的字符串,方便等会直接复制粘贴
另一个是添加cookie登录网站的代码
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver= webdriver.Chrome()
driver.get('https://www.baidu.com/')
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '1454_21122_30211_20697_30347_30327_30283_28701'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': True, 'name': 'BDUSS', 'path': '/', 'secure': False, 'value': ''})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': ''})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': 'F'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'delPer', 'path': '/', 'secure': False, 'value': '0'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': ''})
driver.add_cookie({'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '1'})
driver.add_cookie({'domain': 'baidu.com', 'httpOnly': False, 'name': 'BDORZ', 'path': '/', 'secure': False, 'value': 'B490B5EBF6F3CD402E515D22BCDA1598'})
driver.add_cookie({'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': ''})
driver.refresh() #刷新页面
driver.maximize_window()
整个代码中需要注意的要点有:
- 需要手动进入登录后的界面,并确保程序获取的是登录后状态的cookie,所以时间一定要足够充足,我设定的是time.sleep(20)
- 通过get_cookie()获取的是多个字典的列表,我们需要用for循环单独提出来
- 如果字典中有“expiry”的键值对,需要删除否则会造成程序报错
- 为了方便直接复制粘贴,我们在打印的时候直接构造完整的字符串,其中每个字典需要强制转换为字符串类型