前言
在爬取数据中,不可避免的会遇到各种验证,上一篇文章说了二维码验证的办法
示例
本篇文章讲的是遇到滑块验证的解决方案,本例是在一个b2b的网站内遇到的滑块验证
重点
真实人的拖动操作,是y轴和x都有变化的。这是重中之重。
思路
- 保存完整图片和缺口图片
- 找到缺口和图片的距离
- 生成移动轨迹
- 松开鼠标,确定移动结果
保存(完整和缺口)图片的代码:
def get_code_image(driver, fiel_name, ele_name='token-img', is_by_id=True):
if is_by_id is True:
code_element = driver.find_element_by_id(ele_name)
else:
code_element = driver.find_element_by_css_selector(ele_name)
code_element.location_once_scrolled_into_view
driver.save_screenshot(fiel_name)
left = code_element.location_once_scrolled_into_view['x']
top = code_element.location_once_scrolled_into_view['y']
right = code_element.size['width'] + left
height = code_element.size['height'] + top
im = Image.open(fiel_name)
img = im.crop((left, top, right, height)) # 截取指定元素图片保存
save_res = img.save(fiel_name)
return save_res
处理缺口和完整图片需要设置页面元素的展示和隐藏不同页面操作不同,本例使用的是执行js控制页面元素。
js_code = """
var x = document.getElementsByClassName('gt_slice')[0].style.display="none";
console.log(x)
"""
# 执行js代码
self.browser.execute_script(js_code)
找缺口距离的代码
def get_distance(cut_image, full_image):
cut_image = Image.open(cut_image) # 缺口背景图
full_image = Image.open(full_image) # 完整背景图
threshold = 86 # 灰度值正好为86,86,86 这个是透明度的差值,边界值像素的RGB中的B值为准
for i in range(55, cut_image.size[0]): # 75为滑块的截图最右边阴影到图片最左端的长度
for j in range(0, cut_image.size[1]):
pixel1 = cut_image.getpixel((i, j))
pixel2 = full_image.getpixel((i, j))
res_R = abs(pixel1[0] - pixel2[0]) # 计算RGB差
res_G = abs(pixel1[1] - pixel2[1]) # 计算RGB差
res_B = abs(pixel1[2] - pixel2[2]) # 计算RGB差
if res_R > threshold and res_G > threshold and res_B > threshold:
print(i-7)
return i-7
return 0
根据距离计算出拖动的弧度
def get_stacks(distance):
distance+5
'''
拿到移动轨迹,模仿人的滑动行为,先匀加速后匀减速
变速运动基本公式:
① v=v0+at 匀加速\减速运行
② s=v0t+½at² 位移
③ v²-v0²=2as
'''
# 初速度
v0 = 0
# 加减速度列表
a_list = [50, 65, 80]
# 时间
t = 0.4
# 初始位置
s = 0
# 向前滑动轨迹
forward_stacks = []
mid = distance * 3 / 5
while s < distance:
if s < mid:
a = a_list[random.randint(0, 2)]
else:
a = -a_list[random.randint(0, 2)]
v = v0
stack = v * t + 0.5 * a * (t ** 2)
# 每次拿到的位移
stack = round(stack)
if (s + stack) > distance:
stack = distance - s + 5
s += stack
v0 = v + a * t
forward_stacks.append(stack)
back_stacks = [-5,]
print(forward_stacks)
return {'forward_stacks': forward_stacks, 'back_stacks': back_stacks}
重点
接下来是拖动要真实模拟人的操作,不可能y轴不变,这是重中之重。
n= 0
for forward_stack in forward_stacks:
ActionChains(self.browser).move_by_offset(xoffset=forward_stack, yoffset=n).perform()
n += 1 + random.randint(1, 2)
time.sleep(0.5)
for back_stack in back_stacks:
ActionChains(self.browser).move_by_offset(xoffset=back_stack, yoffset=n).perform()
time.sleep(0.01)
time.sleep(0.5)
n += 1 + random.randint(1, 2)
yoffset=n
这两行是重点,很多文章中没有介绍。