目标

如下图,利用selenium模拟拖动滑块完成验证

关键问题

我们知道selenium可以定位到用户名和密码,用send_keys可以实现输入账号密码。同样我们可以用selenium定位到滑块,用click_and_hold方法可以实现拖动滑块。

关键问题是:

1)如何计算滑块拖动的距离?
2)知道了滑块拖动的距离后,如何设计路径,使得浏览器操作更像人而不被识别出来。

思路

1)滑块拖动距离的计算

通过隐藏滑块,截取只含缺口图片bg_image,再截取既不含滑块又不含缺口的图片fullbg_image
对比两张图的差异,可以得到缺口的位置x1

截取含有滑块和缺口的图片slice_image,对比fullbg_image,可以得到滑块的初始位置x2;

滑块拖动的距离x=x2-x1

2)路径的设计

由牛顿第二定律衍生出的位移计算公式为
l=vt+1/2at*t
把滑块拖动的距离x拆分成l1,l2,l3,l4…每一段设置不同的a和t(后一段的初始速度为前一段的结束速度),a可以设置先增后减,t可以设置随机数

爬虫实施

Step1:初始化

import time
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

Step2:用Selenium打开目标网站

url = 'https://passport.bilibili.com/login'
driver = webdriver.Chrome(r"C:\Users\ThinkPad\AppData\Local\Google\Chrome\Application\chromedriver.exe")
wait = WebDriverWait(driver, 20)
phone_number = "18519146290"
passwd="xc080218"
driver.get(url)
driver.maximize_window()

Step3:定位账号密码输入框,并输入参数,点击登录

send_phone=driver.find_element_by_xpath('//div[@class="item username status-box"]/input')
send_phone.send_keys(phone_number)
send_passwd=driver.find_element_by_xpath('//div[@class="item password status-box"]/input')
send_passwd.send_keys(passwd)
button = wait.until(EC.element_to_be_clickable((By.XPATH, '//div[@class="btn-box"]/a[@class="btn btn-login"]')))
button.click()

这里使用了显式等待

Step4:依次定位到缺口图片、滑块位置、完整图片

c_background=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_bg.geetest_absolute')))
c_slice=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_slice.geetest_absolute')))
c_full_bg=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))

Step5:隐藏滑块,截取全图,根据c_background裁剪只含缺口的图片

driver.execute_script("arguments[0].style=arguments[1]",c_slice,"display: none;")
obj=c_background
name='back'try:pic_url=driver.save_screenshot('.\\bilibili.png')print("%s:截图成功!" % pic_url)#获取元素位置信息left = obj.location['x']top = obj.location['y']right = left + obj.size['width']bottom = top + obj.size['height']print('图:'+name)print('Left %s' % left)print('Top %s' % top)print('Right %s' % right)print('Bottom %s' % bottom)print('')im = Image.open('.\\bilibili.png')im = im.crop((left, top, right, bottom))    #元素裁剪file_name='bili_'+name+'.png'im.save(file_name)    #元素截图except BaseException as msg:print("%s:截图失败!" % msg)

Step6:显示滑块,截图,根据c_slice裁剪既含滑块又含缺口的图片

driver.execute_script("arguments[0].style=arguments[1]",c_slice,"display: block;")
obj=c_slice
name='slice'try:pic_url=driver.save_screenshot('.\\bilibili.png')print("%s:截图成功!" % pic_url)#获取元素位置信息left = obj.location['x']top = obj.location['y']right = left + obj.size['width']bottom = top + obj.size['height']print('图:'+name)print('Left %s' % left)print('Top %s' % top)print('Right %s' % right)print('Bottom %s' % bottom)print('')im = Image.open('.\\bilibili.png')im = im.crop((left, top, right, bottom))    #元素裁剪file_name='bili_'+name+'.png'im.save(file_name)    #元素截图except BaseException as msg:print("%s:截图失败!" % msg)

Step7:

driver.execute_script("arguments[0].style=arguments[1]",c_full_bg,"display: block;")
obj=c_full_bg
name='full'try:pic_url=driver.save_screenshot('.\\bilibili.png')print("%s:截图成功!" % pic_url)#获取元素位置信息left = obj.location['x']top = obj.location['y']right = left + obj.size['width']bottom = top + obj.size['height']print('图:'+name)print('Left %s' % left)print('Top %s' % top)print('Right %s' % right)print('Bottom %s' % bottom)print('')im = Image.open('.\\bilibili.png')im = im.crop((left, top, right, bottom))    #元素裁剪file_name='bili_'+name+'.png'im.save(file_name)    #元素截图except BaseException as msg:print("%s:截图失败!" % msg)

Step8:通过对比像素点,得到两张图像素点第一次不一致时的横坐标

fullbg_image=Image.open('.\\bili_full.png')
bg_image=Image.open('.\\bili_back.png')
# 遍历像素点横坐标
for i in range(fullbg_image.size[0]):# 遍历像素点纵坐标for j in range(fullbg_image.size[1]):bg_pixel = bg_image.load()[i, j]# 获取完整图片的像素点(按照RGB格式)fullbg_pixel = fullbg_image.load()[i, j]# 设置一个判定值,像素值之差超过判定值则认为该像素不相同threshold = 10# 判断像素的各个颜色之差,abs()用于取绝对值if not (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):# 如果差值在判断值之内,返回是相同像素distance=ibreak  else:continueelse:continuebreak

Step9:设计滑块路径,输出轨迹字典

# 创建存放轨迹信息的列表
trace = []
# 设置加速的距离
distance=distance*1.68
faster_distance = distance*(4/5)
# 设置初始位置、初始速度、时间间隔
start, v0, t = 0, 0, 0.1
# 当尚未移动到终点时
while start < distance:# 如果处于加速阶段if start < faster_distance:# 设置加速度为2a = 1.5# 如果处于减速阶段else:# 设置加速度为-3a = -3# 移动的距离公式move = v0 * t + 1 / 2 * a * t * t# 此刻速度v = v0 + a * t# 重置初速度v0 = v# 重置起点start += move# 将移动的距离加入轨迹列表trace.append(round(move,2))

看一下这一段代码的输出:

127.68的距离被拆分成了134小段,步长先快递增加后缓慢减少

Step10: 模拟滑块移动,每移动一小段,停歇一小段时间

slider=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'div.geetest_slider_button')))
# 使用click_and_hold()方法悬停在滑块上,perform()方法用于执行
ActionChains(driver).click_and_hold(slider).perform()
for x in trace:# 使用move_by_offset()方法拖动滑块,perform()方法用于执行ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()
# 模拟人类对准时间time.sleep(0.02)
# 释放滑块
ActionChains(driver).release().perform()

Step11:distance修正

运行Step10中的代码,会发现滑块按照计算轨迹滑动后无法与缺口吻合,笔者尝试手动修改distance促成滑块吻合,得到多组数据,用Sklearn跑一次线性回归

import pandas as pd
data=pd.read_excel(r"E:\崔庆才爬虫\七月在线\滑块测试.xlsx")
data=data.sort_values("x")
print(data)import matplotlib.pyplot as plt
plt.scatter(data["x"].values,data["y"].values)
plt.show()from sklearn import linear_model        #表示,可以调用sklearn中的linear_model模块进行线性回归。
import numpy as np
model = linear_model.LinearRegression()
model.fit(data["x"].values.reshape(-1,1),data["y"].values)
display(model.intercept_)  #截距
display(model.coef_)


得出线性回归方程:y=1.29*x+34

But,带入代码中,发现时灵时不灵。。。
很急,不知道咋搞了,以后再说!

完整代码:

# -*- coding: utf-8 -*-from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
import time
import random
import os
from PIL import Image# 初始化# 登录界面的url
url = 'https://passport.bilibili.com/login'
# 实例化一个chrome浏览器
browser = webdriver.Chrome(r"C:\Users\ThinkPad\AppData\Local\Google\Chrome\Application\chromedriver.exe")
# 用户名
username = '18519146290'
# 密码
password = 'xc080218'
# 设置等待超时
wait = WebDriverWait(browser, 20)# 登录
def login():# 打开登录页面browser.get(url)browser.maximize_window()# 获取用户名输入框user = wait.until(EC.presence_of_element_located((By.ID, 'login-username')))# 获取密码输入框passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd')))# 输入用户名user.send_keys(username)# 输入密码passwd.send_keys(password)#获取登录按钮login_btn=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'a.btn.btn-login')))#随机延时点击time.sleep(random.random() * 3)login_btn.click()#设置元素可见
def show_element(element):browser.execute_script("arguments[0].style=arguments[1]",element,"display: block;")
#设置元素不可见
def hide_element(element):browser.execute_script("arguments[0].style=arguments[1]",element,"display: none;")#对某元素截图
def save_pic(obj,name):try:pic_url=browser.save_screenshot('.\\bilibili.png')print("%s:截图成功!" % pic_url)#获取元素位置信息left = obj.location['x']top = obj.location['y']right = left + obj.size['width']bottom = top + obj.size['height']print('图:'+name)print('Left %s' % left)print('Top %s' % top)print('Right %s' % right)print('Bottom %s' % bottom)print('')im = Image.open('.\\bilibili.png')im = im.crop((left, top, right, bottom))    #元素裁剪file_name='bili_'+name+'.png'im.save(file_name)    #元素截图except BaseException as msg:print("%s:截图失败!" % msg)def cut():c_background=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_bg.geetest_absolute')))c_slice=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_slice.geetest_absolute')))c_full_bg=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))hide_element(c_slice)save_pic(c_background,'back')show_element(c_slice)save_pic(c_slice,'slice')show_element(c_full_bg)save_pic(c_full_bg,'full')# 判断像素是否相同
def is_pixel_equal(bg_image, fullbg_image, x, y):""":param bg_image: (Image)缺口图片:param fullbg_image: (Image)完整图片:param x: (Int)位置x:param y: (Int)位置y:return: (Boolean)像素是否相同"""# 获取缺口图片的像素点(按照RGB格式)bg_pixel = bg_image.load()[x, y]# 获取完整图片的像素点(按照RGB格式)fullbg_pixel = fullbg_image.load()[x, y]# 设置一个判定值,像素值之差超过判定值则认为该像素不相同threshold = 20# 判断像素的各个颜色之差,abs()用于取绝对值if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):# 如果差值在判断值之内,返回是相同像素return Trueelse:# 如果差值在判断值之外,返回不是相同像素return False# 计算滑块移动距离
def get_distance(bg_image, fullbg_image):''':param bg_image: (Image)缺口图片:param fullbg_image: (Image)完整图片:return: (Int)缺口离滑块的距离'''# 滑块的初始位置distance = 60# 遍历像素点横坐标for i in range(distance, fullbg_image.size[0]):# 遍历像素点纵坐标for j in range(fullbg_image.size[1]):# 如果不是相同像素if not is_pixel_equal(fullbg_image, bg_image, i, j):# 返回此时横轴坐标就是滑块需要移动的距离return i# 构造滑动轨迹
def get_trace(distance):''':param distance: (Int)缺口离滑块的距离:return: (List)移动轨迹'''# 创建存放轨迹信息的列表trace = []# 设置加速的距离faster_distance = distance*(4/5)# 设置初始位置、初始速度、时间间隔start, v0, t = 0, 0, 0.2# 当尚未移动到终点时while start < distance:# 如果处于加速阶段if start < faster_distance:# 设置加速度为2a = 1.5# 如果处于减速阶段else:# 设置加速度为-3a = -3# 移动的距离公式move = v0 * t + 1 / 2 * a * t * t# 此刻速度v = v0 + a * t# 重置初速度v0 = v# 重置起点start += move# 将移动的距离加入轨迹列表trace.append(round(move))# 返回轨迹信息return trace# 模拟拖动
def move_to_gap(trace):# 得到滑块标签#slider = wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'gt_slider_knob')))slider=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'div.geetest_slider_button')))# 使用click_and_hold()方法悬停在滑块上,perform()方法用于执行ActionChains(browser).click_and_hold(slider).perform()for x in trace:# 使用move_by_offset()方法拖动滑块,perform()方法用于执行ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()# 模拟人类对准时间time.sleep(0.5)# 释放滑块ActionChains(browser).release().perform()def slide():distance=get_distance(Image.open('.\\bili_back.png'),Image.open('.\\bili_full.png'))*1.29+34print('计算偏移量为:%s Px' % distance)# 计算移动轨迹trace = get_trace(distance)# 移动滑块move_to_gap(trace)time.sleep(3)os.chdir(r"E:\崔庆才爬虫\七月在线")
login()
cut()
slide()

python爬虫进阶-滑块验证码破解(bilibili)相关推荐

  1. 极验第四代滑块验证码破解(四):请求分析及加密参数破解

    极验第四代滑块验证码破解(四):请求分析及加密参数破解 声明 一.极验请求分析 1. 滑块测试网站入口 2. 滑块验证过程抓包 3. 请求详解 3.1. adaptive-captcha-demo 3 ...

  2. 极验第四代滑块验证码破解(三):滑块轨迹构造

    极验第四代滑块验证码破解(三):滑块轨迹构造 声明 一.极验滑动轨迹分析 1. 生成滑动轨迹的js入口 2. 滑动轨迹的python实现 二.结语 *本期文章结束啦,如果对您有帮助,记得收藏加关注哦, ...

  3. 极验滑块验证码破解与研究(二):缺口图片还原

    极验滑块验证码破解与研究(二):缺口图片还原 声明 一.缺口图片还原js分析 1. 为什么需要还原 2. 本篇文章需要用到的小工具 2.1. reres插件 3. 找到图片还原js入口函数 3.1. ...

  4. 极验滑块验证码破解最新版

    一.简述: 最近无聊想搞一下极验的滑块验证码破解这块,发现破解js代码耗时又耗力出现版本更新可能以前的所有努力都要推翻重做,不够通用性,最后还是选用selenium + PIL 来实现滑块验证码的破解 ...

  5. 实战 | 手把手教你用Python+OpenCV实现滑块验证码->自动拖动验证

    导读 本文主要介绍如何使用Python+OpenCV实现滑块验证码->自动拖动验证. 背景介绍 前几天在某网站下载代码时,跳转到滑块验证码界面,需要验证OK后才能下载,貌似这种验证方式现在很流行 ...

  6. Selenium模拟登录系列 | 淘宝滑块验证码破解!不难嘛!

    导 语 好久没更新了,上来冒个泡吧.各位小伙伴还记得前段时间号主挖过的一个坑嘛(虽然我曾经挖过无数个坑T_T): Selenium模拟登录系列 | B站滑块验证码破解 今天随手更新了一下Seleniu ...

  7. Python爬虫进阶五之多线程的用法

    前言 我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理. 首先声明一点! 多线程和多进程是不一样的!一个是 thread ...

  8. 大家沉迷短视频无法自拔?Python爬虫进阶,带你玩转短视频

    大家好,我是辣条. 现在短视频可谓是一骑绝尘,吃饭的时候.休息的时候.躺在床上都在刷短视频,今天给大家带来python爬虫进阶 :美拍视频地址加密解析. 短视频js逆向解析 抓取目标 工具使用 重点学 ...

  9. Python OpenCV 图片滑块验证码 滑块图片验证码 快速自动识别方案 代码简单 模板匹配识别 识别成功率达90%+

    前言 通过上一篇的文章大家已经对图片滑块验证码已经有了初步的了解,图片滑块验证码的核心关键在于图片识别接下来接入讲解.因为初版滑块图片识别虽然能识别验证码,通过一些策略调整也相对提高了一些图片识别率, ...

最新文章

  1. postman安装报错 无法定位_VS2010 + winxp 无法定位程序输入点GetTickCount64 在动态链接库kernel32.dll上 错误...
  2. oracle的redo和undo,OracleUndoRedo通俗理解
  3. sublime开启vim模式
  4. Myeclipse中Tomcat的两种部署方式
  5. 滑块 组件_组件制作:如何使用链接的输入创建滑块
  6. centos提示找不到netstat 和 ifconfig命令的问题
  7. python编码问题无法复现_python中烦人的编码问题
  8. 华为拟安装“俄版安卓”;百度回应内部贪腐;Android Studio 3.5 RC2 发布 | 极客头条...
  9. linux 文件句柄 sock,tcp socket文件句柄泄漏
  10. onvif 模拟摄像头_ONVIF协议测试工具(ONVIF Device Test Tool)
  11. 企业软件 - 创新尝试 - 用友 股份 产业链创新中心 - 产品流程会议问题解决 - 杨天政 - 原型产品发版标准 - 2014-3-13
  12. 微型计算机与接口技术总结,微机原理与接口技术课程总结
  13. 零代码上线小布对话技能:技能平台的实践与思考
  14. 合肥工业大学计算机与信息学院胡敏,合肥工业大学计算机与信息学院导师介绍:胡敏...
  15. Proxifier v3.21 standard edition 激活码
  16. Canvas笔画向量交互动画效果,随着鼠标描绘轨迹
  17. 【Homebrew】Homebrew 介绍与安装
  18. 第六节 交叉分析和非参数检验
  19. 维基解密:攻击 Mac/Linux 的CIA三款工具
  20. Mac开发利器之程序员编辑器MacVim

热门文章

  1. 北京国税 计算机代码,京国税[1995]227号 北京市国家税务局转发国家税务总局关于使用出口退税计算机管理软件有关问题的通知的通知...
  2. Ubuntu18.04安装QT5
  3. Flutter 加载 pdf
  4. C语言--编程将递增数列10、20、30、40、50、60、70、80、90、100保存到数组中,再从键盘输入一个整数,插入到该数列中,使之成为一个递增数列。
  5. 2021年中国石墨烯产业链发展分析:节能环保政策的扶持下,石墨烯产业规模将持续扩大[图]
  6. Visual SVN Server启动失败0x8007042a错误
  7. sas入门-笔记4 描述性统计分析
  8. C语言,实现通讯录功能
  9. python 读取 url 图片并保存到本地
  10. R绘图笔记 | 火山图的绘制