巧用 selenium 解决验证码,模拟登陆某流行网站
一、前言
1. 介绍
验证码多种多样,有图形文字的、有模拟点选的、有拖动滑动的,但其实归根结底都需要人来对某种情形做一些判断,然后把结果返回并提交。
如果此时提交的验证码结果是正确的,并且通过了一些验证码的检测,就能成功突破这个验证码了。既然验证码就是让人来识别的,那么机器怎么办呢?
如果我们也不会什么算法,怎么去解这些验证码呢?此时我们需要利用可以帮助我们来识别验证码的工具或平台就,让工具或平台把验证码识别的结果返回给我们,我们拿着结果提交,那不就好了吗?
有专门的打码平台帮助我们来识别各种各样的验证码,平台内部对算法和人力做了集成,可以 7x24 小时来识别各种验证码,包括识别图形、坐标点、缺口等各种验证码,返回对应的结果或坐标,正好可以解决我们的问题,比如超级鹰。
B站最新登录验证为点选验证码,以模拟登录 B 站来熟悉 selenium 库的使用和打码平台的使用方法。
这个验证码上面显示了几个汉字,同时在图中也显示了几个汉字,我们需要按照顺序依次点击汉字在图中的位置,点击完成之后确认提交,即可完成验证。这种验证码如果我们没有任何图像识别算法基础的话,是很难去识别的,所以这里我们可以借助打码平台来帮助我们识别汉字的位置。
2. 准备工作
本文用到的 Python 库是 Selenium,使用的浏览器为 Chrome,确保已经正确安装好 Selenium 库、Chrome 浏览器,并配置好 ChromeDriver,
使用的打码平台是超级鹰,链接为:https://www.chaojiying.com/,在使用之前请读者自行注册账号并获取一些题分供测试,另外还可以了解平台可识别的验证码的类别。
打码平台能提供的服务种类一般都非常广泛,可识别的验证码类型也非常多,其中就包括点触验证码。
超级鹰平台同样支持简单的图形验证码识别,超级鹰平台提供了如下一些服务:
英文数字:提供最多 20 位英文数字的混合识别; 中文汉字:提供最多 7 个汉字的识别; 纯英文:提供最多 12 位的英文识别; 纯数字:提供最多 11 位的数字识别; 任意特殊字符:提供不定长汉字英文数字、拼音首字母、计算题、成语混合、集装箱号等字符的识别; 坐标选择识别:如复杂计算题、选择题四选一、问答题、点击相同的字、物品、动物等返回多个坐标的识别;
本例需要处理的就是坐标多选识别的情况。我们先将验证码图片提交给平台,平台会返回识别结果在图片中的坐标位置,然后我们再解析坐标模拟点击,下面我们就用程序来实现。
二、实现思路
三、Python代码实现
1. 获取API
通过官方网站下载对应的 Python API,链接为:https://www.chaojiying.com/api-14.html。API 是 Python 2 版本的,用 requests 库来实现的。我们可以简单更改几个地方,即可将其修改为 Python 3 版本。
修改之后的 API 如下所示:
# -*- coding: UTF-8 -*-
# Chaojiying_Client类 用于提交要识别的图片 返回json结果
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf-8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
headers=self.headers)
logging.info(r.json())
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
logging.info(r.json())
return r.json()
这里定义了一个 Chaojiying 类,其构造函数接收三个参数,分别是超级鹰的用户名、密码以及软件 ID,保存以备使用。
最重要的一个方法叫作 post_pic,它需要传入图片对象和验证码类型的代号。该方法会将图片对象和相关信息发给超级鹰的后台进行识别,然后将识别成功的 json 返回。
另一个方法叫作 report_error,它是发生错误时的回调。如果验证码识别错误,调用此方法会返回相应的题分。
2. 分析B站登录界面
进入登陆界面,定位用户名和密码对应的标签,模拟输入我们的账号和密码,点击登录,此时页面会弹出验证码。
对进行验证码的获取,然后提交给「超级鹰」进行识别,接收到汉字的坐标后,处理数据,然后用动作链进行模拟点击操作,最后定位点击确认,实现成功登录 B 站。
3. 具体实现
导入用的库
from selenium import webdriver
from time import sleep
from PIL import Image
from selenium.webdriver import ActionChains
import random
import requests
from hashlib import md5
import logging
日志输出和 webdriver.Chrome() 配置
# 日志输出配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')
# 初始化一个webdriver.Chrome()对象
chrome_driver = r'D:\python\pycharm2020\chromedriver.exe'
options = webdriver.ChromeOptions()
# 关闭左上方 Chrome 正受到自动测试软件的控制的提示
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option("excludeSwitches", ['enable-automation'])
browser = webdriver.Chrome(options=options, executable_path=chrome_driver)
用 selenium 打开登录页面,进行模拟登录
# 登录函数 访问页面->输出账号、密码->点击登录
def login():
browser.get('https://passport.bilibili.com/login')
browser.maximize_window()
# ID定位用户名,密码输入框
username = browser.find_element_by_id('login-username')
password = browser.find_element_by_id('login-passwd')
username.send_keys('your username')
password.send_keys('your password')
# Xpath定位登录按钮并点击
browser.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[5]/a[1]').click()
sleep(random.random()*3)
将当前页面进行截图并保存下来,裁剪出验证码区域
def save_img():
# save_screenshot:将当前页面进行截图并保存下来
browser.save_screenshot('page.png')
# Xpath定位验证码图片的位置
code_img_ele = browser.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div')
location = code_img_ele.location # 验证码左上角的坐标x,y
size = code_img_ele.size # 验证码图片对应的长和宽
# 得到左上角和右下角的坐标
rangle = (
int(location['x'] * 1.25), int(location['y'] * 1.25), int((location['x'] + size['width']) * 1.25),
int((location['y'] + size['height']) * 1.25)
)
image1 = Image.open('./page.png')
# code_img_name = './code.png'
# crop根据rangle元组内的坐标进行裁剪 裁剪出验证码区域
frame = image1.crop(rangle)
frame.save('./code.png')
return code_img_ele
将点击登录后的页面进行截图,然后定位到验证码的位置,通过location()方法获取验证码左上角的坐标, size() 获取验证码的宽和高,左上角坐标加上宽和高就是验证码右下角的坐标。获取坐标后就可以用 crop() 方法来进行裁剪,然后将裁剪到的验证码图片保存。
缩小图片
def narrow_img():
# 缩小图片
code = Image.open('./code.png')
small_img = code.resize((169, 216))
small_img.save('./small_img.png')
print(code.size, small_img.size)
此时虽然获取了验证码图片,但是还不能直接提交给超级鹰。因为超级鹰识别的验证码图片的宽和高有限制,最好不超过460px*310px。但是截取到的验证码图片宽高为338px*432px,这时就要先将图片缩小一倍再提交即可,等到收到坐标数据再将坐标乘2。
将验证码提交给超级鹰进行识别
def submit_img():
# 将验证码提交给超级鹰进行识别
# 用户中心->软件ID 生成你的软件ID->替换掉96001 绑定微信可以得到1000积分 免费使用
chaojiying = Chaojiying_Client('你的用户名', '密码', '生成的软件ID')
with open('./small_img.png', 'rb') as f:
im = f.read()
# 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
result = chaojiying.PostPic(im, 9004)['pic_str']
logging.info(result)
return result
调用已获取的API,传入参数:'你的用户名', '密码','生成的软件ID'
解析返回的汉字的坐标点的结果
def parse_data(result):
node_list = [] # 存储即将被点击的点的坐标 [[x1,y1],[x2,y2]]
print(result)
if '|' in result:
nums = result.split('|')
for i in range(len(nums)):
x = int(nums[i].split(',')[0])
y = int(nums[i].split(',')[1])
xy_list = [x, y]
node_list.append(xy_list)
else:
print(result.split(',')[0])
print(result.split(',')[1])
x = int(result.split(',')[0])
y = int(result.split(',')[1])
xy_list = [x,y]
node_list.append(xy_list)
return node_list
超级鹰识别返回的结果的数据格式为:98,136|87,77 我们可以将数据以' | '进行分割,保存到列表中,再以逗号分割将x,y的坐标保存,得到[ [123,12],[234,21] ]这一格式,然后遍历这一列表,使用动作链对每一个列表元素对应的x,y指定的位置进行模拟点击操作,最后定位并点击确认,可成功实现登录 B 站。
按顺序模拟点击每个坐标点
def click_codeImg(all_list, code_img_ele):
# 遍历列表,使用动作链对每一个列表元素对应的x,y指定的位置进行点击操作
for item in all_list:
x = item[0] * 1.6
y = item[1] * 1.6
# move_to_element_with_offset移动到距某个元素(左上角坐标)多少距离的位置
ActionChains(browser).move_to_element_with_offset(code_img_ele, x, y).click().perform()
sleep(random.random())
logging.info('点击成功!')
sleep(random.random()*2)
# 完成动作链点击操作后,定位确认按钮并点击
browser.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[3]/a').click()
主函数调用
# -*- coding: UTF-8 -*-
def main():
# 进入登录界面,输入账号密码
login()
# 保存页面截图,并根据坐标裁剪获取验证码图片
code_img_ele = save_img()
# 缩小图片
narrow_img()
# 将图片提交给超级鹰,获取返回的识别结果
result = submit_img()
# 解析返回结果,将数据格式化
all_list = parse_data(result)
# 在页面验证码上完成点击操作并登录
click_codeImg(all_list, code_img_ele)
main()
运行效果如下:
鼠标是放在左边的,没有动过,不是人工悄悄点的哦,这样我们就成功实现了 selenium 模拟登录 B 站。
作者:叶庭云
CSDN:https://yetingyun.blog.csdn.net/
本文来自公众号读者投稿,欢迎各位童鞋向公号投稿,点击下面图片了解详情!
-END-