今天

要来说说滑动验证码了

大家应该都很熟悉

点击滑块然后移动到图片缺口进行验证

现在越来越多的网站使用这样的验证方式

为的是增加验证码识别的难度

那么

对于这种验证码

应该怎么破呢

接下来就是

学习 python 的正确姿势

打开 b 站的登录页面

https://passport.bilibili.com/login

可以看到登录的时候需要进行滑块验证

按下 F12

进入 Network

看下我们将滑块移到缺口松开之后做了什么提交

可以看到是一个 GET 请求

但是

这请求链接也太特么长了吧

就是比小帅b短了一点点

我们来看看请求的参数是怎么样的

哇靠

gt?

challenge?

w?

这些都是什么鬼参数

还加密了

完全下不了手啊

那么

本篇完

再见

peace

说

你是不是迷恋我??

好吧

你居然滑到这里来了

说明你还是有点爱小帅b的

小帅b是那种遇到一点困难就放弃的人吗

显然不是

那么接下来才是真的

学习 python 的正确姿势

既然以请求的方式不好弄

我们从它们的源代码入手

看看有什么突破口

回到 b 站的登录页

按下 F12

进入 Element

然后点击滑块出现了图片

定位一下

发现有两个 a 标签

一个 class 是 gt_bg gt_show

一个 class 是 gt_fullbg gt_show

和小帅b想的一样

这个验证码应该是有两张图片

一张是完全的背景图片

一张是缺口的图片

那把这两张图片下载下来对比一下不就行了

打开 a 标签一看

哇靠

一张图片被切割成很多小块

原来这张图片是拼出来的

我们看看原始图片是怎么样的

什么乱七八糟的

再仔细看下源代码

原来是在同一张图片通过偏移量合成了一张完整的图片

background-position: -277px -58px;

厉害厉害

小帅b看了一下缺口的图片也是如此

到这里

我们的第一个思路就是

下载这两张原始图片

然后通过偏移量合成两张真正的图片

背景图

↓变身

缺口图

↓变身

那么怎么做呢?

因为我们还要模拟滑动滑块

所以呢

我们要用到 selenium

打开b站的登录页

然后等到那个滑块显示出来

 # 获取滑块按钮    driver.get(url)    slider = WAIT.until(EC.element_to_be_clickable(        (By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))

接下来我们就获取页面的源码

driver.page_source

然后使用 bs 获取两张原始背景图片的 url

    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')
# 获取缺口背景图片url 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'))

拿到了图片地址之后

将图片下载下来

 # 将图片格式存为 jpg 格式    bg_url = bg_url[0].replace('webp', 'jpg')    fullbg_url = fullbg_url[0].replace('webp', 'jpg')    # print(bg_url)    # print(fullbg_url)
# 下载图片 bg_image = requests.get(bg_url).content fullbg_image = requests.get(fullbg_url).content print('完成图片下载')

ok

我们已经把两张原始图片下载下来了

那么接下来就是要合成图片了

我们要根据图片的位置来合成

也就是源码中的 background-position

获取每一个小图片的位置

我们可以通过字典的形式来表示这些位置

然后将数据放到列表中

    # 存放每个合成缺口背景图片的位置    bg_location_list = []    # 存放每个合成背景图片的位置    fullbg_location_list = []
for bg in 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(location)
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(location)

那么

现在我们已经有了原始图片

还知道了每个位置应该显示原始图片的什么部分

接下来我们就写一个方法

用来合成图片

    # 写入图片    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)

那么问题又来了

怎么合成啊

我们再看看一开始分析的图片

这里图片被分割成的每一个小图片的尺寸是

10 * 58

所以我们也要将我们刚刚下载的原始图片切割成相应的尺寸大小

而且

这张图片是由上半部分的小图片和下半部分的小图片合成的

所以我们定义两个 list 来装这些小图片

  # 存放上下部分的各个小块    upper_half_list = []    down_half_list = []

然后将原始的图片切割好放进去

image = Image.open(image_file)
# 通过 y 的位置来判断是上半部分还是下半部分,然后切割 for location in location_list: if location['y'] == -58: # 间距为10,y:58-116 im = image.crop((abs(location['x']), 58, abs(location['x'])+10, 116)) upper_half_list.append(im) if location['y'] == 0: # 间距为10,y:0-58 im = image.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)) down_half_list.append(im)

至此

我们这两个 list 就分别放好了各个切割的图片了

那么接下来就创建一张空白的图片

然后将小图片一张一张(间距为10)的粘贴到空白图片里

这样我们就可以得到一张合成好的图片了

哎

我真是个天才

 # 创建一张大小一样的图片    new_image = Image.new('RGB', (260, 116))
# 粘贴好上半部分 y坐标是从上到下(0-116) offset = 0 for im in upper_half_list: new_image.paste(im, (offset, 0)) offset += 10
# 粘贴好下半部分 offset = 0 for im in down_half_list: new_image.paste(im, (offset, 58)) offset += 10

那么到现在

我们可以得到网页上显示的那两张图片了

一张完全的图片

一张带缺口的图片

接下来我们就要通过对比这两张图

看看我们要滑动的距离是多远

    # 合成图片    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(distance))

可以通过图片的 RGB 来计算

我们设定一个阈值

如果 r、g、b 大于这个阈值

我们就返回距离

def get_distance(bg_Image, fullbg_Image):
#阈值 threshold = 200
print(bg_Image.size[0]) print(bg_Image.size[1])

for i in range(60, bg_Image.size[0]): for j in range(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])
if r + g + b > threshold: return i

现在

我们知道了关键的滑动距离了

激动人心的时刻到了

我们使用 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).drag_and_drop_by_offset(knob, distance, 0).perform()

运行一下试试看吧

哇哦你妹哦~

妖怪吃了拼图了

看来直接拖拽是不行的

容易遇到妖怪

毕竟这太快了

就算加藤鹰也没那么快吧

小帅b试着拖完滑块让它睡一下再释放

    ActionChains(driver).click_and_hold(knob).perform()    ActionChains(driver).move_by_offset(xoffset=distance, yoffset=0.1).perform()    time.sleep(0.5)    ActionChains(driver).release(knob).perform()

发现拼图还是特么的被妖怪吃了

后来小帅b发现原来别人也遇到了这样的问题

然后又发现了

有个叫匀速直线运动的东西

什么 加速度

什么 v = v0 + at

什么 s = ½at²

哇

这不是高中的知识点么

瞬间想起小帅b高中的时候在最角落的课桌

此刻往右上方抬起头

45 度角

让我的眼泪划出一条美丽的弧线

什么鬼

回到正题

我们可以使用它来构造一个运动路径

该加速时加速

该减速的时候减速

这样的话就更像人类在滑动滑块了


def get_path(distance): result = [] current = 0 mid = distance * 4 / 5 t = 0.2 v = 0 while current < (distance - 10): if current < mid: a = 2 else: a = -3 v0 = v v = v0 + a * t s = v0 * t + 0.5 * a * t * t current += s result.append(round(s)) return result

这次

我们使用这个轨迹来滑动

    knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))    result = get_path(distance)    ActionChains(driver).click_and_hold(knob).perform()
for x in result: ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5) ActionChains(driver).release(knob).perform()

好了好了

我们再来运行一下吧

哈哈哈

cool

成功识别了哇

我不管

当然了

成功率不是 100%

可以多调戏它几次

ok

以上就是识别滑动验证码的具体过程了

对于其它大部分的滑动验证码

也是可以使用这招搞定的

由于篇幅有限

源代码我放在了这个公众号后台了

你发送〔滑动〕两个字

就可以获取啦

那

这次本篇就真的完啦

听说你想约我?

peace

相关文章

(小帅b教你三招搞定模拟登录)

(小帅b教你轻松识别图片验证码)

点个在看啊~~(破音)

python爬虫21 | 对于b站这样的滑动验证码,不好意思,照样自动识别的更多相关文章

  1. Python爬虫之爬取站内所有图片

    title date tags layut Python爬虫之爬取站内所有图片 2018-10-07 Python post 目标是 http://www.5442.com/meinv/ 如需在非li ...

  2. python爬虫——爬取B站用户在线人数

    国庆期间想要统计一下bilibili网站的在线人数变化,写了一个简单的爬虫程序.主要是对https://api.bilibili.com/x/web-interface/online返回的参数进行分析 ...

  3. python爬虫25 | 爬取下来的数据怎么保存? CSV 了解一下

    大家好 我是小帅b 是一个练习时长两年半的练习生 喜欢 唱! 跳! rap! 篮球! 敲代码! 装逼! 不好意思 我又走错片场了 接下来的几篇文章 小帅b将告诉你 如何将你爬取到的数据保存下来 有文本 ...

  4. python爬虫22 | 以后我再讲python「模拟登录」我就是狗

    接下来就是 学习python的正确姿势 做爬虫 绕不开模拟登录 为此小帅b给大家支了几招 python爬虫19 | 遇到需要的登录的网站怎么办?用这3招轻松搞定! 有些网站的登录很弱鸡 传个用户名和密 ...

  5. 【Python爬虫】爬虫利器 requests 库小结

    requests库 Requests 是一个 Python 的 HTTP 客户端库. 支持许多 HTTP 特性,可以非常方便地进行网页请求.网页分析和处理网页资源,拥有许多强大的功能. 本文主要介绍 ...

  6. python爬虫抓站的一些技巧总结

    使用python爬虫抓站的一些技巧总结:进阶篇 一.gzip/deflate支持现在的网页普遍支持gzip压缩,这往往可以解决大量传输时间,以VeryCD的主页为例,未压缩版本247K,压缩了以后45 ...

  7. 转载:用python爬虫抓站的一些技巧总结

    原文链接:http://www.pythonclub.org/python-network-application/observer-spider 原文的名称虽然用了<用python爬虫抓站的一 ...

  8. 小白学 Python 爬虫(21):解析库 Beautiful Soup(上)

    小白学 Python 爬虫(21):解析库 Beautiful Soup(上) 人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前 ...

  9. 用python爬虫抓站的一些技巧总结 zz

    用python爬虫抓站的一些技巧总结 zz 学用python也有3个多月了,用得最多的还是各类爬虫脚本:写过抓代理本机验证的脚本,写过在discuz论坛中自动登录自动发贴的脚本,写过自动收邮件的脚本, ...

随机推荐

  1. pip 清华镜像

    临时使用: 可以在使用pip的时候加参数-i  https://pypi.tuna.tsinghua.edu.cn/simple 例:pip install -i https://pypi.tuna. ...

  2. 杂项:game_navigation

    ylbtech-杂项:game_navigation 1.返回顶部 1. 2. 2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   作者:ylbtech出处: ...

  3. 错误: 实例 "ruiy" 执行所请求操作失败,实例处于错误状态。: 请稍后再试 [错误: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)].

    错误: 实例 "ruiy" 执行所请求操作失败,实例处于错误状态.: 请稍后再试 [错误: 'ascii' codec can't decode byte 0xe6 in posi ...

  4. mac 修改用户权限

    想安装thinkPHP 下载完以后 访问报403错误 于是百度找 也没找到原因 自己猜测是不是用户权限问题 就是下面目录为tp的用户权限 不是root 其他是root的都能访问 于是百度搜了权限如何修 ...

  5. CodeIgnitor 配置类的使用

    CI 的配置文件统一放在 application/config/ 目录下面,框架有一个默认的主配置文件 application/config/config.php.其部分内容如下: <?php ...

  6. 离散化+BFS HDOJ 4444 Walk

    题目传送门 /* 题意:问一个点到另一个点的最少转向次数. 坐标离散化+BFS:因为数据很大,先对坐标离散化后,三维(有方向的)BFS 关键理解坐标离散化,BFS部分可参考HDOJ_1728 */ # ...

  7. nginx connect failed (110- Connection timed out) 问题排查

    首先排查 ping 下 nginx 与代理服务是否ping 的通,带端口的,telnet 下端口号是否是通的,本次遇到问题为 telnet 发现有台服务器不通,原因是端口未开放

  8. SQL 几个查看性能的语句

    1.查找目前SQL Server所执行的SQL语法,并展示资源情况: SELECT s2.dbid , DB_NAME(s2.dbid) AS [数据库名] , --s1.sql_handle , ( ...

  9. 6.12---知道参数的重要性------插入数据-删除数据-修改数据注意Map

    ---------------

  10. 3星|《未来公司》:Uber简史

    未来公司(卡兰尼克和他的Uber帝国)(精) 从Uber创始人卡兰尼克的角度讲的Uber简史,截止到2017年.书中有不少Uber的负面新闻,比国内的同类书要好. 基本是流水账.想了解这家公司的历史, ...