目录
PS(因为CV2 中的图片缺口匹配的方法也会有一定的失败率所以需要继续一个循环)
前言
本文需要使用到的第三方库比较多所以就一并放置此处
import os
import time
import random
from lxml import etree
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from io import BytesIO
from PIL import Image
import base64
import cv2 as cv
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.options import Options
import numpy as np
请自行依据名称 pip insatll 库名 - i http://pypi.doubanio.com/simple/
一、使用base64位方法解析bae64位图片数据
1、获取页面上的base64位数据流
这里获取网页的base64位图片数据流的方法,使用的方法有获取网页的全部JS数据保存至对象res中再进行一个js数据解析排序提取出来当前页面显示的base64位图片的数据流进行之后的解析
def Get_login_page(driver, url):
"""
:param driver: 浏览器驱动器
:param url: 登录页的URL
:return: UI页面滑动验证图片的本地保存名称
"""
WebDriverWait(driver, 30)
driver.get(url)
driver.find_element(By.XPATH, '//*[@id="ext-comp-1001"]').send_keys("liu001")
driver.find_element(By.XPATH, '//*[@id="pwd"]').send_keys("123abcABC@")
loging = driver.find_element(By.XPATH, '//*[@id="logon"]').click()
time.sleep(10)
res = driver.execute_script("return document.documentElement.outerHTML")
root = etree.HTML(res)
time.sleep(10)
resp_url_little = root.xpath('xxx(这里是定位到base64小图片的xp)/@src')
resp_url_big = root.xpath('xxx(这里是定位到base64位大图片的xp)/@src')
# 小图片的base64数据流
resp_url = resp_url_little[0]
# print(resp_url)
# 大图片的base64数据流
resp_big = resp_url_big[0]
# print(resp_big)
# 小图片位置,image_generator函数传出来的是图片的名称
small_picture = image_generator(resp_url)
# 大图片解析
Big_picture = image_generator(resp_big)
return small_picture, Big_picture, driver
2、解释图片的base64位解析函数
def image_generator(base64_data):
"""
:param base64_data: 图片的base64位数据流
:return: 生成后图片的名字
"""
binary_data = base64.b64decode(base64_data.split(",")[1])
# 使用 PIL 库将二进制数据转换为图片
image = Image.open(BytesIO(binary_data))
# blurred = cv.pyrMeanShiftFiltering(np.array(image), 10, 100)
# 将图片放大1.29倍
new_width = int(image.width * 1.19)
new_height = int(image.height * 1.19)
resized_image = image.resize((new_width, new_height))
RNG= random.randint(0, 1000)
# 保存至本地图片
resized_image.save(f"image{+RNG}.png")
print(f"image{+RNG}")
return f"image{+RNG}"
这里传入base64位数据后进行base64方法直接进行解析,解析成可图片二进制的数据流,使用PIL库转换成图片,PS(这里是本人项目中前端base64位解析出来的图片比UI显示的要小)rng是一个随机生成数据的函数,用来随机图片名称
二、CV2中的图片定位方法
1、缺口匹配方案
def identify_gap(bg, tp):
"""
:param bg: 背景图片
:param tp: 缺口图片
:return int
"""
# 读取背景图片和缺口图片
bg_img = get_cv2_img(bg) # 背景图片
tp_img = get_cv2_img(tp) # 缺口图片
if type(bg_img) == str or type(tp_img) == str:
print('图片格式存在问题,无法用cv2读取!')
return
# 识别图片边缘
bg_edge = cv.Canny(bg_img, 100, 200)
tp_edge = cv.Canny(tp_img, 100, 200)
# 转换图片格式
bg_pic = cv.cvtColor(bg_edge, cv.COLOR_GRAY2RGB)
tp_pic = cv.cvtColor(tp_edge, cv.COLOR_GRAY2RGB)
# 缺口匹配
res = cv.matchTemplate(bg_pic, tp_pic, cv.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
print(min_val, max_val, min_loc, max_loc)
# 标记位置
# 返回缺口的X坐标
max_loc_int = max_loc[0]+35.7
cv.circle(bg_pic, (max_loc), radius=5, color=(0, 0, 255), thickness=-1)
cv.imshow('Marked Image', bg_pic)
cv.waitKey(0)
cv.destroyAllWindows()
return max_loc_int
这里使用的的是图片对比法标记坐标的正式使用时可关闭,不然每次运行到这里都需要手动关闭显示图片。PS(相信看得懂得都看出来这里缺口匹配的位置为什么还要加上数据再传出呢?缺口匹配的方法成功率可达90%,是不知道前端的滑块与滑动条是如何匹配的,所以需要补上差值,可问前端拿该值)
判断图片是否生成完整
def get_cv2_img(img_object):
if type(img_object) == str:
cv_img = cv.imread(img_object)
elif type(img_object) == bytes:
image_data = BytesIO(img_object)
img = Image.open(image_data)
cv_img = np.asarray(img)
else:
cv_img = None
return cv_img
-
如果
img_object
的类型是字符串 (str
),那么它会被视为图像文件的路径,使用 OpenCV 的cv.imread()
函数读取该图像文件,并将读取到的图像赋值给cv_img
。 -
如果
img_object
的类型是字节 (bytes
),则会将字节数据转换为BytesIO
对象 (image_data
),然后使用PIL
的Image.open()
函数打开BytesIO
对象,将其作为图像,再使用numpy
的np.asarray()
函数将图像转换为ndarray
格式,最终赋值给cv_img
。 -
如果
img_object
的类型不是字符串也不是字节,则cv_img
被赋值为None
。
三、模拟人为去滑动滑动条移动滑块
1、防止自动化机器人检测
def generate_tracks(distance):
"""
:param distance: 传进来需要滑动至的位置
:return: 返回两个数组:一个用于加速拖动滑块,一个用于减速拖动滑块
"""
# 给距离加上20,这20像素用在滑块滑过缺口后,减速折返回到缺口
distance += 0
v = 40
t = 0.2
forward_tracks = []
current = 0
mid = distance * 5 / 8 # 减速阀值
while current < distance:
if current < mid:
a = random.randint(5, 7) # 加速运动
else:
a = -4 # 加速度-3
s = v * t + 0.5 * a * (t ** 2)
v = v + a * t
current += s
forward_tracks.append(round(s))
back_tracks = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1]
return forward_tracks, back_tracks
2、模拟人为去缓慢拉动滑块的拉动条
def do_match(driver, url,mode='slow'):
"""
:param driver: 登录至需要滑动地方的浏览器
:param mode:
:return:
"""
# Get_login_page函数传入执行到滑块步骤的浏览器
img_slider,img_bg,div = Get_login_page(driver,url)
value_2 = identify_gap(f'./{img_bg}.png', f'./{img_slider}.png') # 使用缺口匹配函数传出最佳匹配值
# 检查文件是否存在,然后进行删除
if os.path.exists(f'./{img_bg}.png'):
# 删除文件
os.remove(f'./{img_bg}.png')
print("图片已成功删除")
if os.path.exists(f'./{img_slider}.png'):
os.remove(f'./{img_slider}.png')
print("图片已成功删除")
else:
print("文件不存在")
action = ActionChains(div)
slider = div.find_element(By.XPATH, '滑动条拉动点的xp')
action.move_to_element(slider) # 填需要滑动的地方的定位
action.click_and_hold(slider).perform()
if mode == 'slow':
forward_tracks, back_tracks = generate_tracks(value_2)
for x in forward_tracks:
action.move_by_offset(x, 0).perform() # 前进移动滑块
for x in back_tracks:
action.move_by_offset(x, 0).perform() # 后退移动滑块
else:
action.move_by_offset(value_2, 0).perform() # 前进移动滑块
action.release().perform()
return div
这里集成前面的函数进行获取解析加模拟认为滑动滑块
PS(因为CV2 中的图片缺口匹配的方法也会有一定的失败率所以需要一个循环)
def match_code(driver, url, flag_element_xpath, mode='slow'):
"""
:param driver: 登录至滑动验证的浏览器驱动
:param flag_element_xpath:确定通过的信息的xp定位
:param mode:固定传的信息用来判断是否是否需要滑动
:return:
"""
while True:
try:
div = do_match(driver, url, mode)
time.sleep(2)
div.find_element(By.XPATH, '//*[@id="logon"]').click()
if div.find_element(By.XPATH, flag_element_xpath):
break
except:
pass
if __name__ == '__main__':
url = 'UI界面的URL' # 相信你懂的!!!
chrome_options = Options()
# 启用缓存
chrome_options.add_argument("--disk-cache-dir=/path/to/cache/directory")
flag_element_xpath = '//*[@id="_logininfo"]/ul[1]/li'
# 启动带有缓存的 Chrome 浏览器
driver = webdriver.Chrome(options=chrome_options)
match_code(driver, url, flag_element_xpath)
如安全检测比较高要不被检测出来是自动化机器人可自行设置浏览器的头部信息进行一个躲避的操作,这里只要打开项目连接时不太慢,所以就使用了一个启用浏览器缓存的方式去打开浏览器再传入
1、flag_element_xpath是你确定滑动检测通过后的xp既可以是其它
言外
def drivers(headless=None,procxy=None,time=40):
'''
初始化driver
'''
option = webdriver.ChromeOptions()
option.add_argument('--headless') #设置 Chrome 浏览器以无头模式运行。
option.add_argument('disable-infobars') #禁用信息栏(infobars)的显示
option.add_argument('--disable-gpu') #用于禁用 GPU 加速
option.add_argument('--lang=zh-cn') #设置浏览器的语言为简体中文
option.add_argument('--no-sandbox') #禁用沙箱模式
option.add_argument('disable-dev-shm-usage') #禁用使用/dev/shm临时文件系统
option.add_argument("--no-sandbox") # 禁用沙箱模式
option.add_argument("--disable-dev-shm-usage") #禁用/dev/shm临时文件系统的使用
option.add_argument("--window-size=1920x1080") # 设置浏览器窗口大小为 1920x1080。
option.add_argument("start-maximised") #确保浏览器以最大化的方式打开
option.add_experimental_option('excludeSwitches', ['enable-automation']) #排除开关选项中的 “enable-automation”,以防止被检测为自动化测试任务
option.add_argument("--disable-blink-features=AutomationControlled") #避免被网站检测到你正在使用自动化测试工具
# option.add_argument('--proxy-server=' + get_procxy()["http"])
driver = webdriver.Chrome(options=option)
driver.set_page_load_timeout(time)
driver.set_script_timeout(40)
# 全屏,为了方便点击,有些页面由于不是自适应所有全屏操作更为保险
# driver.maximize_window()
return driver
该函数是浏览器的部分头部信息,可自行参考