最近在爬虫开发的过程中,遇到了关于滑块验证码,需要进行滑块验证码破解。这里涉及到图像方面的技术,可以借助OpenCV进行解决。
这里通过CV2库进行滑块验证的解决。简单介绍一下滑块验证的几个步骤。
根据网页,获取到滑块图片,一般来说分为两个图片,一个是缺口图,也就是缺少缺口的图片。一个是滑块图,也就是缺口图缺少的图片。假设缺口图为img1,滑块图为img2。
img1:
img2:
一般而言,我们通过网页获取到的图片和实际在网页上显示的图片大小是不一致的。读者可以根据自己需要破解的滑块验证码进行校验。查看在网页中的图片大小和下载的图片大小是否一致。如果不一致,那么需要调整下载的图片的大小为网页中图片的大小,因为移动的距离是以网页为准。
读取图片,然后调整图片的大小为网页中显示的大小。
img1 = cv2.imread(缺口图的路径)
img1 = cv2.resize(img1, (网页缺口图实际宽度, 网页缺口图实际高度))
img2 = cv2.imread(缺块图的路径)
img2 = cv2.resize(img2, (网页缺块图实际宽度, 网页缺块图实际高度))
如果要识别缺口位置,可以借助cv2库的matchTemplate方法来获取缺块距离缺口的位置。实现代码如下所示,一般来说这种方法识别会带有误差,也就是存在失败的情况,这个时候可以在程序中进行设置,进行多次尝试解决滑块验证(可以采用循环处理的办法,重复多次滑块验证解决流程),以达到解决滑块问题的效果。
img1_gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 对图片灰度化处理
img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# 对图片灰度化处理
res = cv2.matchTemplate(img1_gray, img2_gray, cv2.TM_CCOEFF_NORMED)
value = cv2.minMaxLoc(res) #获取缺口和缺块的距离
value = value[3][0] #value值就是缺块距离缺口的距离
我们可以借助selenium进行模拟滑块拖动的过程,但是在模拟滑块的拖动过程中,需要模拟人的速度。人在拖动滑块的时候,正常是先快后慢的过程,这个时候我们可以设置一个先快后慢的过程(可以设置两个运动方程,一段加速度为a1,一段加速度为a2),用于模拟人拖动滑块。示例代码如下所示:
# ditance为移动的距离,也就是上面获得的value
def getTrack(self,distance):
#用于存储每个过程中,拉动滑块的距离
track = []
# 当前位移
current = 0
# 当距离超过什么时候,进行减速
mid = distance * 4 / 5
# 计算间隔
t = 0.2
# 初速度
v = 0
while current < distance:
if current < mid:
# 加速度为正2
a = 2
else:
# 加速度为负3
a = -3
# 初速度v0
v0 = v
# 当前速度v = v0 + at
v = v0 + a * t
# 移动距离x = v0t + 1/2 * a * t^2
move = v0 * t + 1 / 2 * a * t * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
return track
通过selenium模拟运动,可以借助ActionChains来拖拽滑块。首先我们需要获取到滑块元素。
这个我们可以通过F12来查看拖拽滑块元素的属性,然后通过driver获取到元素。
ActionChains(driver).click_and_hold(拖拽滑块的元素).perform() # 点击并且不释放鼠标
for x in track: # 根据轨迹,移动元素
ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform() #移动元素
time.sleep(0.5)
ActionChains(driver).release().perform() #释放元素
class SlidingBlock(object):
def __init__(self):
self.block = "" # 这里是获取缺块的特征属性
self.blockBd = "" # 这里是获取缺块背景图的特征属性
# 用于返回缺块的图片链接(img2)
def getBlock(self,driver):
# TO-DO
return src
# 用于返回缺块背景图的图片链接(img1)
def getBlockBd(self,driver):
# TO-DO
return src
# 用于获取轨迹
def getTrack(self,distance):
"""
根据偏移量获取移动轨迹
:param distance:偏移量
:return:移动轨迹
"""
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * 4 / 5
# 计算间隔
t = 0.2
# 初速度
v = 0
while current < distance:
if current < mid:
# 加速度为正2
a = 2
else:
# 加速度为负3
a = -3
# 初速度v0
v0 = v
# 当前速度v = v0 + at
v = v0 + a * t
# 移动距离x = v0t + 1/2 * a * t^2
move = v0 * t + 1 / 2 * a * t * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
return track
# 进行破解滑块
def solveSliding(self,driver):
download = dataDownload() # 辅助类,用于提供图片下载方法,读者可以自己实现,就是一个图片下载方法。
# 进行滑块破解,直到破解成功结束
while(True):
bdUrl = self.getBlockBd(driver)
# 因为滑块破解之后,浏览器就不存在滑块标签属性了,所以为None就可以判定破解呈贡
if bdUrl == None:
return
# 根据滑块背景图链接,下载背景图
download.download_image_by_urlrecive(bdUrl, "bg1.png")
url = self.getBlock(driver)
if url == None:
return
# 根据滑块背景图链接,下载缺口图
download.download_image_by_urlrecive(url, "bg2.png")
# 获取缺口
img1 = cv2.imread('bg1.png')
# 这里由于网页上显示的(宽,高)为(420,210)所以要对图片处理
# 读者根据自己实际情况,进行调整
img1 = cv2.resize(img1, (420, 210))
img2 = cv2.imread('bg2.png')
# 同上,进行调整
img2 = cv2.resize(img2, (63, 210))
# 灰度化处理图片
img1_gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# 模板匹配
res = cv2.matchTemplate(img1_gray, img2_gray, cv2.TM_CCOEFF_NORMED)
value = cv2.minMaxLoc(res)
value = value[3][0]
# 根据距离获取位移的轨迹路线
track = self.getTrack(value)
time.sleep(1)
#模拟拖拽滑块,滑块元素的获取,读者根据实际情况进行改动
ActionChains(driver).click_and_hold(driver.find_element_by_class_name("滑块特征属性")).perform()
for x in track:
ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5)
ActionChains(driver).release().perform()
time.sleep(3)