Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)


Posted in Python onFebruary 17, 2020

准备工作

B站登录页 https://passport.bilibili.com/login
python3
pip install selenium (webdriver框架)
pip install PIL (图片处理)
chrome driver:http://chromedriver.storage.googleapis.com/index.html
firefox driver:https://github.com/mozilla/geckodriver/releases

Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)

B站的滑块验证码如上。

这类验证码可以使用 selenium 操作浏览器拖拽滑块来进行破解,难点两个,一个如何确定拖拽到的位置,另一个是避开人机识别(反爬虫)。

确定滑块验证码需要拖拽的位移距离

有三种方式

  • 人工智能机器学习,确定滑块位置
  • 通过完整图片与缺失滑块的图片进行像素对比,确定滑块位置
  • 边缘检测算法,确定位置

各有优缺点。人工智能机器学习,确定滑块位置,需要进行训练,比较麻烦,也可以看是否存在在线api可以调用。以下介绍其他两种方式。

对比完整图片与缺失滑块的图片

| 仅介绍,本文不进行实现。对于B站来说,是准确率最高的方式(100%),但不能保证未来B站的滑块验证升级,导致不可用。

B站的滑块验证模块,一共有三张图片:

完整图、缺失滑块图、滑块图,都是由画布绘制出的。类似于:

完整图:

Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)

缺失滑块图:

Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)

滑块图:

Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)

HTML代码类似于:

<div class="geetest_canvas_img geetest_absolute" style="display: block;">
<div class="geetest_slicebg geetest_absolute">
	<canvas class="geetest_canvas_bg geetest_absolute" height="160" width="260"></canvas>
	<canvas class="geetest_canvas_slice geetest_absolute" width="260" height="160"></canvas>
</div>
<canvas class="geetest_canvas_fullbg geetest_fade geetest_absolute" height="160" width="260" style="display: none;"></canvas>
</div>

只需要通过selenium获取画布元素,执行js拿到画布像素,遍历完整图和缺失滑块图的像素,一旦获取到差异(需要允许少许像素误差),像素矩阵x轴方向即是滑块位置。
另外由于滑块图距离画布坐标原点有距离,还需要减去这部分距离。
最后使用 selenium 拖拽即可。

边缘检测算法,确定位置

| 滑块基本上是个方形,通过算法确定方形起始位置即可。

Python破解BiliBili滑块验证码的思路详解(完美避开人机识别)

介绍两种方式

  • 滑块是方形的,存在垂直的边,该边在缺失滑块图中基本都是灰黑的。遍历像素找到基本都是灰黑的边即可。
  • 缺失滑块图中滑块位置是灰黑封闭的。通过算法可以找到封闭区域,大小与滑块相近,即是滑块需要拖拽到的位置。

第二种实现起来有些复杂,不进行实现了。

下面是第一种实现方式,会存在检测不出或错误的情况,使用时需要换一张验证码。也可能存在检测出的边是另一条(因为B站的滑块不是长方形,存在弧形边),那么需要减去滑块宽度

class VeriImageUtil():

 def __init__(self):
  self.defaultConfig = {
   "grayOffset": 20,
   "opaque": 1,
   "minVerticalLineCount": 30
  }
  self.config = copy.deepcopy(self.defaultConfig)

 def updateConfig(self, config):
  # temp = copy.deepcopy(config)
  for k in self.config:
   if k in config.keys():
    self.config[k] = config[k]

 def getMaxOffset(self, *args):
  # 计算偏移平均值最大的数
  av = sum(args) / len(args)

  maxOffset = 0
  for a in args:
   offset = abs(av - a)
   if offset > maxOffset:
    maxOffset = offset
  return maxOffset

 def isGrayPx(self, r, g, b):
  # 是否是灰度像素点,允许波动offset
  return self.getMaxOffset(r, g, b) < self.config["grayOffset"]

 def isDarkStyle(self, r, g, b):
  # 灰暗风格
  return r < 128 and g < 128 and b < 128

 def isOpaque(self, px):
  # 不透明
  return px[3] >= 255 * self.config["opaque"]

 def getVerticalLineOffsetX(self, bgImage):
  # bgImage = Image.open("./image/bg.png")
  # bgImage.im.mode = 'RGBA'
  bgBytes = bgImage.load()

  x = 0
  while x < bgImage.size[0]:
   y = 0
   # 点》》线,灰度线条数量
   verticalLineCount = 0
   if x == 258:
    print(y)
   while y < bgImage.size[1]:
    px = bgBytes[x, y]
    r = px[0]
    g = px[1]
    b = px[2]
    # alph = px[3]
    # print(px)
    if self.isDarkStyle(r, g, b) and self.isGrayPx(r, g, b) and self.isOpaque(px):
     verticalLineCount += 1
    else:
     verticalLineCount = 0
     y += 1
     continue

    if verticalLineCount >= self.config["minVerticalLineCount"]:
     # 连续多个像素都是灰度像素,直线
     # print(x, y)
     return x

    y += 1

   x += 1
  pass


if __name__ == '__main__':
 bgImage = Image.open("./image/bg.png")
 veriImageUtil = VeriImageUtil()

 # veriImageUtil.updateConfig({
 #  "grayOffset": 20,
 #  "opaque": 0.6,
 #  "minVerticalLineCount": 10
 # })
  bgOffsetX = veriImageUtil.getVerticalLineOffsetX(bgImage)
 print("bgOffsetX:{} ".format(bgOffsetX))

总结

以上所述是小编给大家介绍的Python破解BiliBili滑块验证码的思路详解(完美避开人机识别),希望对大家有所帮助!

Python 相关文章推荐
手动实现把python项目发布为exe可执行程序过程分享
Oct 23 Python
Python循环语句之break与continue的用法
Oct 14 Python
Python解析最简单的验证码
Jan 07 Python
python中metaclass原理与用法详解
Jun 25 Python
python字典的setdefault的巧妙用法
Aug 07 Python
Python中sorted()排序与字母大小写的问题
Jan 14 Python
python_array[0][0]与array[0,0]的区别详解
Feb 18 Python
Python关于反射的实例代码分享
Feb 20 Python
Django单元测试中Fixtures的使用方法
Feb 26 Python
ITK 实现多张图像转成单个nii.gz或mha文件案例
Jul 01 Python
使用Python爬虫爬取小红书完完整整的全过程
Jan 19 Python
pytorch 一行代码查看网络参数总量的实现
May 12 Python
Tensorflow 实现将图像与标签数据转化为tfRecord文件
Feb 17 #Python
将自己的数据集制作成TFRecord格式教程
Feb 17 #Python
tensorflow 实现数据类型转换
Feb 17 #Python
Django Haystack 全文检索与关键词高亮的实现
Feb 17 #Python
python使用docx模块读写docx文件的方法与docx模块常用方法详解
Feb 17 #Python
python itsdangerous模块的具体使用方法
Feb 17 #Python
django-crontab实现服务端的定时任务的示例代码
Feb 17 #Python
You might like
新手配置 PHP 调试环境(IIS+PHP+MYSQL)
2007/01/10 PHP
如何使用PHP对网站验证码进行破解
2015/09/17 PHP
WordPress中用于获取文章信息以及分类链接的函数用法
2015/12/18 PHP
JS 容错处理代码, 屏蔽错误信息
2021/03/09 Javascript
javascript学习随笔(使用window和frame)的技巧
2007/03/08 Javascript
javascript之卸载鼠标事件的代码
2007/05/14 Javascript
JQuery获取浏览器窗口内容部分高度的代码
2012/02/24 Javascript
javascript在子页面中函数无法调试问题解决方法
2014/01/17 Javascript
Javascript中数组方法汇总(推荐)
2015/04/01 Javascript
基于javascript实现图片预加载
2016/01/05 Javascript
jquery日历插件datepicker用法分析
2016/01/22 Javascript
JavaScript中三个等号和两个等号的区别(== 和 ===)浅析
2016/09/22 Javascript
js事件冒泡与事件捕获详解
2017/02/20 Javascript
vue2.0 如何把子组件的数据传给父组件(推荐)
2018/01/15 Javascript
AngularJS监听ng-repeat渲染完成的两种方法
2018/01/16 Javascript
Vue.js+Layer表格数据绑定与实现更新的实例
2018/03/07 Javascript
webpack@v4升级踩坑(小结)
2018/10/08 Javascript
实例讲解vue源码架构
2019/01/24 Javascript
vue scroll滚动判断的实现(是否滚动到底部、滚动方向、滚动节流、获取滚动区域dom元素)
2020/06/11 Javascript
nuxt 每个页面head标签内容设置方式
2020/11/05 Javascript
vue绑定class的三种方法
2020/12/24 Vue.js
[03:36]DOTA2完美大师赛coL战队趣味视频——我演你猜
2017/11/23 DOTA
NumPy 数学函数及代数运算的实现代码
2018/07/18 Python
OpenCV2从摄像头获取帧并写入视频文件的方法
2018/08/03 Python
python3+selenium实现qq邮箱登陆并发送邮件功能
2019/01/23 Python
Python爬虫实现“盗取”微信好友信息的方法分析
2019/09/16 Python
Django模板标签{% for %}循环,获取制定条数据实例
2020/05/14 Python
Python urllib request模块发送请求实现过程解析
2020/12/10 Python
Python Selenium库的基本使用教程
2021/01/04 Python
泰国王权免税店官方网站:KingPower
2019/03/11 全球购物
巴西宠物商店:Cobasi
2019/04/19 全球购物
Claire’s法国:时尚配饰、美容、珠宝、头发
2021/01/16 全球购物
超市实习总结自我鉴定
2013/09/19 职场文书
影视艺术学院毕业生自荐信
2013/11/13 职场文书
开服装店计划书
2014/08/15 职场文书
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
2022/04/04 Python