爬虫学习笔记(十九)—— 滑动验证码
Posted 别呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爬虫学习笔记(十九)—— 滑动验证码相关的知识,希望对你有一定的参考价值。
文章目录
一、概念
滑动验证码也叫行为验证码,比较流行的一种验证码,通过用户的操作行为来完成验证,其中最出名的就是极验。滑动验证码的原理就是使用机器学习中的深度学习技术,根据一些特征来区分是否为正常用户。通过记录用户的滑动速度,还有每一小段时间的瞬时速度,用户鼠标点击情况,以及滑动后的匹配程度来识别。而且,不是说滑动到正确位置就是验证通过,而是根据特征识别来区分是否为真用户,滑到正确位置只是一个必要条件。
二、实现步骤
通过代码来模拟验证码滑动主要有4个步骤:
- 获取验证码图片,包含缺口图,滑块图,完整图
- 计算缺口位置,滑块位置,滑块要滑动的距离
- 通过算法模拟人工移动轨迹
- 通过selenium模拟操作
(练习网址:https://captcha1.scrape.center/)
2.1、获取验证码图片
首先通过“网页调试工具“观察 缺口图,滑块图,完整图 在源码中的位置
三个canvas标签分别对应了缺口图,滑块图和完整图。接下来,我们要通过修改页面样式来获取这三个图:
- 让滑块隐藏,截取缺口图;
- 隐藏缺口图,显示滑块图,截取滑块图;
- 显示完整图,截取完整图。
注意: 获取完图片记得还原,避免在接下来的操作中出错
2.1.1、获取缺口图
通过修改display属性,隐藏滑块,获取缺口图
代码:
js_hide_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="none"'
driver.execute_script(js_hide_slice)
#截取缺口图
gap_imgpath = './gap.png'
driver.find_element_by_class_name('geetest_canvas_bg').screenshot(gap_imgpath)
2.1.2、获取滑块图
通过修改display属性,隐藏缺口图,显示滑块图,截取滑块图
代码:
js_hide_gap = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="none"'
js_show_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="block"'
driver.execute_script(js_hide_gap+';'+js_show_slice)
#截取滑块图
slice_imgpath='./slice.png'
driver.find_element_by_class_name('geetest_canvas_slice').screenshot(slice_imgpath)
2.1.3、获取完整图
通过修改display属性,获取完整图
代码:
js_show_full = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="block"'
driver.execute_script(js_show_full)
full_imgpath = './full.png'
driver.find_element_by_class_name('geetest_canvas_fullbg').screenshot(full_imgpath)
2.1.4、完整代码
def get_captcha_img():
"获取验证码图片"
#1、隐藏滑块 获取缺口图
time.sleep(3)
js_hide_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="none"'
driver.execute_script(js_hide_slice)
#获取缺口图
gap_imgpath = './gap.png'
driver.find_element_by_class_name('geetest_canvas_bg').screenshot(gap_imgpath)
# 2、隐藏缺口图 获取滑块图
js_hide_gap = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="none"'
js_show_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="block"'
driver.execute_script(js_hide_gap+';'+js_show_slice)
#获取滑块图
slice_imgpath='./slice.png'
driver.find_element_by_class_name('geetest_canvas_slice').screenshot(slice_imgpath)
# 3、获取完整图
js_show_full = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="block"'
driver.execute_script(js_show_full)
full_imgpath = './full.png'
driver.find_element_by_class_name('geetest_canvas_fullbg').screenshot(full_imgpath)
# 4、还原验证码图形
js_hide_full = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="none"'
js_show_gap = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="block"'
driver.execute_script(js_hide_full+';'+js_show_gap)
return gap_imgpath,slice_imgpath,full_imgpath
2.2、计算缺口位置
通过计算缺口位置与滑块位置的差值,获得滑块要移动的距离
代码:
def get_gapX(gap_imgpath,full_imgpath):
"获取缺口的位置,缺口左边缘X坐标 "
gap_img = Image.open(gap_imgpath)
full_img = Image.open(full_imgpath)
w,h = gap_img.size
for x in range(w):
for y in range(h):
# 彩色图片,每一个点是一个元组
gap_rgb = gap_img.getpixel((x,y))
full_rgb = full_img.getpixel((x,y))
#比较缺口图与完整图,相同的坐标上的点,求颜色差值
#相同的颜色通道相减之后的绝对值相加;非缺口的地方差别小,缺口大的色差大
r = gap_rgb[0]-full_rgb[0]
g = gap_rgb[1]-full_rgb[1]
b = gap_rgb[2]-full_rgb[2]
value = abs(r)+abs(g)+abs(b)
print((x,y),value)
if value>120:
return x
def get_sliceX(slice_imgpath):
"获取滑块的位置,滑块左边缘X坐标"
slice_img = Image.open(slice_imgpath)
w,h = slice_img.size
#滑块为彩色,其余地方为白色
for x in range(w):
for y in range(h):
rgb = slice_img.getpixel((x,y))
value = rgb[0] + rgb[1] + rgb[2] #740
# print((x,y),value)
if value < 570:
return x
def get_distance(gap_imgpath, slice_imgpath, full_imgpath):
"计算距离"
gapX = get_gapX(gap_imgpath,full_imgpath)
sliceX = get_sliceX(slice_imgpath)
return abs(gapX - sliceX)
2.3、模拟人工移动
通过算法模拟人工移动的轨迹:
- 1、直接根据获取到的距离移动,滑块与缺口能够契合,但是不能通过
- 2、牛顿运动定律模拟人工移动,偶尔有通过,概率低;
- 3、增加随机性模拟人工移动轨迹,通过概率高
2.3.1、直接根据距离移动
def move_slice1(distance):
"1、直接根据距离移动"
elem = driver.find_element_by_class_name('geetest_slider_button')
ActionChains(driver).click_and_hold(elem).perform()
ActionChains(driver).move_by_offset(xoffset=distance,yoffset=0).perform()
ActionChains(driver).release(elem).perform()
2.3.2、牛顿运动定律模拟人工移动
拖动时的速度要平滑变化,开始变快,后来变慢,最后可能拖过头然后回拖,则速度可能为负,抛物线只是一种方案,轨迹方案体现拖动滑块时速度变化的理解。
这里要稍微用一点高中的物理知识:
代码实现:
def get_track(distance):
'''
获得移动轨迹,模仿人的滑动行为,先匀加速后匀减速匀变速运动基本公式:
①v=v0+at
②s=v0t+0.5at^2
:param distance: 需要移动的距离
:return: 每0.2s移动的距离
'''
#初速度
v0 = 0
#单位时间0.2s
t = 0.2
#轨迹列表,每个元素代表0.2s的位移
tracks = []
#当前的位移
current = 0
#达到mid开始减速
mid = distance*5/8
#先滑过一点,最后再反着滑动回来
distance+=10
while current<=distance:
# 增加运动随机性
t = random.randint(1,4)/10
if current<mid:
#加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细
a = random.randint(2,7) #加速运动
else:
a = -random.randint(2,6) #减速运动
#0.2s时间的位移
s = v0*t + 0.5*a*(t**2)
#当前位置
current+=s
#添加到轨迹列表
tracks.append(round(s))
#当前速度,作为下次的初速度
v0 = v0+a*t
#反着滑动到大概准确位置
for i in range(4):
tracks.append(-random.randint(1,3))
return tracks
2.4、selenium 移动滑块
def move_slice1(distance):
"1、直接根据距离移动"
elem = driver.find_element_by_class_name('geetest_slider_button')
ActionChains(driver).click_and_hold(elem).perform()
ActionChains(driver).move_by_offset(xoffset=distance,yoffset=0).perform()
ActionChains(driver).release(elem).perform()
def move_slice2(distance):
"2、牛顿运动定律模拟人工移动"
elem = driver.find_element_by_class_name('geetest_slider_button')
ActionChains(driver).click_and_hold(elem).perform()
tracks = get_track(distance)
for track in tracks:
ActionChains(driver).move_by_offset(xoffset=track,yoffset=0).perform()
ActionChains(driver).release(elem).perform()
移动卡顿问题
代码在运行的过程中可能会出现卡顿的问题,解决方法是修改pointer_input.py
文件中的DEFAULT_MOVE_DURATION
的值,把这个值改小一点,这个值的主要作用是控制花费多少毫秒来完成移动鼠标的动作。
python文件下 Lib\\site-packages\\selenium\\webdriver\\common\\actions\\pointer_input.py
以上是关于爬虫学习笔记(十九)—— 滑动验证码的主要内容,如果未能解决你的问题,请参考以下文章