Python破解BiliBili滑块验证码,完美避开人机识别( 二 )

使用selenium进行滑动验证(会失败)首先 , 我们需要从html中获取滑块验证的图片 , 通过执行js , 将画布像素转为base64 , 然后python即可获取 , 进行拖拽处理:
from selenium import webdriverimport timeimport base64from PIL import Imagefrom io import BytesIOfrom selenium.webdriver.support.ui import WebDriverWaitdef checkVeriImage(driver):WebDriverWait(driver, 5).until(lambda driver: driver.find_element_by_css_selector('.geetest_canvas_bg.geetest_absolute'))time.sleep(1)im_info = driver.execute_script('return document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png");')# 拿到base64编码的图片信息im_base64 = im_info.split(',')[1]# 转为bytes类型im_bytes = base64.b64decode(im_base64)with open('./temp_bg.png', 'wb') as f:# 保存图片到本地 , 方便查看预览f.write(im_bytes)image_data = https://www.isolves.com/it/cxkf/yy/Python/2020-06-18/BytesIO(im_bytes)bgImage = Image.open(image_data)# 滑块距离左边有 5~10 像素左右误差offsetX = VeriImageUtil().getVerticalLineOffsetX(bgImage)eleDrag = driver.find_element_by_css_selector(".geetest_slider_button")action_chains = webdriver.ActionChains(driver)action_chains.drag_and_drop_by_offset(eleDrag,offsetX-10,0).perform()貌似可以了 , 但实际上 , 验证时会遇到“拼图被怪物吃掉了 , 请重试” , 导致失败 。这是因为被检测到机器人(爬虫)操作了 。
避开人机识别B站滑块验证码的人机识别 , 其实不咋滴 , 主要靠是否存在停留间隔来判断 。一开始被网上文章误导 , 弄了什么距离=初速度乘以时间t + 1/2加速度乘以(时间平方)模拟拖拽 , 实际上是完全不对路的 。

Python破解BiliBili滑块验证码,完美避开人机识别

文章插图
 
webdriver.ActionChains(driver).drag_and_drop_by_offset(eleDrag,offsetX-10,0).perform() 拖动滑块会导致验证失败 。在B站中 , 这是因为这个动作太快了的缘故 。有的同学就打算直接加 time.sleep(1) 了 , 这么做是不会成功的 , 会提示拼图被怪物吃掉了 , 请重试
实际上人做滑块验证的过程可以归为:手指快速拖拽验证码到指定位置 , 修正误差 , 停留一会儿 , 释放滑块 。
简单实现代码可以简单实现 , 都不需要模拟人修正拖拽误差的过程 , 普通网站不会去统计这个 , 至少B站不会 。
def simpleSimulateDragX(self, source, targetOffsetX):"""简单拖拽模仿人的拖拽:快速沿着X轴拖动 , 直接一步到达正确位置 , 再暂停一会儿 , 然后释放拖拽动作B站是依据是否有暂停时间来分辨人机的 , 这个方法适用 。:param source::param targetOffsetX::return: None"""#参考`drag_and_drop_by_offset(eleDrag,offsetX-10,0)`的实现 , 使用move方法action_chains = webdriver.ActionChains(self.driver)# 点击 , 准备拖拽action_chains.click_and_hold(source)action_chains.pause(0.2)action_chains.move_by_offset(targetOffsetX,0)action_chains.pause(0.6)action_chains.release()action_chains.perform()添加修正过程的实现其实也就最后一段多出了fix的过程 , 
action_chains.move_by_offset(10,0)
def fixedSimulateDragX(self, source, targetOffsetX):#参考`drag_and_drop_by_offset(eleDrag,offsetX-10,0)`的实现 , 使用move方法action_chains = webdriver.ActionChains(self.driver)# 点击 , 准备拖拽action_chains.click_and_hold(source)action_chains.pause(0.2)action_chains.move_by_offset(targetOffsetX-10,0)action_chains.pause(0.6)action_chains.move_by_offset(10,0)action_chains.pause(0.6)action_chains.release()action_chains.perform()终极版实现为了更像人类操作 , 可以进行拖拽间隔时间和拖拽次数、距离的随机化 。虽然这对B站没什么用 , 还可能会导致验证时间变久一些 。
拖拽多次 , 可以使用循环遍历 , 不过代码可能不好理解 , 直接判断就行 , 最多也就两到3次就完成修正误差的过程 。
def __getRadomPauseScondes(self):""":return:随机的拖动暂停时间"""return random.uniform(0.6, 0.9)def simulateDragX(self, source, targetOffsetX):"""模仿人的拖拽动作:快速沿着X轴拖动(存在误差) , 再暂停 , 然后修正误差防止被检测为机器人 , 出现“图片被怪物吃掉了”等验证失败的情况:param source:要拖拽的html元素:param targetOffsetX: 拖拽目标x轴距离:return: None"""action_chains = webdriver.ActionChains(self.driver)# 点击 , 准备拖拽action_chains.click_and_hold(source)# 拖动次数 , 二到三次dragCount = random.randint(2, 3)if dragCount == 2:# 总误差值sumOffsetx = random.randint(-15, 15)action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)# 暂停一会action_chains.pause(self.__getRadomPauseScondes())# 修正误差 , 防止被检测为机器人 , 出现图片被怪物吃掉了等验证失败的情况action_chains.move_by_offset(-sumOffsetx, 0)elif dragCount == 3:# 总误差值sumOffsetx = random.randint(-15, 15)action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)# 暂停一会action_chains.pause(self.__getRadomPauseScondes())# 已修正误差的和fixedOffsetX = 0# 第一次修正误差if sumOffsetx < 0:offsetx = random.randint(sumOffsetx, 0)else:offsetx = random.randint(0, sumOffsetx)fixedOffsetX = fixedOffsetX + offsetxaction_chains.move_by_offset(-offsetx, 0)action_chains.pause(self.__getRadomPauseScondes())# 最后一次修正误差action_chains.move_by_offset(-sumOffsetx + fixedOffsetX, 0)action_chains.pause(self.__getRadomPauseScondes())else:raise Exception("莫不是系统出现了问题?!")# 参考action_chains.drag_and_drop_by_offset()action_chains.release()action_chains.perform()


推荐阅读