常见的滑动验证码就是哔哩哔哩了,就以哔哩哔哩作为例子
1、拿到完整图片
2、拿到有缺口的图片
3、计算滑动距离
4、滑动(如何避免被识别出来)
当鼠标放在上面不点击的时候,就会产生完整的图
当点击的时候就会出现有缺口的图片
在图片上右击检查,就会到图片的位置
然后打开图片的URL,你会发现这是一个乱序的图片
如何解决这个问题,
第一种解决办法:
就是将获取到的图片的按照前端的顺序,重新拼接
def get_merge_image(self, filename, location_list):
"""
合并图像,并还原图像
:return:
"""
im = image.open(filename)
new_im = image.new('RGB', (260, 116)) # width 260px, height 116px
im_list_upper = []
im_list_down = [] # 以列表形式储存按照一定的顺序储存图片碎片
for location in location_list: # 截取对应位置的图,并添加到列表中
if location['y'] == -58:
im_list_upper.append(im.crop((abs(location['x']), 58, abs(location['x'])+10, 166)))
if location['y'] == 0:
im_list_down.append(im.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)))
x_offset = 0
for im in im_list_upper:
new_im.paste(im, (x_offset, 0))
x_offset += im.size[0]
x_offset = 0
for im in im_list_down:
new_im.paste(im, (x_offset, 58))
x_offset += im.size[0]
new_im.save(filename)
time.sleep(1)
# 将图片碎片组合到复制到新图片上
return new_im
第二种:
截图,分别截图,完整的时候截一次图(截图全屏),不用管是否乱序,获取图片的坐标,将截图再次按照坐标截图处理,这样就能获得完整的图和有缺口的图。
def get_image(driver):
img = driver.find_element_by_class_name('gt_cut_fullbg')
time.sleep(2)
location = img.location
size = img.size
left = location['x']
top = location['y']
right = left + size['width']
bottom = top + size['height']
page_snap_obj = get_snap(driver)
image_obj = page_snap_obj.crop((left, top, right, bottom))
# image_obj.show()
return image_obj
def get_snap(driver):
driver.save_screenshot('full_snap.png')
page_snap_obj = Image.open('full_snap.png')
return page_snap_obj
使用PIL裁切图片
使用PIL需要引用Image,使用Image的open(file)方法可以返回打开的图片,使用crop((x0,y0,x1,y1))方法可以对图片做裁切。
区域由一个4元组定义,表示为坐标是 (left, upper, right, lower),Python Imaging Library 使用左上角为 (0, 0)的坐标系统
box(100,100,200,200)就表示在原始图像中以左上角为坐标原点,截取一个100*100(像素为单位)的图像,为方便理解,如下为示意图box(b1,a1,b2,a2)
这样我们就拿到了图片一个是完整的,一个是有缺口的。
第三步就是计算要滑动的距离了
def is_pixel_equal(self, img1, img2, x, y):
"""
判断两个像素是否相同
:param img1:
:param img2:
:param x: 位置x
:param y: 位置y
:return:
"""
# 取两张图片的像素点
pix1 = img1.load()[x, y]
pix2 = img2.load()[x, y]
threshold = 60
if (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(
pix1[2] - pix2[2] < threshold)):
return True
else:
return False
def get_pag(self, img1, img2):
"""
获取偏移量
:param img1:不带缺口
:param img2:带缺口
:return:
"""
left = 43
for i in range(left, img1.size[0]):
for j in range(img1.size[1]):
if not self.is_pixel_equal(img1, img2, i, j):
left = i
return left
return left
得到了滑动的距离就是计算如何去滑动了
def get_track(self, distance):
"""
根据偏移量获取移动轨迹
:param distance:
:return:
"""
distance += 24
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阀值
mid = distance * 3 / 5
# 计算阀值
t = 0.2
# 计算初速度
v = 0
while current < distance:
if current < mid:
# 加速度
a = 2
else:
# 加速度为-3
a = -3
# 初速度为
v0 = v
# 移动距离x = v0t+1/2*a^2
move = v0*t + 1/2*a*t*t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
# 速度已到达v,该速度作为下次的初速度
v = v0 + a*t
# track = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1]
return track
def get_track(self, x_offset):
track = list()
length = x_offset - 6
x = random.randint(1,5)
while length - x >4:
track.append([x,0,0])
length=length - x
x= random.randint(1,15)
for i in range(length):
if x_offset>47:
track.append([1,0,random.randint(10,12)/100.0])
else:
track.append([1, 0, random.randint(13, 14)/100.0])
return track
上面是两种获取移动轨迹的方式,大家可以自己试一下,那个效率更高。
最后就是控制滑块去滑动了
def move_to_gap(self, slider, track):
"""
拖动滑块到缺口处
:param slider: 滑块
:param track: 轨迹
:return:
"""
ActionChains(self.browser).click_and_hold(slider).perform()
while track:
x = random.choice(track)
ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
track.remove(x)
tracks = [-3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1]
time.sleep(0.5)
for track in tracks:
ActionChains(self.browser).move_by_offset(xoffset=track, yoffset=0).perform()
# 小范围震荡一下,进一步迷惑极验后台,这一步可以极大地提高成功率
ActionChains(self.browser).move_by_offset(xoffset=-1, yoffset=0).perform()
ActionChains(self.browser).move_by_offset(xoffset=1, yoffset=0).perform()
time.sleep(0.5)
ActionChains(self.browser).release().perform()
def simulate_drag(self, track):
dom_div_slider = self.browser.find_element_by_xpath('//*[@id="gc-box"]/div/div[3]/div[2]')
ActionChains(self.browser).click_and_hold(on_element=dom_div_slider).perform()
for x,y,z in track:
ActionChains(self.browser).move_to_element_with_offset(
to_element=dom_div_slider,
xoffset=x+22,
yoffset=y+22).perform()
time.sleep(z)
time.sleep(0.9)
ActionChains(self.browser).release(on_element=dom_div_slider).perform()
time.sleep(1)
dom_div_gt_info = self.browser.find_element_by_class_name('gt_info_type')
return dom_div_gt_info.text
试试两种不同的滑动方式有什么不同
方法都给大家了,大家可以自己多试试,找出最高效的滑动方式,
如果碰到其他的滑动验证码只需修改元素的定位,思路一样
关于ActionChains,不懂的就去查查
最后源码地址:https://github.com/AndrewAndrea/check_img_code
不要忘了star, 谢谢!
有问题可以留言
抖音剩下的下次的更新,如何破解api,还有appnium 的使用。
本文分享自 Python爬虫scrapy 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!