1.问题界面分析
验证码网址:https://007.qq.com/online.html
解决方法:
我们用的时selenium自动测试框架,selenium能够模拟人工操作网页界面,只用selenium很显然不太足够,我们还需要用到opencv,opencv是一个图像处理库,安装他的方法网上都有,我们在这里主要是用它来识别缺口图像的位置的,在实际应用中可能还需要用到PIL,因为我发现图片是经过缩放的,所以在实际操作中,我们应该将图片缩放后再去判断缺口位置,其次还需要用到urllib去网页上获取图片保存到本地再去判断。有的滑块一打开并不是在最左边我们也需要判断这个位置,可以直接定位按钮获取style属性在加上此段距离。
代码流程:
基于上面的网址界面,我们第一步是找到弹出验证码的按钮的xapth路径,当然在实际开发中,可能需要先去定位用户输入框与密码输入框,然后定位登录按钮才会触发验证码界面,这里的话我们直接弹出验证码界面即可,然后定位到补全图片以及被补全图片的img src 地址,然后通过urllib请求下载到本地,通过PIL按实际缩放比例缩放,然后通过opencv识别缺口位置,最后判断滑块在缺口位置的左坐标,然后通过selenium模拟鼠标拖动滑块进行验证。
2.应用代码
#!usr/bin/env python
# -*- coding:utf-8 -*-
"""
@author:白杨
@file: 破解验证码登录.py
@time: 2021/10/19
"""
import time
from tenacity import retry, stop_after_attempt
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from PIL import Image
from icecream import ic
import urllib.request
import cv2
import numpy as np
# 避免意外,使用python重试机制, 发生意外重试十次
@retry(stop=stop_after_attempt(10))
def Crack():
"""
破解滑动验证码
"""
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True) # 程序结束chrome不被关闭
chrome = webdriver.Chrome(options=option)
# chrome = webdriver.Chrome()
chrome.maximize_window() # 浏览器最大化
chrome.get(url='https://007.qq.com/online.html') # 跳转页面
chrome.find_element(By.XPATH, '//*[@id="code"]').click() # 点击弹出验证码按钮
time.sleep(3)
iframe = chrome.find_element(By.XPATH, '//*[@id="tcaptcha_iframe"]') # 找到“嵌套验证码界面”的iframe
chrome.switch_to.frame(iframe) # 切换到iframe
button = chrome.find_element(By.XPATH, '//*[@id="tcaptcha_drag_thumb"]') # 找到滑动按钮
img1 = chrome.find_element(By.XPATH, '//*[@id="slideBg"]').get_attribute('src') # 找到待补全图并获取src属性
img2 = chrome.find_element(By.XPATH, '//*[@id="slideBlock"]').get_attribute('src') # 找到缺口图并获取src属性
resp1 = urllib.request.urlopen(img1) # 下载待补全图
image1 = np.asarray(bytearray(resp1.read()), dtype="uint8")
new_img1 = cv2.imdecode(image1, cv2.IMREAD_COLOR) # cv2.imdecode()函数将数据解码成Opencv图像格式
resp2 = urllib.request.urlopen(img2) # 下载待缺口图
image2 = np.asarray(bytearray(resp2.read()), dtype="uint8")
new_img2 = cv2.imdecode(image2, cv2.IMREAD_COLOR) # cv2.imdecode()函数将数据解码成Opencv图像格式
cv2.imwrite('login1.jpg', new_img1) # 保存在本地
cv2.imwrite('login2.jpg', new_img2) # 保存在本地
im = Image.open('login1.jpg') # 重新打开图像
im = im.resize((341, 195)) # 缩放
im.save('login1.jpg')
im = Image.open('login2.jpg')
im = im.resize((68, 68)) # 缩放
im.save('login2.jpg')
im1 = cv2.imread("login1.jpg") # 再次打开
im2 = cv2.imread('login2.jpg')
bg_edge = cv2.Canny(im1, 100, 200) # 识别图片边缘
tp_edge = cv2.Canny(im2, 100, 200)
bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB) # 转换图片格式
tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB)
res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED) # 缺口匹配
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
x = max_loc[0] # 滑块在验证图片的x坐标(左边)
# 创建一个新的ActionChains,将webdriver实例对driver作为参数值传入,然后通过WenDriver实例执行用户动作
ActionChains(chrome).click_and_hold(on_element=button).perform()
# 第一步:在滑块处按住鼠标左键
ActionChains(chrome).move_by_offset(xoffset=x - 27, yoffset=0).perform() # 鼠标移动到距离当前位置(x,y)
time.sleep(2)
# 第二步:相对鼠标当前位置进行移动
ActionChains(chrome).release(on_element=button).perform()
# 第三步:释放鼠标
time.sleep(5)
ic("登陆成功")
time.sleep(5)
if __name__ == '__main__':
Crack()