今天偶然看到一个pass滑块校验的帖子,觉得挺有趣,实验了一下,发现帖子上的一些东西已经被官方规避了,不想半途而废,所以就补充了一下。
记得当时文档主要有以下几点要注意:
- 定位滑块元素的时候,没有缺口的那个图css是设置为none的,所以没办法直接截图,需要js帮助
- 通过截图长宽和浏览器长宽对比得到截图比例,但是在计算滑块移动距离时要记得要按照当前浏览器的长宽再换算一次
- 计算滑块移动距离时,要减去一个固定宽度,因为滑块的左边和背景图的左边是有边距的
不多说了,上码,不知道会不会涉及某司安全问题,隐去部分元素名称,用中+英代替
from selenium import webdriver
from import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PIL import Image
from six import BytesIO
import time
from time import sleep
from selenium.webdriver import ActionChains
class BiliLogin():
def __init__(self,url, username, password):
self.browser = webdriver.Chrome ()
self.url = url
self.username = username
self.password = password
def get_incheck(self):
'''输入参数点击登录'''
self.browser.get (self.url)
self.browser.maximize_window ()
self.wait = WebDriverWait (self.browser, 10)
# self.wait.until (EC.presence_of_element_located ((By.CLASS_NAME, 'geetest-wrap')))
self.user_input = self.browser.find_element_by_id ("yonghuming~~~")
self.pwd_input = self.browser.find_element_by_id ("mima~~~")
self.btn = self.browser.find_element_by_link_text ('denglu~~~')
self.user_input.send_keys (self.username)
self.pwd_input.send_keys (self.password)
self.btn.click ()
self.element = WebDriverWait(self.browser, 10).until(lambda x: x.find_element_by_class_name("等待滑块出现哦"))
self.slider = self.browser.find_element_by_class_name ("滑块的class_name")
# 拖动滑块
#登录成功
def get_position(self):
# 使用location是获取标签左上角的位置,然后再通过该标签的大小,即可算出其四个角的位置
self.img_label = self.wait.until (EC.presence_of_element_located ((By.CLASS_NAME, '滑块图的class_name')))
location = self.img_label.location
size = self.img_label.size
, self.bottom, self.left, self.right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
'width']
return (self.left, , self.right, self.bottom)
def get_sliderbgimg(self):
'''获取带缺口图的浏览器截图'''
#执行一个js,隐藏小滑块
slider_none = "document.querySelector('body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowslide > div.geetest_panel_next > div > div.geetest_wrap > div.geetest_widget > div > a > div.geetest_canvas_img.geetest_absolute > div > 这里就是滑块的地址啦').style.display = 'none'"
self.browser.execute_script (slider_none)
sleep (1)
sliderbgimg = self.browser.get_screenshot_as_png()
f = BytesIO ()
f.write (sliderbgimg)
return Image.open (f)
def get_fullbgimg(self):
'''获取不带缺口的浏览器截图'''
# 再把缺口图也隐藏起来
sliderbg_none = "document.querySelector('body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowslide > div.geetest_panel_next > div > div.geetest_wrap > div.geetest_widget > div > a > div.geetest_canvas_img.geetest_absolute > div > 缺口图的地址哦').style.display = 'none'"
self.browser.execute_script (sliderbg_none)
#再把没有缺口的图漏出来
fullbg_blcok = "document.querySelector('body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowslide > div.geetest_panel_next > div > div.geetest_wrap > div.geetest_widget > div > a > div.完整图的地址哦').style.display = 'block'"
self.browser.execute_script (fullbg_blcok)
fullbgimg = self.browser.get_screenshot_as_png()
f = BytesIO ()
f.write (fullbgimg)
return Image.open (f)
def back(self):
'''复原图片位置'''
#复原没什么作用,可以不加,主要就是为对比下
slider_block = "document.querySelector('body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowslide > div.geetest_panel_next > div > div.geetest_wrap > div.geetest_widget > div > a > div.geetest_canvas_img.geetest_absolute > div > 啦啦啦').style.display = 'block'"
self.browser.execute_script (slider_block)
liderbg_block = "document.querySelector('body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowslide > div.geetest_panel_next > div > div.geetest_wrap > div.geetest_widget > div > a > div.geetest_canvas_img.geetest_absolute > div > 啦啦啦').style.display = 'block'"
self.browser.execute_script (liderbg_block)
fullbg_none = "document.querySelector('body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowslide > div.geetest_panel_next > div > div.geetest_wrap > div.geetest_widget > div > a > div.啦啦啦').style.display = 'none'"
self.browser.execute_script (fullbg_none)
def get_position_scale(self):
'''通过对比截图和浏览器宽高的大小,算出换算比例'''
# 由于截图是有浏览器的边缘的拖拽条,所以浏览器的宽度+10px
# 因为我用的是mac,浏览器没有边框就不加了
height = self.browser.execute_script ('return document.documentElement.clientHeight')
width = self.browser.execute_script ('return document.documentElement.clientWidth')
self.browser.get_screenshot_as_file("base.png")
png = Image.open("base.png")
self.x_scale = png.size[0] / (width)
self.y_scale = png.size[1] / (height)
return (self.x_scale, self.y_scale)
def getSliderbgAndFullbg(self):
# 从浏览器截图中截出缺口图和完整图
x_scale, y_scale = self.get_position_scale()
position = self.get_position()
self.position = [position[0] * x_scale, position[1] * y_scale, position[2] * x_scale, position[3] * y_scale]
self.sliderbgimg = self.get_sliderbgimg().crop(self.position)
self.fullbgimg = self.get_fullbgimg().crop(self.position)
return self.sliderbgimg,self.fullbgimg
def compare_pixel(self, slice_img, full_img, x, y):
pixel1 = slice_img.load ()[x, y]
pixel2 = full_img.load ()[x, y]
threshold = 50
if abs (pixel1[0] - pixel2[0]) <= threshold:
if abs (pixel1[1] - pixel2[1]) <= threshold:
if abs (pixel1[2] - pixel2[2]) <= threshold:
return True
return False
def compare(self):
'''取到缺口左上角坐标后,
按照比例换算出屏幕上距离,
再减去滑块距离边界的固定距离,
得到滑块的移动距离'''
slice_img, full_img = self.getSliderbgAndFullbg()
left = 0
for i in range (full_img.size[0]):
for j in range (full_img.size[1]):
if not self.compare_pixel (full_img, slice_img, i, j):
# i是截图的长度哦,要按照浏览器的比例换算回来
# 还有个滑块距离图片边框固定距离7
return round(i/self.x_scale-7)
return left
def get_track_action(self, distance):
"""
根据偏移量获取移动轨迹
:param distance: 偏移量
:return: 移动轨迹
“完全照抄网上的”
"""
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * 4 / 5
# 计算间隔
t = 0.2
# 初速度
v = 0
while current < distance:
if current < mid:
# 加速度为正 2
a = 2
else:
# 加速度为负 3
a = -3
# 初速度 v0
v0 = v
# 当前速度 v = v0 + at
v = v0 + a * t
# 移动距离 x = v0t + 1/2 * a * t^2
move = v0 * t + 1 / 2 * a * t * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
print(distance)
return track
def move_to_release(self):
"""
拖动滑块到缺口处
:param slider: 滑块
:param tracks: 轨迹
:return:
“还是完全摘抄的”
"""
tracks = self.get_track_action(self.compare())
self.back()
ActionChains (self.browser).click_and_hold (on_element=self.slider).perform()
for x in tracks:
ActionChains (self.browser).move_by_offset (xoffset=x, yoffset=0).perform ()
time.sleep (0.5)
ActionChains (self.browser).release ().perform ()
time.sleep(0.5)
if __name__ == "__main__":
url = "https://login*******"
user = "*******"
pa = "*****"
test = BiliLogin(url=url,username=user,password=pa)
test.get_incheck()
test.move_to_release()