selenium+opencv实现滑块验证码的登陆

作者:plexming 时间:2022-03-28 06:49:04 

很多网站登录登陆时都要用到滑块验证码,在某些场景例如使用爬虫爬取信息时常常受到阻碍,想着用opencv的模板匹配试试能不能实现模拟登陆。本来觉得网上资料多应该还蛮容易,但实际上手还是搞了蛮久,在这里记录一下整个流程,网站无所谓主要是要有滑动验证码:

selenium+opencv实现滑块验证码的登陆

环境

python 3.9, selenium和Opencv相关依赖,用于抓取图片的requests包,具体安装这里不多讲了,其中selenium用的火狐版本。

selenium登录网站

整体流程就是这个样子:访问网站->点击登录->输入账号密码->搞定滑块验证->登录网站,其中最大的难点是滑块验证码,但在此之前我们当然要先让selenium自动打开网站把账号密码输好,我们通过find_element()方法定位输入框之后执行操作,元素的各个属性F12就可以找到:

selenium+opencv实现滑块验证码的登陆

代码如下:

options = webdriver.FirefoxOptions()
driver = webdriver.Firefox(options=options)
driver.get('网址')
driver.find_element("link text", "登录").click()
name = driver.find_element("id", "name-input")
name.send_keys("账号######") # 输入账号
pw = driver.find_element("id", "password-input")
pw.send_keys("密码#########") # 输入密码
driver.find_element("id", "submit").click() # 提交

requests抓取验证码图片

为了做后续处理我们需要把滑块验证码相关图片抓到本地,网上关于滑块验证码这块很多都是用原图和有缺口的图对比来确定缺口位置的,但是我并没有找到原图,这里用到的是有缺口的背景图和滑块图,如下:

滑块图:

selenium+opencv实现滑块验证码的登陆

有缺口的背景图:

selenium+opencv实现滑块验证码的登陆

这里爬图是selenium定位之后用requests包爬的,注意验证码和登陆界面不在一个iframe里,selenium记得切到对应iframe才能定位到图片,代码如下:

driver.switch_to.frame('tcaptcha_iframe')
# 切换iframe
img = driver.find_element("id", "slideBg").get_attribute('src')
headers = {
   'Accept': "application/json, text/plain, */*",
   'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
} # 请求头
r = requests.get(img, headers=headers)
with open('img.png', 'wb') as f:
   f.write(r.content)
block = driver.find_element("id", "slideBlock").get_attribute('src')
r = requests.get(block, headers=headers)
with open('block.png', 'wb') as f:
   f.write(r.content)

OpenCV识别缺口位置

接下来就是重点,如何确定缺口位置来定位滑动验证码该往哪滑。这里主要用到OpenCV的模板匹配。 首先对滑块也就是稍后匹配时用到的模板进行处理,这里主要就是把形状轮廓提取出来然后去掉多余的东西,先把原图变成灰度图:

tpl_gray = cv2.cvtColor(tpl, cv2.COLOR_BGR2GRAY)

selenium+opencv实现滑块验证码的登陆

可以看到边缘有一圈阴影部分,我们需要把周围这圈去掉,遍历找到黑色像素点把它变成和周围一样。

width, height = tpl_gray.shape
   for h in range(height):
       for w in range(width):
           if tpl_gray[w, h] == 0:
               tpl_gray[w, h] = 96

处理后变成了这样,然后把中间主体部分涂黑,也就是将图片二值化。

binary = cv2.inRange(tpl_gray, 96, 96)  # 二值化
kernel = np.ones((8, 8), np.uint8)
template = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 去白色噪点

处理完毕得到理想的模板图:

selenium+opencv实现滑块验证码的登陆

接下来对带有缺口的背景图进行处理,这个过程稍微麻烦一点,不过思路还是比较清晰的,还是先转化成灰度图再二值化,这里有一个问题,不同验证码图片之间差距很大,有的颜色很显眼,有的却很清淡,比如下面这两差别太大了,这就导致在二值化的过程中很难有一个固定的参数。

selenium+opencv实现滑块验证码的登陆

selenium+opencv实现滑块验证码的登陆

这里我根据图片的平均灰度值设定了几个区间,对不同区间的验证码图片传入不同参数进行二值化:

def avg_mean(img):
   mean_val, _, _, _ = cv2.mean(img)
   print("平均灰度:", mean_val)
   return mean_val

def match(img):
   gauss = cv2.GaussianBlur(img, [5, 5], 0)
   img_gray = cv2.cvtColor(gauss, cv2.COLOR_BGR2GRAY)
   cv2.imshow("111", img_gray)
   if avg_mean(img) > 140: # 二值化
       ret, target = cv2.threshold(img_gray, 105, 255, cv2.THRESH_BINARY)
   elif avg_mean(img) > 102:
       ret, target = cv2.threshold(img_gray, 95, 255, cv2.THRESH_BINARY)
   else:
       ret, target = cv2.threshold(img_gray, 85, 255, cv2.THRESH_BINARY)

处理过的结果大概像这样:

selenium+opencv实现滑块验证码的登陆

效果还是不错的,清晰的凸显了缺口位置,最后把背景图和模板传入opencv的模板匹配方法,记录下匹配到的坐标即可。

result = cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
left_up = max_loc
print(left_up)
right_down = (left_up[0] + height, left_up[1] + width)
cv2.rectangle(img, left_up, right_down, (0, 0, 255), 2)
cv2.imshow('res', img)

这里框出来看看效果:

selenium+opencv实现滑块验证码的登陆

模拟拖动滑块

识别出位置之后就要算出滑块移动了多少距离,我们可以看到滑块初始状态距离边缘有26个像素:

selenium+opencv实现滑块验证码的登陆

同时抓下来的图片相比在网页中放大了一倍,所以真实滑动距离是: 

(left_up - 26*2)/2

于是用selenium的actionchains模拟拖动滑块:

def drag_block(l):
   drag = driver.find_element("id", "tcaptcha_drag_button")
   ActionChains(driver).click_and_hold(on_element=drag).perform()
   ActionChains(driver).move_to_element_with_offset(to_element=drag, xoffset=l, yoffset=0).perform()
   ActionChains(driver).release().perform()

这样整个流程就搞定了,理论上这样简单粗暴的自动拖过去在很多时候会不奏效,还需要模拟人手动拖动,不过因为我做测试的时候直接就成功了,所以没写下去,整体思路大概是加速减速停几秒或者中间触发几个mouse_up(),mouse_down()事件。

脚本示例:

下面是测试时用到的脚本,selenium部分和主函数,拿某个CTF靶场做的测试,仅供参考,根据实际网站不同肯定得改改:

import time
import requests

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains

import block_loc

def drag_block(l):
   drag = driver.find_element("id", "tcaptcha_drag_button")
   ActionChains(driver).click_and_hold(on_element=drag).perform()
   ActionChains(driver).move_to_element_with_offset(to_element=drag, xoffset=l, yoffset=0).perform()
   ActionChains(driver).release().perform()

def login_in(username, password):
   headers = {
       'Accept': "application/json, text/plain, */*",
       'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
   }
   driver.get('网址')
   driver.find_element("link text", "登录").click()
   name = driver.find_element("id", "name-input")
   name.send_keys(username)
   pw = driver.find_element("id", "password-input")
   pw.send_keys(password)
   driver.find_element("id", "submit").click()
   time.sleep(2)
   driver.switch_to.frame('tcaptcha_iframe')
   img = driver.find_element("id", "slideBg").get_attribute('src')
   r = requests.get(img, headers=headers)
   with open('img.png', 'wb') as f:
       f.write(r.content)
   block = driver.find_element("id", "slideBlock").get_attribute('src')
   r = requests.get(block, headers=headers)
   with open('block.png', 'wb') as f:
       f.write(r.content)

if __name__ == '__main__':
   options = webdriver.FirefoxOptions()
   driver = webdriver.Firefox(options=options)
   user = "##########"
   pw = "############"
   login_in(user, pw)
   image = "img.png"
   tpl = "block.png"
   length = block_loc.match(image, tpl)
   print(length)
   drag_block(length)

Opencv部分:

import cv2
import numpy as np

def avg_mean(img):
   mean_val, _, _, _ = cv2.mean(img)
   print("平均灰度:", mean_val)
   return mean_val

def match(image, temp):
   img = cv2.imread(image)
   tpl = cv2.imread(temp)
   tpl_gray = cv2.cvtColor(tpl, cv2.COLOR_BGR2GRAY)
   # cv2.imshow("111", tpl_gray)
   width, height = tpl_gray.shape
   for h in range(height):
       for w in range(width):
           if tpl_gray[w, h] == 0:
               tpl_gray[w, h] = 96
   binary = cv2.inRange(tpl_gray, 96, 96)
   kernel = np.ones((8, 8), np.uint8)
   template = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
   # cv2.imshow('tpl', template)
   print(img.shape)
   gauss = cv2.GaussianBlur(img, [5, 5], 0)
   img_gray = cv2.cvtColor(gauss, cv2.COLOR_BGR2GRAY)
   # cv2.imshow("111", img_gray)
   if avg_mean(img) > 140:
       ret, target = cv2.threshold(img_gray, 105, 255, cv2.THRESH_BINARY) # 二值化
   elif avg_mean(img) > 102:
       ret, target = cv2.threshold(img_gray, 95, 255, cv2.THRESH_BINARY)  # 二值化
   else:
       ret, target = cv2.threshold(img_gray, 80, 255, cv2.THRESH_BINARY)
   # cv2.imshow('target', target)
   result = cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED)
   min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
   left_up = max_loc
   print(left_up)
   right_down = (left_up[0] + height, left_up[1] + width)
   cv2.rectangle(img, left_up, right_down, (0, 0, 255), 2)
   # cv2.imshow('res', img)
   length = (left_up[0] - 26*2)/2
   return length

来源:https://blog.csdn.net/qq_46145027/article/details/129971618

标签:selenium,opencv,滑块验证码
0
投稿

猜你喜欢

  • ASP.NET 2.0中的数据操作之九:跨页面的主/从报表

    2023-07-19 20:28:42
  • np.dot()函数的用法详解

    2023-06-14 02:12:21
  • python算法题 链表反转详解

    2021-01-26 11:47:35
  • python中with用法讲解

    2022-11-29 23:59:37
  • ACCESS如何打印窗体中当前显示的记录

    2008-11-20 16:31:00
  • 从Python的源码来解析Python下的freeblock

    2023-07-26 20:44:39
  • python在windows命令行下输出彩色文字的方法

    2021-11-10 09:20:36
  • Python连接Mssql基础教程之Python库pymssql

    2022-01-24 04:47:20
  • 给ubuntu18安装python3.7的详细教程

    2022-11-02 16:34:57
  • php返回相对时间(如:20分钟前,3天前)的方法

    2023-10-26 11:20:38
  • python导入坐标点的具体操作

    2023-02-24 19:59:46
  • Python3.6 Schedule模块定时任务(实例讲解)

    2022-08-14 15:55:00
  • 使用django实现一个代码发布系统

    2022-06-28 02:42:45
  • 安装MySQL的步骤和方法

    2009-07-30 08:38:00
  • 2008农历新年各大网站Logo秀

    2008-02-11 16:33:00
  • 屏蔽浏览器缓存另类方法

    2023-11-21 08:09:23
  • GoLang 逃逸分析的机制详解

    2023-08-06 16:46:43
  • FusionCharts图表显示双Y轴双(多)曲线

    2023-08-22 17:55:38
  • python处理json字符串(使用json.loads而不是eval())

    2023-06-13 11:50:39
  • 彻底删除thinkphp3.1案例blog标签的方法

    2023-11-21 12:01:01
  • asp之家 网络编程 m.aspxhome.com