当前位置:   article > 正文

滑块验证码------啥?你居然还在手动滑动,你不来试试自动滑动吗_canvas 标签 如何实现滑块验证

canvas 标签 如何实现滑块验证

测试网站

测试网站:https://www.geetest.com/demo/slide-float.html

我的giteer:秦老大大 (qin-laoda) - Gitee.com里面有我写的代码

作者备注:由于我个人原因,文章写得感觉太长,后面我会把一个知识分成多部文章,这样可以简单明了的看到了

验证码的思路有两种:一种是通过selenium来操作,这种操作简单但是运行过程速度慢,二是用js去做,这种做法比较麻烦,但是速度快

下面我来讲解一下selenium的做法 :

第一步,先在网页定位到我们要操作的地方,html的标签canvas

 思路1:要拿到两张图片,一张有缺口的图片,一张没有缺口的图片,后面我们可以通过比例来获取滑块需要滑动的距离(因为网页上的图片大小和实际的图片大小不一样,因为那是按比例缩小或者放大的)

思路2:获取缺口图片和滑块图片,来做对比得到滑块滑动的距离

如果细心的小可爱就会发现,滑块图片没有链接下载

下面我们来一一解决,首先我们要获取图片

 我们要个改一下页面的html代码如上图所示:

而实际发送回来的却是没有改的,我们需要用python代码更改一下:

代码:

  1. # 修改css的样式
  2. def get_css(self):
  3. time.sleep(2)
  4. # js语句执行 改变css
  5. self.driver.execute_script("document.querySelectorAll('canvas')[2].style=''")# canvas标签的个数下标从0开始,更改第三个的style

结果: 

获取图片代码如下:

  1. def crop_image(self,img_name):
  2. # 获取该图片的大小,可以通过div的框的大小进行
  3. img=self.driver.find_element_by_xpath('//div[@class="geetest_canvas_img geetest_absolute"]')
  4. # 把页面全屏化
  5. # self.driver.maximize_window()
  6. # 框的左上角的位置(x,y)
  7. img_left_up=img.location
  8. print(img_left_up)
  9. # 获取div框的大小(一个字典)
  10. print(img.size)
  11. # 获取图片右下角的坐标
  12. img_x,img_y=img_left_up["x"],img_left_up["y"]
  13. img_right_below_x,img_right_below_y=img_x + img.size["width"],img_y + img.size["height"]
  14. # 截屏(二进制)
  15. screen_shot=self.driver.get_screenshot_as_png()
  16. # 读取这个图片(读取内存中的二进制)
  17. by_io=BytesIO(screen_shot)
  18. # 打开图片
  19. op_img=Image.open(by_io)
  20. # 截取图片(截取下来的宽高),一个元组(a,b)
  21. cap_img=op_img.crop((int(img_x),int(img_y),int(img_right_below_x),int(img_right_below_y)))
  22. # 保存图片到文件中
  23. cap_img.save(img_name)
  24. return cap_img

下面我来一一讲解

我们要先清楚,因为我们没有链接下载图片,就只能截取图片,截图要清楚截图哪个地方 ,

 首先我们要截图div的框的大小

img=self.driver.find_element_by_xpath('//div[@class="geetest_canvas_img geetest_absolute"]')

我是定位了这个div框

img_left_up=img.location

这行代码比较重要,location返回一个坐标,返回哪个坐标,嘿嘿,我来告诉你们,

返回的是我们定位框的左上角的坐标(因为框和图片重合了,可以理解为图片的左上角坐标),

可是,我们要截图给光靠一个坐标还不行,我们还需得到右下角的坐标,怎么获取?我们可以获取框的大小,通过数学算法来得出

print(img.size)打印的是一个字典,返回了框的大小,

img_x,img_y=img_left_up["x"],img_left_up["y"]
img_right_below_x,img_right_below_y=img_x + img.size["width"],img_y + img.size["height"]

 这两行代码就得出了右下角的坐标了

那我们就来截取图片吧

screen_shot=self.driver.get_screenshot_as_png()

可以看出这个截图方法和我们之前的截图方法有点不一样

driver.save_screenshot("百度.png")

可以看出这个是有保存在文件里的,所以get_screenshot_as_png()是保存在内存里的(二进制)

那我们需要读取这个图片(二进制)就需要我们导入from io import BytesIO

BytesIO(二进制)

读取过后,我们还需要打开图片,

我们就需要下载模块了pip install pillow

导入模块:

from PIL import Image

打开图片

Image.open(by_io)

下面我们就要截取图片l:

cap_img=crop((x1,y1,x2,y2))

保存图片

cap_img.save("文件路径")

因为我们要保存两张图片一张原图,一张是有滑块的

原图:

滑块图:

图片的对比:

 

我们要比较无滑块的部分(利用RGB三色比较色差)

下面两张图可以让你们理解一下页面的坐标轴

 

代码如下:

  1. # 像素的对比
  2. def compare_pixel(self,img1,img2,i,j):
  3. pixel1=img1.load()[i,j]# 加载img1需要对比的像素并转换为RGB
  4. pixel2=img2.load()[i,j]# 加载img2需要对比的像素并转换为RGB
  5. # 对比误差范围
  6. threshold=60
  7. #RGB三颜色
  8. if(pixel1[0]-pixel2[0])<=threshold and (pixel1[1]-pixel2[1])<=threshold and(pixel1[2]-pixel2[2])<=threshold:
  9. return True
  10. return False
  11. def pixel(self,img1,img2):
  12. left=60#从像素60的位置开始
  13. has_find=False #没有发现那个凹槽
  14. #图片的大小(截取下来的宽高),一个元组(a,b)
  15. print(img1.size)
  16. # 一个个像素对比
  17. for i in range(left,img1.size[0]):#宽
  18. if has_find:
  19. break
  20. for j in range(img2.size[1]):
  21. if not self.compare_pixel(img1,img2,i,j):#img1对应的像素点(i,j)和img2对应的像素点(i,j)做对比(一个个的对比)
  22. distance=i
  23. print(distance)
  24. has_find=True
  25. # 只要碰到就立刻停止
  26. break
  27. return distance

我先来讲解一下第二个实例方法:

left=60从像素50开始对比

has_find=False是用于判断循环的在碰见凹槽时就停止循环,

return返回遇见有凹槽第一个像素的坐标的x,(这个只是大概的后面还需要微调)

第一个实例方法:

pixel1=img1.load()[i,j]# 加载img1需要对比的像素并转换为RGB
pixel2=img2.load()[i,j]# 加载img2需要对比的像素并转换为RGB

load()加载  [i,j]对应的像素位置

threshold=60

色差

RGB有三色,三色比较,每一种色差不超过600

由于我们得到了移动的大概距离,因为有检测,我们就要躲过检测,模拟人来滑动

模拟人工滑动轨迹

代码如下:

  1. #移动的轨迹
  2. def trajectory(self,distance):
  3. # d=vt+1/2at**2==>位移=初速度*时间+1/2加速度*时间的平方
  4. # v1=v+at
  5. # 思路:先加速,然后加速
  6. # 移动轨迹
  7. distance-=6
  8. track=[]
  9. # 当前移动的距离
  10. current=0
  11. # 移动到某个地方开始减速
  12. mid=distance*3/4
  13. # 间隔时间(加速时间)
  14. t=0.1
  15. # 初速度
  16. v=0
  17. pass
  18. while current<distance:
  19. if(current<mid):
  20. a=random.randint(2,3)
  21. else:
  22. a=random.randint(4,5)
  23. v0=v
  24. v=v0+a*t
  25. move=v0*t+1/2*a*t**2
  26. current+=move
  27. track.append(round(move))
  28. return track

我们可以借用物理的位移公式

d=vt+1/2at**2==>位移=初速度*时间+1/2加速度*时间的平方

我们需要自定义初速度,加速时间,加速度

思路:如果位移小于要移动的距离,继续移动

我们还要设计一个加速度在哪个地方加大

最后我们通过位移公式来计算每加速t秒移动的距离,设计成运动轨迹

selenium操作移动

代码如下:

  1. def selenium(self,track):
  2. # js过检
  3. js = "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
  4. self.driver.execute_script(js)
  5. #运用行为链
  6. ac=ActionChains(self.driver)
  7. #定位
  8. div=self.driver.find_element_by_class_name("geetest_slider_button")
  9. # 按住不松
  10. ac.click_and_hold(div)
  11. # 移动
  12. for i in track:
  13. ac.move_by_offset(xoffset=i,yoffset=0)
  14. time.sleep(0.1)
  15. # 松开
  16. ac.release()
  17. # 提交行为
  18. ac.perform()

前面看过我写过的selenium的文章就会知道思路来,

我还是一一来解释一下:

利用行为链来操作

导入

from selenium.webdriver.common.action_chains import ActionChainsc

创建行为链对象

ac=ActionChains(self.driver)

进行操作:

定位
div=self.driver.find_element_by_class_name("geetest_slider_button")
# 按住不松
ac.click_and_hold(div)
# 移动
for i in track:
    ac.move_by_offset(xoffset=i,yoffset=0)
time.sleep(0.1)
# 松开
ac.release()

注意ac.move_by_offset(xoffset=i,yoffset=0)是每次移动多少距离,

全部代码如下:

  1. from selenium import webdriver
  2. import time
  3. from PIL import Image
  4. from io import BytesIO
  5. import random
  6. from selenium.webdriver.common.action_chains import ActionChains
  7. class Radar(object):
  8. def __init__(self):
  9. self.url="https://www.geetest.com/demo/slide-float.html"
  10. # 创建一个浏览器
  11. self.driver=webdriver.Chrome()
  12. # 打开网页
  13. self.driver.get(self.url)
  14. # 隐式等待
  15. self.driver.implicitly_wait(5)
  16. # 定位
  17. def gps(self):
  18. return self.driver.find_element_by_xpath('//span[@class="geetest_radar_tip_content"]')
  19. # 点击
  20. def click1(self):
  21. self.gps().click()
  22. # 修改css的样式
  23. def get_css(self):
  24. # 加个休息时间,让页面加载出来
  25. time.sleep(2)
  26. # js语句执行 改变css(把有缺口的图片隐藏起来)
  27. self.driver.execute_script("document.querySelectorAll('canvas')[2].style=''")# canvas标签的个数下标从0开始,更改第三个的style
  28. # 恢复有缺口的图片
  29. def restore_img(self):
  30. # 加个休息时间,让页面加载出来
  31. time.sleep(2)
  32. # 显示出有缺口的图片
  33. self.driver.execute_script("document.querySelectorAll('canvas')[2].style='display:none'")
  34. # 截取验证码
  35. def crop_image(self,img_name):
  36. # 获取该图片的大小,可以通过div的框的大小进行
  37. img=self.driver.find_element_by_xpath('//div[@class="geetest_canvas_img geetest_absolute"]')
  38. # 把页面全屏化
  39. # self.driver.maximize_window()
  40. # 框的左上角的位置(x,y)
  41. img_left_up=img.location
  42. print(img_left_up)
  43. # 获取div框的大小(一个字典)
  44. print(img.size)
  45. # 获取图片右下角的坐标
  46. img_x,img_y=img_left_up["x"],img_left_up["y"]
  47. img_right_below_x,img_right_below_y=img_x + img.size["width"],img_y + img.size["height"]
  48. # 截屏(二进制)
  49. screen_shot=self.driver.get_screenshot_as_png()
  50. # 读取这个图片(读取内存中的二进制)
  51. by_io=BytesIO(screen_shot)
  52. # 打开图片
  53. op_img=Image.open(by_io)
  54. # 截取图片(截取下来的宽高),一个元组(a,b)
  55. cap_img=op_img.crop((int(img_x),int(img_y),int(img_right_below_x),int(img_right_below_y)))
  56. # 保存图片到文件中
  57. cap_img.save(img_name)
  58. return cap_img
  59. # 像素的对比
  60. def compare_pixel(self,img1,img2,i,j):
  61. pixel1=img1.load()[i,j]# 加载img1需要对比的像素并转换为RGB
  62. pixel2=img2.load()[i,j]# 加载img2需要对比的像素并转换为RGB
  63. # 对比误差范围
  64. threshold=60
  65. #RGB三颜色
  66. if(pixel1[0]-pixel2[0])<=threshold and (pixel1[1]-pixel2[1])<=threshold and(pixel1[2]-pixel2[2])<=threshold:
  67. return True
  68. return False
  69. def pixel(self,img1,img2):
  70. left=60#从像素60的位置开始
  71. has_find=False #没有发现那个凹槽
  72. #图片的大小(截取下来的宽高),一个元组(a,b)
  73. print(img1.size)
  74. # 一个个像素对比
  75. for i in range(left,img1.size[0]):#宽
  76. if has_find:
  77. break
  78. for j in range(img2.size[1]):
  79. if not self.compare_pixel(img1,img2,i,j):#img1对应的像素点(i,j)和img2对应的像素点(i,j)做对比(一个个的对比)
  80. distance=i
  81. print(distance)
  82. has_find=True
  83. # 只要碰到就立刻停止
  84. break
  85. return distance
  86. #移动的轨迹
  87. def trajectory(self,distance):
  88. # d=vt+1/2at**2==>位移=初速度*时间+1/2加速度*时间的平方
  89. # v1=v+at
  90. # 思路:先加速,然后加速
  91. # 移动轨迹
  92. distance-=6
  93. track=[]
  94. # 当前移动的距离
  95. current=0
  96. # 移动到某个地方开始减速
  97. mid=distance*3/4
  98. # 间隔时间(加速时间)
  99. t=0.1
  100. # 初速度
  101. v=0
  102. pass
  103. while current<distance:
  104. if(current<mid):
  105. a=random.randint(2,3)
  106. else:
  107. a=random.randint(4,5)
  108. v0=v
  109. v=v0+a*t
  110. move=v0*t+1/2*a*t**2
  111. current+=move
  112. track.append(round(move))
  113. return track
  114. def selenium(self,track):
  115. # js过检
  116. js = "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
  117. self.driver.execute_script(js)
  118. #运用行为链
  119. ac=ActionChains(self.driver)
  120. #定位
  121. div=self.driver.find_element_by_class_name("geetest_slider_button")
  122. # 按住不松
  123. ac.click_and_hold(div)
  124. # 移动
  125. for i in track:
  126. ac.move_by_offset(xoffset=i,yoffset=0)
  127. time.sleep(0.1)
  128. # 松开
  129. ac.release()
  130. # 提交行为
  131. ac.perform()
  132. def main(radar):
  133. radar.gps()
  134. radar.click1()
  135. #下载无缺口图片
  136. radar.get_css()
  137. img1=radar.crop_image("./截图.png")
  138. # 下载有缺口的图片
  139. radar.restore_img()
  140. img2=radar.crop_image("./截图1.png")
  141. # 获取距离
  142. distance=radar.pixel(img1,img2)
  143. # 人工模拟滑动轨迹
  144. track=radar.trajectory(distance)
  145. print(track)
  146. #selenium滑动
  147. radar.selenium(track)
  148. if __name__ == '__main__':
  149. radar=Radar()
  150. main(radar)

总结:滑块验证码最重要的思路就是能算出移动距离,selenium移动只是一个辅助,着重看计算距离的代码,因为现在很多能检测到selenium特征的反爬,后面我会写另一种,滑块验证,有兴趣的小可爱可以过来参观

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/738943
推荐阅读
相关标签
  

闽ICP备14008679号