记录一下,滑块分为几种,由于找不到有原背景可以对比出像素位置的,其他的也做不来,就找了这个先入门凑活一下,针对头条的登录作为举例:https://sso.toutiao.com/login/
无事先百度谷歌,不行就多打几个空格,总会有回应的,把原理搞明白了再下手具体操作,对于接触到一个未知领域,能够很快入行就显得很牛逼了,反正我不是。
中途学习借鉴到:https://www.cnblogs.com/lmx123/p/9246215.html , https://www.kancloud.cn/aollo/aolloopencv/269602
思路:
分析:分析页面可以找到小滑块和大背景是两个图片,缺口和大背景是一张图片,所以直接算出距离是不太可能,按照selenium进行滑动,需要计算出滑动的距离,可以考虑图片识别(pillow, cv2)进行边缘检测或者降噪(反正能实现目的就可以),中间肯定会出现得到距离不准确的问题(先细心搞懂原理做出来,后面自己看着处理就好,别太在意冗余什么的),对于不准确的范围要自己进行判断。得到距离便可以进行滑动,但是滑动的太人机了肯定不行(一般都会识别),可以按照高中位移内容X=1/2*a*t*t (a为加速度,t为时间)可以得到一串位移的和为距离的列表,然后进行滑动就像人了(当然也可以划过头然后划回来)。
具体代码实现:
通过分析背景分析距离,所以肯定是越精确越好,图片识别也好识别,如果就拿screenshot,就准备按尺寸进行截取,具体参数,网站各不一样,但是都是常数
def save_img(self): # 懒得写显式等待了
# 进行初始点击到滑块
self.driver.find_element_by_xpath('//*[@id="login-type-account"]/span').click()
time.sleep(0.3)
self.driver.find_element_by_id("user-name").send_keys(self.username)
time.sleep(0.3)
self.driver.find_element_by_id("password").send_keys(self.password)
time.sleep(0.3)
self.driver.find_element_by_class_name("bytedance-submit").click()
time.sleep(2)
# 获取全背景图,进行裁剪保存
img = self.driver.find_element_by_class_name("sc-iwsKbI")
time.sleep(0.3)
location = img.location
print("location: ", location)
size = img.size
print("验证码大小:", size)
print(img.__dir__())
left, up, right, down = location["x"], location["y"], location["x"] + size["width"], location["y"] + size[
"height"]
print("截取后的图片信息:", left, up, right, down)
# 保存大背景图
self.driver.save_screenshot("toutiao.png")
# 截取小验证图
need_bg_img = Image.open("toutiao.png")
img_writer = need_bg_img.crop((left, up, right, down)) # 指定上下左右截取
img_writer.save("captcha.png")
原图 灰度图
左边的滑块y轴距离是不确定的,但是x轴是确定的,所以可以直接拿到x距离。然后右边是不固定的,可以通过对图像进行降噪或者边缘处理,由于技术问题,我直接进行变为灰度值,发现也还可以弄,获取到所有灰度值为255的,根据出现的y坐标频率最高的以及数值最大的判断得出缺口的x坐标,即缺口距离y轴距离。
def get_distance(self):
# 转成灰度图进行处理
img = cv2.imread("captcha.png", 0) # 由于Canny只能处理灰度图,所以将读取的图像转成灰度图
img = cv2.GaussianBlur(img, (3, 3), 0) # 用高斯平滑处理原图像降噪。若效果不好可调节高斯核大小
canny = cv2.Canny(img, 200, 600) # 调用Canny函数,指定最大和最小阈值,其中apertureSize默认为3。
# cv2.imshow('Canny', canny)
# 获取图片大小
x_len, y_len = canny.shape
# 遍历每个像素获取到 X 值,进行统计
x_arr = []
for i in range(1, x_len):
for j in range(1, y_len):
if canny[i, j] == 255.:
print(i, j)
x_arr.append(j)
print("顺序打印看一下:",sorted(x_arr))
# 根据频率统计得到缺口距离y轴距离
former_dict = Counter(x_arr).most_common(5)
far_distance = max(dict(former_dict).keys())
distance = far_distance - self.margin_left
return distance
只要看起来不像人机,怎么写都行,这里使用返回一个轨迹列表。
def get_track(self, distance): # 人为滑动是先慢 中快 后慢
"""
根据偏移量获取移动轨迹
:param distance: 偏移量
:return: 移动轨迹列表
"""
track = []
current = 0 # 当前位移
mid = distance * 4 / 5 # 设定一个阈值进行改变加速度
t = 1 # 计算间隔
v = 0 # 初速度
while current < distance:
if current < mid:
a = 1 # 加速度为正1
else:
a = -2 # 加速度为负2
v0 = v # 初速度v0
v = v0 + a * t # 当前速度v = v0 + at
# 移动距离x = v0t + 1/2 * a * t^2
move = v0 * t + 1 / 2 * a * t * t
current += move # 当前位移
track.append(round(move)) # 加入轨迹
return track
tt = TouTiao()
tt.save_img()
distance = tt.get_distance()
button = tt.driver.find_element_by_class_name("secsdk-captcha-drag-icon")
ActionChains(tt.driver).click_and_hold(button).perform()
print("获取到的distance为:",distance)
dis_list = tt.get_track(distance)
# 查看get_track函数的长短是否匹配
dis_list = list(filter(lambda x: x > 0, dis_list))
print("过滤掉为0的轨迹列表:",dis_list)
print("reduce获取到和值:",reduce(lambda x, y: x + y, dis_list))
for dis in dis_list:
ActionChains(tt.driver).move_by_offset(xoffset=dis, yoffset=0).perform()
ActionChains(tt.driver).release(button).perform()
time.sleep(0.3)
内容就这些,但是还是碰到了一些坑:
小结:
第一次写博客,sbxx(随便写写)吧,本来是想做有原背景的,因为有的话,只需要背景图和有缺口图对比,然后遍历像素就可以得到缺口的位置。然后没找着,就找个简单一点的把原理整明白。果然看点东西容易,写点东西还真要点东西,做个记录,慢慢来吧。
附上源码地址:隅偶的gitee