大家应该都很熟悉
点击滑块然后移动到图片缺口进行验证
现在越来越多的网站使用这样的验证方式
为的是增加验证码识别的难度
那么
对于这种验证码
应该怎么破呢
接下来就是
打开 b 站的登录页面
可以看到登录的时候需要进行滑块验证
按下 F12
进入 Network
看下我们将滑块移到缺口松开之后做了什么提交
可以看到是一个 GET 请求
但是
这请求链接也太特么长了吧
我们来看看请求的参数是怎么样的
哇靠
gt?
challenge?
w?
这些都是什么鬼参数
还加密了
完全下不了手啊
既然以请求的方式不好弄
我们从它们的源代码入手
看看有什么突破口
回到 b 站的登录页
按下 F12
进入 Element
然后点击滑块出现了图片
定位一下
发现有两个 a 标签
一个 class 是 gt_bg gt_show
一个 class 是 gt_fullbg gt_show
和小帅b想的一样
这个验证码应该是有两张图片
一张是完全的背景图片
一张是缺口的图片
那把这两张图片下载下来对比一下不就行了
打开 a 标签一看
一张图片被切割成很多小块
原来这张图片是拼出来的
我们看看原始图片是怎么样的
什么乱七八糟的
再仔细看下源代码
原来是在同一张图片通过偏移量合成了一张完整的图片
background-position: -277px -58px;
看了一下缺口的图片也是如此
到这里
我们的第一个思路就是
下载这两张原始图片
然后通过偏移量合成两张真正的图片
背景图
缺口图
↓变身
那么怎么做呢?
因为我们还要模拟滑动滑块
所以呢
我们要用到 selenium
打开b站的登录页
然后等到那个滑块显示出来
# 获取滑块按钮
接下来我们就获取页面的源码
driver.page_source
然后使用 bs 获取两张原始背景图片的 url
bs = BeautifulSoup(driver.page_source,'lxml')
拿到了图片地址之后
将图片下载下来
# 将图片格式存为 jpg 格式
ok
我们已经把两张原始图片下载下来了
那么接下来就是要合成图片了
我们要根据图片的位置来合成
也就是源码中的 background-position
获取每一个小图片的位置
我们可以通过字典的形式来表示这些位置
然后将数据放到列表中
# 存放每个合成缺口背景图片的位置
那么
现在我们已经有了原始图片
还知道了每个位置应该显示原始图片的什么部分
接下来我们就写一个方法
用来合成图片
# 写入图片
那么问题又来了
怎么合成啊
我们再看看一开始分析的图片
这里图片被分割成的每一个小图片的尺寸是
10 * 58
所以我们也要将我们刚刚下载的原始图片切割成相应的尺寸大小
而且
这张图片是由上半部分的小图片和下半部分的小图片合成的
所以我们定义两个 list 来装这些小图片
# 存放上下部分的各个小块
然后将原始的图片切割好放进去
image = Image.open(image_file)
至此
我们这两个 list 就分别放好了各个切割的图片了
那么接下来就创建一张空白的图片
然后将小图片一张一张(间距为10)的粘贴到空白图片里
这样我们就可以得到一张合成好的图片了
# 创建一张大小一样的图片
那么到现在
我们可以得到网页上显示的那两张图片了
一张完全的图片
一张带缺口的图片
接下来我们就要通过对比这两张图
看看我们要滑动的距离是多远
# 合成图片
可以通过图片的 RGB 来计算
我们设定一个阈值
如果 r、g、b 大于这个阈值
我们就返回距离
def get_distance(bg_Image, fullbg_Image):
现在
我们知道了关键的滑动距离了
激动人心的时刻到了
我们使用 selenium
拿到滑块的元素
然后根据这个距离拖动到缺口位置不就好了么
马上打开 selenium 的文档
看到了这个函数
它可以使用左键点击元素
然后拖动到指定距离
最后释放鼠标左键
knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))
运行一下试试看吧
妖怪吃了拼图了
看来直接拖拽是不行的
试着拖完滑块让它睡一下再释放
ActionChains(driver).click_and_hold(knob).perform()
发现拼图还是特么的被妖怪吃了
有个叫匀速直线运动的东西
什么 加速度
什么 v = v0 + at
什么 s = ½at²
什么鬼
回到正题
我们可以使用它来构造一个运动路径
该加速时加速
该减速的时候减速
这样的话就更像人类在滑动滑块了
这次
我们使用这个轨迹来滑动
knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))
好了好了
我们再来运行一下吧
哈哈哈
成功识别了
当然了
成功率不是 100%
可以多调戏它几次
ok
以上就是识别滑动验证码的具体过程了
对于其它大部分的滑动验证码
也是可以使用这招搞定的
如果大家想找一个Python学习环境,可以加入我们的 Python学习[秋秋]圈:784758214 更多案例资源
完整代码:
进口时间
导入请求
来自 PIL 导入图片
来自 selenium import webdriver
来自 selenium.webdriver 导入 ActionChains
从 selenium.webdriver.common.by 进口通过
来自 selenium.webdriver.support.ui 导入 WebDriverWait
从 selenium.webdriver.support 导入 expected_conditions 作为 EC
来自 bs4 进口 BeautifulSoup
进口重新
来自 io import BytesIO
driver = webdriver.Chrome(' / usr / lib / chromium-browser / chromedriver ')
WAIT = WebDriverWait(驱动程序,10)
url = ' https://passport.bilibili.com/login '
def mergy_Image(image_file,location_list):
“””
将原始图片进行合成
:param image_file:图片文件
:param location_list:图片位置
:return:合成新的图片
“””
#存放上下部分的各个小块
upper_half_list = []
down_half_list = []
image = Image.open(image_file)
#通过y的位置来判断是上半部分还是下半部分,然后切割
在 location_list 中的位置:
如果 location [ ' y ' ] == - 58:
#间距为10,Y:58-116
IM = image.crop((ABS(位置[ ' X ' ]),58,ABS(位置[ ' X ' ])+ 10,116))
upper_half_list.append(IM)
如果 location [ ' y ' ] == 0:
#间距为10,Y:0-58
IM = image.crop((ABS(位置[ ' X ' ]),0,ABS(位置[ ' X ' ])+ 10,58))
down_half_list.append(IM)
#创建一张大小一样的图片
new_image = Image.new(' RGB ',(260,116))
#粘贴好上半部分y坐标是从上到下(0-116)
offset = 0
对于 IM 在 upper_half_list:
new_image.paste(im,(offset,0))
偏移+ = 10
#粘贴好下半部分
offset = 0
对于 IM 在 down_half_list:
new_image.paste(im,(offset,58))
偏移+ = 10
返回 new_image
def get_distance(bg_Image,fullbg_Image):
#阈值
阈值= 200
print(bg_Image.size [ 0 ])
print(bg_Image.size [ 1 ])
对于我在 范围(60,bg_Image.size [ 0 ]):
为 Ĵ 在 范围(bg_Image.size [ 1 ]):
bg_pix = bg_Image.getpixel((i,j))
fullbg_pix = fullbg_Image.getpixel((i,j))
r = abs(bg_pix [ 0 ] - fullbg_pix [ 0 ])
g = abs(bg_pix [ 1 ] - fullbg_pix [ 1 ])
b = abs(bg_pix [ 2 ] - fullbg_pix [ 2 ])
如果 r + g + b >阈值:
回来我
def get_path(距离):
result = []
current = 0
中期=距离* 4 / 5
t = 0.2
v = 0
而当前<(距离- 10):
如果当前< mid:
a = 2
否则:
a = - 3
v0 = v
v = v0 + a * t
s = v0 * t + 0.5 * a * t * t
当前+ = s
result.append(round(s))
返回结果
def start_drag(driver,distance):
#被妖怪吃掉了
#旋钮= WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, “#GC-箱> DIV> div.gt_slider> div.gt_slider_knob.gt_show”)))
# ActionChains(司机).click_and_hold(旋钮).perform()
# ActionChains(驱动程序).move_by_offset(X偏移=距离,Y偏移= 0.1).perform()
# time.sleep(0.5)
# ActionChains(司机).release(旋钮).perform()
#被妖怪吃掉了
# ActionChains(驱动程序).drag_and_drop_by_offset(旋钮,距离10,0).perform()
knob = WAIT .until(EC .presence_of_element_located((。CSS_SELECTOR,“#gc-box> div> div.gt_slider> div.gt_slider_knob.gt_show ”)))
result = get_path(距离)
ActionChains(司机).click_and_hold(旋钮).perform()
for x in result:
ActionChains(驱动程序).move_by_offset(xoffset = x,yoffset = 0).perform()
time.sleep(0.5)
ActionChains(司机).release(旋钮).perform()
高清 recognize_code(司机):
“””
识别滑动验证码
:param driver:selenium驱动
:返回:
“””
bs = BeautifulSoup(driver.page_source,' lxml ')
#找到背景图片和缺口图片的DIV
bg_div = bs.find_all(class_ = ' gt_cut_bg_slice ')
fullbg_div = bs.find_all(class_ = ' gt_cut_fullbg_slice ')
#获取缺口背景图片网址
bg_url = re.findall(' background-image:\ surl \(“(。*?)”\)',bg_div [ 0 ] .get(' style '))
#获取背景图片的URL
fullbg_url = re.findall(' background-image:\ surl \(“(。*?)”\)',fullbg_div [ 0 ] .get(' style '))
#存放每个合成缺口背景图片的位置
bg_location_list = []
#存放每个合成背景图片的位置
fullbg_location_list = []
为 BG 在 bg_div:
location = {}
location [ ' x ' ] = int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',bg.get(' style '))[ 0 ] [ 0 ])
location [ ' y ' ] = int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',bg.get(' style '))[ 0 ] [ 1 ])
bg_location_list.append(位置)
for fullbg in fullbg_div:
location = {}
location [ ' x ' ] = int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',fullbg.get(' style '))[ 0 ] [ 0 ])
location [ ' y ' ] = int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',fullbg.get(' style '))[ 0 ] [ 1 ])
fullbg_location_list.append(位置)
print(bg_location_list)
print(fullbg_location_list)
#将图片格式存为jpg格式
bg_url = bg_url [ 0 ] .replace(' webp ',' jpg ')
fullbg_url = fullbg_url [ 0 ] .replace(' webp ',' jpg ')
#打印(bg_url)
#打印(fullbg_url)
#下载图片
bg_image = requests.get(bg_url).content
fullbg_image = requests.get(fullbg_url).content
print('完成图片下载')
#写入图片
bg_image_file = BytesIO(bg_image)
fullbg_image_file = BytesIO(fullbg_image)
#合成图片
bg_Image = mergy_Image(bg_image_file,bg_location_list)
fullbg_Image = mergy_Image(fullbg_image_file,fullbg_location_list)
# bg_Image.show()
# fullbg_Image.show()
#计算缺口偏移距离
distance = get_distance(bg_Image,fullbg_Image)
print('得到 距离:%s '% str(距离))
start_drag(司机,距离)
如果 __name__ == ' __main__ ':
#获取滑块按钮
driver.get(URL)
slider = WAIT .until(EC .element_to_be_clickable(
(通过.CSS_SELECTOR,“#gc-box> div> div.gt_slider> div.gt_slider_knob.gt_show ”)))
recognize_code(驱动器)
# driver.close()