python爬虫21 | 对于b站这样的滑动验证码,很差意思,照样自动识别

今天javascript

 

要来讲说滑动验证码了php

 

你们应该都很熟悉css

 

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

 

 

如今愈来愈多的网站使用这样的验证方式java

 

为的是增长验证码识别的难度python

 

 

那么ios

 

对于这种验证码web

 

应该怎么破呢apache

 

接下来就是ruby

 

学习 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(knobdistance, 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教你轻松识别图片验证码)

 

 

 

      点个在看啊~~(破音)

相关文章
相关标签/搜索