当前位置:   article > 正文

使用Selenium破解滑动验证码的原理及解决思路_selenium滑动验证码

使用Selenium破解滑动验证码的原理及解决思路_selenium滑动验证码

:param driver: webdriver
:return: 返回验证码背景图片Image对象
“”"

webdriver.ActionChains(driver).move_to_element(slider).perform()
time.sleep(0.2)
img = driver.find_element_by_xpath(‘//*[@id=“captcha”]/div/div[1]/div[2]/div[1]/a[2]’)
if ‘show’ in img.get_attribute(‘class’):
res = img.screenshot_as_png
return Image.open(BytesIO(res))
else:
raise ValueError(‘获取验证码背景图片失败’)

有缺口图片:

def get_cut_image(driver):
“”"
点击滑动按钮获取有缺口图片
:param driver: webdriver
:return: 返回验证码有缺口图片的Image对象
“”"

slider = driver.find_element_by_xpath(‘//[@id=“captcha”]/div/div[3]/div[2]‘)
webdriver.ActionChains(driver).click_and_hold(slider).perform()
time.sleep(0.1)
img = driver.find_element_by_xpath(’//
[@id=“captcha”]/div/div[1]/div[2]/div[1]/a[1]’)
res = img.screenshot_as_png

cut_img = Image.open(BytesIO(res))
return Image.open(BytesIO(res))

计算移动距离

找出缺口位置,计算移动距离。算法有很多,大家可以自由发挥。这里我们讲一种最简单的方法。我们要算出的距离是滑块要滑动的距离。

通过比较没有缺口的图片,和这张有缺口的图片,找出滑块的位置和缺口的位置即可。

经过观察,发现滑块出现的位置固定在x轴的0-100像素范围内,所以循环比较两张图片的x轴0-100像素范围内的每一行像素点,直到找到第一行出现两个图片像素点颜色完全不同的点,即找到了滑块的最左边最上的第一个像素点。

但是在实际操作中发现,虽然肉眼看起来两张图片公共部分一模一样,但是程序处理后的像素的具体rgb值也是不相同的,所以需要设置一个阈值来判断,具体需要进行测试。

按照相同的思路,比较两张图片x轴100-end像素的部分,找到缺口的最左最上那个点。

用找到的缺口像素点的x坐标减去找到的滑块的点的x坐标得到近似移动距离。这种算法,经过测试准确率还不错,大家如果在实际工作过程中发现有问题,需要根据具体情况去设计不同算法。

代码如下:

def get_distance(full_image, cut_image):
full_pixies = full_image.load()
cut_pixies = cut_image.load()

w, h = full_image.size

full_image.save(‘full.png’)
cut_image.save(‘cut.png’)

先找最左边不同的点

left = []

for j in range(h):

for i in range(100):

if abs(full_pixies[i, j][0] - cut_pixies[i, j][0]) + abs(full_pixies[i, j][1] - cut_pixies[i, j][1]) + abs(
full_pixies[i, j][2] - cut_pixies[i, j][2]) > 150:
left.append((i, j))

if left:
break

再找最右边不同的点

right = []

for j in range(h):

for i in range(100, w):

if abs(full_pixies[i, j][0] - cut_pixies[i, j][0]) + abs(full_pixies[i, j][1] - cut_pixies[i, j][1]) + abs(
full_pixies[i, j][2] - cut_pixies[i, j][2]) > 150:
right.append((i, j))

if right:
break

length = right[0][0] - left[0][0]

return length

计算滑动轨迹

滑动验证码早期刚面世的时候没有做行为校验,很快被破解。随着人工智能的发展,目前所有商用滑动验证码后台都有做行为校验,根据前端传递的移动轨迹,后台会进行特征校验,如果判定非人工则返回校验失败。模拟人的滑动行为,最常见的以中方法是通过加速度公式。

目前这个方法已经被识别,但相对较简单,我们首先学习其思路。大家根据自己的能力可以自行扩展。

基本思路是,分析手动的移动轨迹后发现,是先加速后减速,所以通过加速度公式进行如下的设计:

def get_track(self, distance):
‘’’
拿到移动轨迹,模仿人的滑动行为,先匀加速后匀减速
匀变速运动基本公式:
①v=v0+at
②s=v0t+(1/2)at²
③v²-v0²=2as

:param distance: 需要移动的距离
:return: 存放每0.2秒移动的距离
‘’’

初速度

v=0

单位时间为0.2s来统计轨迹,轨迹即0.2内的位移

t=0.3

位移/轨迹列表,列表内的一个元素代表0.2s的位移

tracks=[]

当前的位移

current=0

到达mid值开始减速

mid=distance * 5/8

distance += 10 # 先滑过一点,最后再反着滑动回来

a = random.randint(1,3)

while current < distance:
if current < mid:

加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细

a = random.randint(1,3) # 加速运动
else:
a = -random.randint(2,4) # 减速运动

初速度

v0 = v

0.2秒时间内的位移

s = v0t+0.5a*(t**2)

当前的位置

current += s

添加到轨迹列表

tracks.append(round(s))

速度已经达到v,该速度作为下次的初速度

v= v0+a*t

反着滑动到大概准确位置

for i in range(4):
tracks.append(-random.randint(1,3))

for i in range(4):

tracks.append(-random.randint(1,3))

random.shuffle(tracks)
return tracks

滑动滑块

利用selenium,根据算出的轨迹,进行模拟滑动,代码如下:

def slide(self, tracks):

slider = self.driver.find_element_by_xpath(‘//*[@id=“captcha”]/div/div[3]/div[2]’)

鼠标点击并按住不松

webdriver.ActionChains(self.driver).click_and_hold(self.slider).perform()

让鼠标随机往下移动一段距离

webdriver.ActionChains(self.driver).move_by_offset(xoffset=0, yoffset=100).perform()
time.sleep(0.15)
for item in tracks:
webdriver.ActionChains(self.driver).move_by_offset(xoffset=item, yoffset=random.randint(-2,2)).perform()

稳定一秒再松开

time.sleep(1)
webdriver.ActionChains(self.driver).release(self.slider).perform()
time.sleep(1)

随机拿开鼠标

webdriver.ActionChains(self.driver).move_by_offset(xoffset=random.randint(200, 300), yoffset=random.randint(200, 300)).perform()
time.sleep(0.2)
info = self.driver.find_element_by_xpath(‘//*[@id=“login-modal”]/div/div/div/div[2]/div[1]/div[2]/div[1]/div/div[1]/div[2]/div[2]/div/div[2]/span[1]’)
if ‘验证通过’ in info.text:
return 1

if ‘验证失败’ in info.text:
return 2

if ‘再来一次’ in info.text:
return 3

if ‘出现错误’ in info.text:
return 4

完整代码

#!/usr/bin/env python

encoding: utf-8

#@author: jack
#@contact: 935650354@qq.com
#@site: https://www.cnblogs.com/jackzz

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

def get_merge_img(img_content,location_list,num):
‘’’
拼接图片
:param img_content:
:param location_list:
:param num:
:return:
‘’’
im = Image.open(img_content)
im_list_upper = []
im_list_done = []
for location in location_list:

print(location)

if int(location[‘y’]) == -58:
im_list_upper.append(im.crop((abs(int(location[‘x’])),58,abs(int(location[‘x’]))+10,116)))
if int(location[‘y’]) == 0:
im_list_done.append(im.crop((abs(int(location[‘x’])),0,abs(int(location[‘x’]))+10,58)))

#create new image
new_im = Image.new(‘RGB’,(260,116))
x_offset=0
for im in im_list_upper:
new_im.paste(im,(x_offset,0))
x_offset +=10

x_offset = 0
for im in im_list_done:
new_im.paste(im, (x_offset, 58))
x_offset += 10

return new_im

def get_img(driver,div_class,num):
‘’’
获取图片
:param driver:
:param div_class:
:param num:
:return:
‘’’
background_imgs = driver.find_elements_by_class_name(div_class)
location_list = []
imge_url = ‘’
for img in background_imgs:

location = {}
imge_url = re.findall(r’background-image: url(“(.?)"); background-position: (.?)px (.?)px;',img.get_attribute(‘style’))[0][0]
location[‘x’] = re.findall(r’background-image: url("(.
?)”); background-position: (.?)px (.?)px;‘,img.get_attribute(‘style’))[0][1]
location[‘y’] = re.findall(r’background-image: url("(.?)"); background-position: (.?)px (.*?)px;’,img.get_attribute(‘style’))[0][2]

location_list.append(location)

response = requests.get(imge_url).content
img_content = BytesIO(response)

image = get_merge_img(img_content,location_list,num)
image.save(‘{}.jpg’.format(num))
return image

def get_diff_location(image1,image2):
‘’’
通过像素对比 找到缺口位置
:param image1:
:param image2:
:return:
‘’’
for x in range(1,259):
for y in range(1, 115):
if is_similar(image1,image2,x,y) == False:
#判断成立 表示xy这个点 两张图不一样
return x

def is_similar(image1,image2,x,y):
pixel1 = image1.getpixel((x,y))
pixel2 = image2.getpixel((x,y))

for i in range(0,3):
if abs(pixel1[i]) - pixel2[i] >=50:
return False
return True

def get_track(x):
‘’’
滑块移动轨迹
初速度 v =0
单位时间 t = 0.2
位移轨迹 tracks = []
当前位移 ccurrent = 0
:param x:
:return:
‘’’
v = 0
t = 0.2
tracks = []
current = 0

mid = x*5/8#到达mid值开始减速

x = x+10

while current < x:

if current < mid:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数软件测试工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上软件测试开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注软件测试)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

1712938552911)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上软件测试开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注软件测试)
[外链图片转存中…(img-OAVgHRzC-1712938552911)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号