时间:2020-08-13 python教程 查看: 1238
准备工作
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
B站的滑块验证码如上。
这类验证码可以使用 selenium 操作浏览器拖拽滑块来进行破解,难点两个,一个如何确定拖拽到的位置,另一个是避开人机识别(反爬虫)。
确定滑块验证码需要拖拽的位移距离
有三种方式
各有优缺点。人工智能机器学习,确定滑块位置,需要进行训练,比较麻烦,也可以看是否存在在线api可以调用。以下介绍其他两种方式。
对比完整图片与缺失滑块的图片
| 仅介绍,本文不进行实现。对于B站来说,是准确率最高的方式(100%),但不能保证未来B站的滑块验证升级,导致不可用。
B站的滑块验证模块,一共有三张图片:
完整图、缺失滑块图、滑块图,都是由画布绘制出的。类似于:
完整图:
缺失滑块图:
滑块图:
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 拖拽即可。
边缘检测算法,确定位置
| 滑块基本上是个方形,通过算法确定方形起始位置即可。
介绍两种方式
第二种实现起来有些复杂,不进行实现了。
下面是第一种实现方式,会存在检测不出或错误的情况,使用时需要换一张验证码。也可能存在检测出的边是另一条(因为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滑块验证码的思路详解(完美避开人机识别),希望对大家有所帮助!