一、简述:
最近无聊想搞一下极验的滑块验证码破解这块,发现破解js代码耗时又耗力出现版本更新可能以前的所有努力都要推翻重做,不够通用性,最后还是选用selenium + PIL 来实现滑块验证码的破解。
期间也翻阅过很多文章,大多都已经失效,并且缺口位置查找和模拟滑动轨迹成功率很低,很难应用到实际开发项目中,本次是针对最新版本的极验滑块验证码进行破解。
二、项目环境
大致需要用到以下模块各位看观请提前准备好:
python3.6、selenium、numpy、PIL、chromedriver
三、分析步骤以及代码编写
首先分析目标网站(本次主要以geetest官网滑块demo为参考)
网站大致长这个样子,首先f12打开 开发者工具选择Elements查看节点,发现最新版本的滑块图片是使用画布来进行呈现的,期间查阅大量文档,使用如下代码获得画布中的图片数据,获取到的图片是base64进行编码的
document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png")
2.通过分析发现这两个画布放的是所需要的背景图和缺口图(其实一眼就看出来的)
3。接下来就是代码的编写了
3.1 首先是获得背景图和缺口图的数据
def get_images(self):
"""
获取验证码图片
:return: 图片的location信息
"""
time.sleep(1)
self.browser.web_driver_wait_ruishu(10, "class", 'geetest_canvas_slice')
fullgb = self.browser.execute_js('document.getElementsByClassName("geetest_canvas_bg geetest_'
'absolute")[0].toDataURL("image/png")')["value"]
bg = self.browser.execute_js('document.getElementsByClassName("geetest_canvas_fullbg geetest_fade'
' geetest_absolute")[0].toDataURL("image/png")')["value"]
return bg, fullgb
3.2 对数据进行解码操作并保存图片
def get_decode_image(self, filename, location_list):
"""
解码base64数据
"""
_, img = location_list.split(",")
img = base64.decodebytes(img.encode())
new_im: image.Image = image.open(BytesIO(img))
return new_im
3.3 接下来就是计算缺口位置了(这里使用的PIL中计算两张图片的差值获得缺口位置)
def compute_gap(self, img1, img2):
"""计算缺口偏移 这种方式成功率很高"""
# 将图片修改为RGB模式
img1 = img1.convert("RGB")
img2 = img2.convert("RGB")
# 计算差值
diff = ImageChops.difference(img1, img2)
# 灰度图
diff = diff.convert("L")
# 二值化
diff = diff.point(self.table, '1')
left = 43
# 这里做了优化为减少误差 纵坐标的像素点大于5时才认为是找到
# 防止缺口有凸起时有误差
for w in range(left, diff.size[0]):
lis = []
for h in range(diff.size[1]):
if diff.load()[w, h] == 1:
lis.append(w)
if len(lis) > 5:
return w
3.4 当滑块的缺口位置找到以后就需要生成滑动轨迹(其中加20是保证在滑动时先超过缺口位置然后在慢慢还原到正确位置)
def ease_out_quart(self, x):
return 1 - pow(1 - x, 4)
def get_tracks_2(self, distance, seconds, ease_func):
"""
根据轨迹离散分布生成的数学 生成 # 参考文档 https://www.jianshu.com/p/3f968958af5a
成功率很高 90% 往上
:param distance: 缺口位置
:param seconds: 时间
:param ease_func: 生成函数
:return: 轨迹数组
"""
distance += 20
tracks = [0]
offsets = [0]
for t in np.arange(0.0, seconds, 0.1):
ease = ease_func
offset = round(ease(t / seconds) * distance)
tracks.append(offset - offsets[-1])
offsets.append(offset)
tracks.extend([-3, -2, -3, -2, -2, -2, -2, -1, -0, -1, -1, -1])
return tracks
3.5 最后也就是滑动滑块到缺口位置
def move_to_gap(self, track):
"""移动滑块到缺口处"""
slider = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_slider_button')))
ActionChains(self.browse