终于搞定selenium图片滑块验证码了!

因为种种原因没能实现愿景的目标,在这里记录一下中间结果,也算是一个收场吧。这篇文章主要是用selenium解决滑块验证码的个别案列。

思路:

  • selenium打开浏览器指定网站

  • 将残缺块图片和背景图片下载到本地

  • 对比两张图片的相似地方,计算要滑动的距离

  • 规划路线,移动滑块

01 实现步骤

01 用selenium打开浏览器浏览指定网站

1、找到chromedriver.exe的路径

点击开始找到谷歌图标==》右键更多==》打开文件位置==》右键谷歌快捷方式==》属性 ==》打开文件所在的位置 ==》复制路径

2、代码

 
  1. from selenium import webdriver

  2. # chrome_path要改成你自己的路径

  3. chrome_path = r"C:\Users\11248\AppData\Local\Google\Chrome\Application\chromedriver.exe"

  4. url = 'https://icas.jnu.edu.cn/cas/login'

  5. driver = webdriver.Chrome(chrome_path)

  6. driver.get(url)

02 将残缺块图片和背景图片下载到本地

1、找到图片位置

打开网页进入开发者工具,找到图片位置

2、代码

 
  1. import time

  2. import requests

  3. from PIL import Image

  4. from selenium.webdriver.common.by import By

  5. from io import BytesIO

  6. time.sleep(5)# 进入页面要停留几秒钟,等页面加载完

  7. target_link = driver.find_element(By.CLASS_NAME, "yidun_bg-img").get_attribute('src')

  8. template_link = driver.find_element(By.CLASS_NAME, "yidun_jigsaw").get_attribute('src')

  9. target_img = Image.open(BytesIO(requests.get(target_link).content))

  10. template_img = Image.open(BytesIO(requests.get(template_link).content))

  11. target_img.save('target.jpg')

  12. template_img.save('template.png')

03 对比两张图片的相似地方,计算要滑动的距离

1、用matchTemplate获取移动距离

因为背景图片中的残缺块位置和原始残缺图的亮度有所差异,直接对比两张图片相似的地方,往往得不到令人满意的结果,在此要对两张图片进行一定的处理,为了避免这种亮度的干扰,笔者这里将两张图片先进行灰度处理,再对图像进行高斯处理,最后进行边缘检测。

 
  1. def handel_img(img):

  2.     imgGray = cv2.cvtColor(img, cv2.COLOR_RGBA2GRAY)  # 转灰度图

  3.     imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)  # 高斯模糊

  4.     imgCanny = cv2.Canny(imgBlur, 60, 60)  # Canny算子边缘检测

  5.     return imgCanny

  6. 将JPG图像转变为4通道(RGBA)

  7. def add_alpha_channel(img):

  8.     """ 为jpg图像添加alpha通道 """

  9.     r_channel, g_channel, b_channel = cv2.split(img)  # 剥离jpg图像通道

  10.     alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255  # 创建Alpha通道

  11.     img_new = cv2.merge((r_channel, g_channel, b_channel, alpha_channel))  # 融合通道

  12.     return img_new

2、代码

 
  1. import cv2

  2. # 读取图像

  3. def match(img_jpg_path, img_png_path):

  4.     # 读取图像

  5.     img_jpg = cv2.imread(img_jpg_path, cv2.IMREAD_UNCHANGED)

  6.     img_png = cv2.imread(img_png_path, cv2.IMREAD_UNCHANGED)

  7.     # 判断jpg图像是否已经为4通道

  8.     if img_jpg.shape[2] == 3:

  9.         img_jpg = add_alpha_channel(img_jpg)

  10.     img = handel_img(img_jpg)

  11.     small_img = handel_img(img_png)

  12.     res_TM_CCOEFF_NORMED = cv2.matchTemplate(img, small_img, 3)

  13.     value = cv2.minMaxLoc(res_TM_CCOEFF_NORMED)

  14.     value = value[3][0]  # 获取到移动距离

  15.     return value

3、检验效果

为了验证思路和方法是否得当,这里将滑块图片与背景图片进行拼接,为后面埋下一个小坑。

 
  1. def merge_img(jpg_img, png_img, y1, y2, x1, x2):

  2.     """ 将png透明图像与jpg图像叠加

  3.         y1,y2,x1,x2为叠加位置坐标值

  4.     """

  5.     # 判断jpg图像是否已经为4通道

  6.     if jpg_img.shape[2] == 3:

  7.         jpg_img = add_alpha_channel(jpg_img)

  8.     # 获取要覆盖图像的alpha值,将像素值除以255,使值保持在0-1之间

  9.     alpha_png = png_img[yy1:yy2, xx1:xx2, 3] / 255.0

  10.     alpha_jpg = 1 - alpha_png

  11.     # 开始叠加

  12.     for c in range(0, 3):

  13.         jpg_img[y1:y2, x1:x2, c] = ((alpha_jpg * jpg_img[y1:y2, x1:x2, c]) + (alpha_png * png_img[yy1:yy2, xx1:xx2, c]))

  14.     return jpg_img

  15.     

  16. img_jpg_path = 'target.jpg'  # 读者可自行修改文件路径

  17. img_png_path = 'template.png'  # 读者可自行修改文件路径

  18. x1 = match(img_jpg_path, img_png_path)

  19. y1 = 0

  20. x2 = x1 + img_png.shape[1]

  21. y2 = y1 + img_png.shape[0]

  22. # 开始叠加

  23. res_img = merge_img(img_jpg, img_png, y1, y2, x1, x2)

  24. cv2.imshow("res_img ", res_img)

  25. cv2.waitKey(0)

04 规划路线,移动滑块

1、点击滑块移动

用第3节已经获取到的距离,点击滑块进行移动

 
  1. from selenium.webdriver.support import expected_conditions as EC

  2. from selenium.webdriver.support.wait import WebDriverWait

  3. from selenium.webdriver import ActionChains

  4. def crack_slider(distance):

  5. wait = WebDriverWait(driver, 20)

  6.     slider = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'yidun_slider')))

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

  8.     ActionChains(self.driver).move_by_offset(xoffset=distance, yoffset=0).perform()

  9.     time.sleep(2)

  10.     ActionChains(self.driver).release().perform()

  11.     return 0

神奇的事情是,坑来了,没有匹配成功。

2、匹配失败原因

这里有以下两点原因:

  • 图片尺寸发生了变化,距离要进行转换。

  • 滑块滑动时,滑块和残缺块的相对位置有变动。

首先解决图片尺寸变化问题,找到网页中图片大小:345x172.500

图片

下载到本地图片大小:480x240

所以要对距离进行以下处理:

distance = distance / 480 * 345

关于第二个问题,这里没有找到很好的测量工具测量出来,好在验证码对位置精确度要求不高,就一个个试数吧。

distance = distance /480 * 345 + 12

05 补充

在对极验验证码进行学习中,有的网站对移动轨迹进行了验证,如果滑动太快,也会被识别出机器操作,为了模拟人工操作,出色的程序员写出了一个魔幻移动轨迹

举个例子:我们可以先超过目标,再往回移动。

 
  1. def get_tracks(distance):

  2.      distance += 20

  3.      v = 0

  4.      t = 0.2

  5.      forward_tracks = []

  6.      current = 0

  7.      mid = distance * 3 / 5

  8.      while current < distance:

  9.          if current < mid:

  10.              a = 2

  11.          else:

  12.              a = -3

  13.          s = v * t + 0.5 * a * (t ** 2)

  14.          v = v + a * t

  15.          current += s

  16.          forward_tracks.append(round(s))

  17.      back_tracks = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1]

  18.      return {'forward_tracks': forward_tracks, 'back_tracks': back_tracks}

  19.   def crack_slider(tracks):

  20.     wait = WebDriverWait(driver, 20)

  21.       slider = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'yidun_slider')))

  22.       ActionChains(driver).click_and_hold(slider).perform() # 模拟按住鼠标左键

  23.       for track in tracks['forward_tracks']:

  24.           ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform()

  25.       time.sleep(0.5)

  26.       for back_tracks in tracks['back_tracks']:

  27.           ActionChains(driver).move_by_offset(xoffset=back_tracks, yoffset=0).perform()

  28.       ActionChains(driver).move_by_offset(xoffset=-4, yoffset=0).perform()

  29.       ActionChains(driver).move_by_offset(xoffset=4, yoffset=0).perform()

  30.       time.sleep(0.5)

  31.       ActionChains(driver).release().perform()# 释放左键

  32.       return 0

06 完整代码 

 
  1. # coding=utf-8

  2. import re

  3. import requests

  4. import time

  5. from io import BytesIO

  6. import cv2

  7. import numpy as np

  8. from PIL import Image

  9. from selenium import webdriver

  10. from selenium.webdriver import ActionChains

  11. from selenium.webdriver.common.by import By

  12. from selenium.webdriver.support import expected_conditions as EC

  13. from selenium.webdriver.support.wait import WebDriverWait

  14. class CrackSlider():

  15. # 通过浏览器截图,识别验证码中缺口位置,获取需要滑动距离,并破解滑动验证码

  16. def __init__(self):

  17. super(CrackSlider, self).__init__()

  18. self.opts = webdriver.ChromeOptions()

  19. self.opts.add_experimental_option('excludeSwitches', ['enable-logging'])

  20. # self.driver = webdriver.Chrome(ChromeDriverManager().install(), options=self.opts)

  21. chrome_path = r"C:\Users\11248\AppData\Local\Google\Chrome\Application\chromedriver.exe"

  22. self.driver = webdriver.Chrome(chrome_path, options=self.opts)

  23. self.url = 'https://icas.jnu.edu.cn/cas/login'

  24. self.wait = WebDriverWait(self.driver, 10)

  25. def get_pic(self):

  26. self.driver.get(self.url)

  27. time.sleep(5)

  28. target_link = self.driver.find_element(By.CLASS_NAME, "yidun_bg-img").get_attribute('src')

  29. template_link = self.driver.find_element(By.CLASS_NAME, "yidun_jigsaw").get_attribute('src')

  30. target_img = Image.open(BytesIO(requests.get(target_link).content))

  31. template_img = Image.open(BytesIO(requests.get(template_link).content))

  32. target_img.save('target.jpg')

  33. template_img.save('template.png')

  34. def crack_slider(self, distance):

  35. slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'yidun_slider')))

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

  37. ActionChains(self.driver).move_by_offset(xoffset=distance, yoffset=0).perform()

  38. time.sleep(2)

  39. ActionChains(self.driver).release().perform()

  40. return 0

  41. def add_alpha_channel(img):

  42. """ 为jpg图像添加alpha通道 """

  43. r_channel, g_channel, b_channel = cv2.split(img) # 剥离jpg图像通道

  44. alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255 # 创建Alpha通道

  45. img_new = cv2.merge((r_channel, g_channel, b_channel, alpha_channel)) # 融合通道

  46. return img_new

  47. def handel_img(img):

  48. imgGray = cv2.cvtColor(img, cv2.COLOR_RGBA2GRAY) # 转灰度图

  49. imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1) # 高斯模糊

  50. imgCanny = cv2.Canny(imgBlur, 60, 60) # Canny算子边缘检测

  51. return imgCanny

  52. def match(img_jpg_path, img_png_path):

  53. # 读取图像

  54. img_jpg = cv2.imread(img_jpg_path, cv2.IMREAD_UNCHANGED)

  55. img_png = cv2.imread(img_png_path, cv2.IMREAD_UNCHANGED)

  56. # 判断jpg图像是否已经为4通道

  57. if img_jpg.shape[2] == 3:

  58. img_jpg = add_alpha_channel(img_jpg)

  59. img = handel_img(img_jpg)

  60. small_img = handel_img(img_png)

  61. res_TM_CCOEFF_NORMED = cv2.matchTemplate(img, small_img, 3)

  62. value = cv2.minMaxLoc(res_TM_CCOEFF_NORMED)

  63. value = value[3][0] # 获取到移动距离

  64. return value

  65. # 1. 打开chromedriver,试试下载图片

  66. cs = CrackSlider()

  67. cs.get_pic()

  68. # 2. 对比图片,计算距离

  69. img_jpg_path = 'target.jpg' # 读者可自行修改文件路径

  70. img_png_path = 'template.png' # 读者可自行修改文件路径

  71. distance = match(img_jpg_path, img_png_path)

  72. distance = distance /480 * 345 + 12

  73. # 3. 移动

  74. cs.crack_slider(distance)

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

 

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值