网络爬虫笔记—滑动验证码识别

一、什么是滑动验证码

点击之前

点击之后

像这种通过滑动图片,补全缺口的方式,就是滑动验证码。

二、识别思路

  • 1)使用selenium库操作谷歌浏览器,打开目标网站;关于selenium的教程,大家可参考这篇文章:网络爬虫笔记—Selenium
  • 2)模拟操作浏览器,对网页截图,先获取全屏截图;
  • 3)根据滑动验证码的元素,获取滑动验证码不带缺口的图片和带缺口的图片;
  • 4)通过不带缺口验证码图片和带缺口验证码图片的对比,识别滑动验证码缺口的位置;
  • 5)模拟移动滑块,完成验证;

三、具体实践

3.1、验证码获取

  • 目标网站:https://cxcx.iachina.cn/

*该网站为财产保险公司自主注册产品查询平台,该教程仅是案例教学,请大家不要使用该方法恶意爬取或访问任何网站。

  • 网站特点

该网站需要先输入用户名和密码,然后点击登录才可以弹出滑动验证码;

点击登录前

点击登录后

  • 代码实现

打开网站→输入用户名和验证码→点击登录→全屏截图

from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from PIL import Image
from selenium.webdriver import ActionChains#1、隐藏Chrome 正受到自动测试软件的控制
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
browser = webdriver.Chrome(options=chrome_options)#2、打开浏览器
myurl="https://cxcx.iachina.cn/"
browser.get(myurl)#打开浏览器
browser.maximize_window()#将浏览器全屏
time.sleep(2)#3、输入用户名和验证码
myname = browser.find_element(By.XPATH, "/html/body/form/div/div[2]/div[1]/input[1]")#运用Xpath方法,找到用户名的输入框
myname.send_keys("1111")#在用户名处,输入1111
mycode = browser.find_element(By.XPATH, '//*[@id="passWord"]')#运用Xpath方法,找到密码的输入框
mycode.send_keys("1111")#在密码处,输入1111#4、点击登录按钮
button = browser.find_element(By.XPATH, '/html/body/form/div/div[3]/input[5]')#运用Xpath方法,找到登录的按钮
button.click()#点击登录按钮
time.sleep(2)#等待2秒#5、全屏截图
screenshot_all = browser.save_screenshot("screenshot_all.png")#获取全屏截图,并保存到本地
  • 代码答疑

a)1、隐藏Chrome 正受到自动测试软件的控制

selenium操作谷歌浏览器时,会在浏览器左上角显示“Chrome 正受到自动测试软件的控制。”此处代码的目的,就是取消该显示,不取消一般也不影响系统的正常运行。

b)3、输入用户名和验证码

此处用到了Xpath方法来定位元素,关于Xpath的使用方法,大家可参考我之前写的一篇文章:网络爬虫笔记—解析库Xpath

  • 保存的全屏截图

3.2、截取未带缺口的验证码

  • 代码实现

获取验证码元素的位置信息→读取本地保存的全屏截图图片(screenshot_all.png)→截取出验证码图片

#6、获取验证码位置
code_element = browser.find_element(By.XPATH,'//*[@id="slideBar"]/div/div[1]/div[2]/div[2]/div[1]')#通过Xpath方法获取验证码元素
left = code_element.location['x']*1.5#返回一个字典格式,里面有两个参数x和y,代表元素的位置,1.5表示的电脑缩放比例
top = code_element.location['y']*1.5
right = code_element.size['width']*1.5+left#验证码元素右边的点位置
height = code_element.size['height']*1.5+top#验证码元素左上边点位置#7、读取本地验证码并截取验证码图片
im = Image.open("screenshot_all.png")#读取全屏截图
img = im.crop((left, top, right, height))#截取验证码图片
img.save("beforecode.png")#保存验证码图片
  • 代码解析

关于验证码的截取,大家可参考我之前写的一篇文章:网络爬虫笔记—图形验证码获取

  • 保存的验证码图片

3.3、截取带缺口的验证码

带缺口的验证码图片,只有点击下面的滑块后才会显示。显示出来之后,后面的方法就和“3.1、验证码获取”及“3.2、截取未带缺口的验证码”保持一致了。

  • 代码实现

获取滑块的位置信息→点击滑块→重复3.1及3.2的步骤

#8、滑块位置获取和点击滑块
button = browser.find_element(By.XPATH, '//*[@id="slideBtn"]')#运用Xpath方法,找到滑块的按钮
button.click()#点击滑块的按钮
time.sleep(3)#等待3秒
screenshot_all = browser.save_screenshot("afterscreenshot_all.png")#获取全屏截图,并保存到本地#9、重复6和7步
code_element = browser.find_element(By.XPATH,'//*[@id="slideBar"]/div/div[1]/div[2]/div[2]/div[1]')#通过Xpath方法获取验证码元素
left = code_element.location['x']*1.5#返回一个字典格式,里面有两个参数x和y,代表元素的位置,1.5表示的电脑缩放比例
top = code_element.location['y']*1.5
right = code_element.size['width']*1.5+left#验证码元素右边的点位置
height = code_element.size['height']*1.5+top#验证码元素左上边点位置
im = Image.open("afterscreenshot_all.png")#读取全屏截图
img = im.crop((left, top, right, height))#截取验证码图片
img.save("aftercode.png")#保存验证码图片
  • 保存的带缺口的验证码图片

3.4、缺口识别

beforecode.png

aftercode.png


通过对比两张图片可以看出,右边图片在缺口处有一个正方形的空白。因此,我们可以获取两张图片的像素点的RGB值,并遍历所有的像素点,如果两者像素点的RGB值相同,则代表不是缺口,如果不同则代表是缺口。

实际运行中为了避免出现误差,一般会设置一个阈值,只有两者的差异超过这个阈值,我们才认为是两个不同的点。

  • 代码实现

读取两张照片的像素点RGB值→遍历所有像素点并进行比较→输出差异区域

#10、读取两张照片
beforecode = Image.open("beforecode.png").convert("L")#读取未带缺口的验证码图片,并转化为灰度图片
aftercode = Image.open("aftercode.png").convert("L")#读取带缺口的验证码图片,并转化为灰度图片
threshold = 60#设置差异阈值为60,即两个像素点的RGB值得差,大于60,才被认为是差异点
width = beforecode.size[0]#获取图片的宽度
height = beforecode.size[1]#获取图片的高度#11、遍历像素点,输出差异区域
mylocal = []
for h in range(0,height):for w in range(int(round(width/3,0)),width):#因为缺口一般在右边,所有我们从左边的1/3处开始遍历beforepixel=beforecode.getpixel((w,h))#获取该像素点的RGB值,因为转化成灰度图片了,所有只有一个值afterpixel=aftercode.getpixel((w,h))if abs(beforepixel-afterpixel) > threshold:#遍历每一个像素点,并与阈值比较if w not in mylocal:#为了排除重复值mylocal.append(w)#将差异点的宽,保存到列表中。因为滑动验证码,只需要左右移动就可以了
  • 思路解析

a)为什么要转化为灰度图片

电脑的彩色图片是由三个值决定的即RGB值,详情可见下图。对于图片来说,图片是由一些列带颜色的点组成的,即我们常说的像素。每一个像素在电脑都会有三个值,即R值、G值、B值,这三个值共同决定了这个像素的颜色。但我们处理相片时,如果RGB三个值都遍历,则会加大计算量,因此我们可以将彩色图片变成灰度图片(黑白照片),这样图片的每个像素点将不是由RGB三个值决定的,而是仅有一个值,这个值决定了这个像素点的黑白程度。如果值越接近255则越白,越接近0则越黑。所以变成灰度图片的目的主要是方便计算。

b)为什么从照片的1/3处开始识别不同

大多数验证码图片都是从左向右滑动,也就是说缺口会在照片右边,所以从照片1/3处开始向右遍历每个像素点。一是为了减少计算量;二是为了避免识别出左侧的不同的位置。这个位置设置,可以根据滑动验证码的实际情况,进行设置,不一定必须设置为1/3。

c)为什么缺口位置仅保留了宽的位置

这是因为,滑动验证码,大多都是向右移动的,不会上下移动,所以只需要保留宽度,记录向右移动的位置即可。

3.5、移动轨迹计算

现在滑动验证码都加了反爬虫识别,如果我们均速移动、匀加速移动或匀减速运动都会被系统识别为非人为操作,进而无法通过验证。

实际我们点击的时候,前面会移动的快,后面会移动的慢,因此我们可以先匀加速运动后匀减速运动,这样就可以模拟人的操作了。这里需要用到物理的匀加速公式,这里直接将其写出来,感兴趣的可以再去回味一下高中物理。
{x=v0t+12a∗t2v=v0+at\left\{ \begin{array}{c} x = v_0 t + \frac{1}{2}a*t^2\\ v = v_0 + at \end{array} \right. {x=v0​t+21​a∗t2v=v0​+at​

v0v_0v0​:初速度,ttt:运动时间,aaa:加速度,xxx:移动流程。

  • 代码实现
#12、移动轨迹计算
distance = mylocal[0]/1.5#滑块移动的距离,就是上面识别到的第一个差异点的w值
track = []# 移动轨迹,里面保留的是每次移动的距离
current = 0# 当前位移
mid = distance * 4 / 5# 减速阈值,前面4/5做匀加速移动,后面1/5做匀加速移动
t = 0.2# 计算间隔,每次移动两秒
v = 0# 初速度
while current < distance:#当移动的距离超过,目标距离后,就停止循环if current < mid:a = 2#前面匀加速的加速度为正2else:a = -3#后面匀减速的加速度为负3v0 = v# 初速度 v0v = v0 + a * t #当前速度 v = v0 + atmove = v0 * t + 1 / 2 * a * t * t # 移动距离 x = v0t + 1/2 * a * t^2current = current + move#总位移track.append(round(move))#每次移动的距离
track[-1]=distance-sum(track[0:-1])#上面计算的总距离,会超过目标距离,所以最后一个移动距离需要修正一下

  • 代码解析

a)distance = mylocal[0]/1.5

为什么距离这里,要除以1.5?由于我的电脑是1.5倍显示的,但系统移动距离却是按照100%显示移动的,所以要恢复至原来的100%显示的距离。

b)track[-1]=distance-sum(track[0:-1])

为什么滑块最后一个移动距离要修正?按照上面的逻辑计算的移动距离会大于实际移动的距离,因此对于最后一步的移动距离需要用总距离修正一下,即总距离减去前面的移动距离和,就是最后一步需要移动的距离。

3.6、移动滑块

缺口位置和移动轨迹全部计算好之后,最后一步就是运用动作链来操作滑块移动,完成图片验证的操作。

  • 代码实现

找到滑块的位置→运用动作链移动滑块

#13、移动滑块
actions = ActionChains(browser)#创建动作链
slider = browser.find_element(By.XPATH,'//*[@id="slideBtn"]')#运用Xpath方法获取滑块元素
actions.click_and_hold(slider).perform()#点击滑块,并保持点击动作
for x in track:actions.move_by_offset(xoffset=x, yoffset=0).perform()#拖动滑块
time.sleep(1)
actions.release().perform()#松开滑块,验证完成。
  • 代码解析

a)actions.click_and_hold(slider).perform()

slider为滑块元素,此代码可以保持点击滑块不松开的动作。

b)actions.move_by_offset(xoffset=x, yoffset=0).perform()

此代码表示持续的移动滑块,xoffset=x表示向左或向右移动的距离,yoffset=0表示向上或向下移动的距离。

c)actions.release().perform()

此代码表示松开滑块,取消点击动作。

  • 本篇笔记首先在微信公众号“宏蜘蛛”上发布,链接为:网络爬虫笔记—滑动验证码识别

—End—

参考资料

1、《Python3网络爬虫开发实战》_崔庆才

2、selenium自动化测试时,chrome 出现“Chrome 正受到自动测试软件的控制”的解决办法…_51CTO博客_selenium被检测的解决办法

3、Python 关于浮点数取整详解_XianZhe_的博客-CSDN博客_python浮点数转换整数

4、python PIL库中的getpixel函数_cuicui_ruirui的博客-CSDN博客_im.getpixel

5、6种在 Python 中从 List 中删除重复项的方法_普通网友的博客-CSDN博客

6、python列表求和 (baidu.com)

网络爬虫笔记—滑动验证码识别相关推荐

  1. 网络爬虫笔记—图形验证码识别

    网络爬虫笔记-图形验证码识别 <兄弟们,本文章开启了关注后阅读.大家如不想关注,可直接微信搜索"宏蜘蛛"或文章标题,查看文章.> 1.什么是图形验证码 像知网注册界面的 ...

  2. 网络爬虫笔记—图形验证码获取

    网络爬虫笔记-图形验证码获取 1.验证码获取思路 1)使用selenium库操作谷歌浏览器,打开目标网站: 2)对目标网站进行截图,并将图片保存到本地: 3)获取验证码元素节点在屏幕上的位置,即横纵坐 ...

  3. 网络爬虫中的验证码识别

    网络爬虫遇到的验证码 在写网络,爬虫时,遇到很多网站存在验证码的情形,有其是比较烦的是,爬取数据的每一页都有验证码,如果只有登陆时,存在验证码,这个很好解决,只需将验证码获取后手动输入就行. 但对于每 ...

  4. 爬虫—GEETEST滑动验证码识别

    一.准备工作 本次使用Selenium,浏览器为Chrome,并配置好ChromDriver 二.分析 1.模拟点击验证按钮:可以直接使用Selenium完成.    2.识别滑块的缺口位置:先观察图 ...

  5. 网络爬虫笔记—Selenium

    网络爬虫笔记-Selenium 1.简介及环境安装 Selenium是一种自动化测试工具,利用它可以操作浏览器执行固定动作,例如点击.下拉等操作.在日常工作中,如果你需要用浏览器并且重复某项操作,那S ...

  6. 用Python爬虫破解滑动验证码

    我们可以借用opencv来解决这个问题,主要步骤: opencv 是什么? OpenCV(Open Source Computer Vision Library)是开放源代码计算机视觉库,主要算法涉及 ...

  7. Python 网络爬虫笔记11 -- Scrapy 实战

    Python 网络爬虫笔记11 – Scrapy 实战 Python 网络爬虫系列笔记是笔者在学习嵩天老师的<Python网络爬虫与信息提取>课程及笔者实践网络爬虫的笔记. 课程链接:Py ...

  8. Python 网络爬虫笔记10 -- Scrapy 使用入门

    Python 网络爬虫笔记10 – Scrapy 使用入门 Python 网络爬虫系列笔记是笔者在学习嵩天老师的<Python网络爬虫与信息提取>课程及笔者实践网络爬虫的笔记. 课程链接: ...

  9. Python 网络爬虫笔记9 -- Scrapy爬虫框架

    Python 网络爬虫笔记9 – Scrapy爬虫框架 Python 网络爬虫系列笔记是笔者在学习嵩天老师的<Python网络爬虫与信息提取>课程及笔者实践网络爬虫的笔记. 课程链接:Py ...

最新文章

  1. NumPy中可用的聚合函数
  2. 架构设计器_MySQL:数据库结构优化、高可用架构设计、数据库索引优化
  3. 怎样维护成功的开源项目
  4. linux 基础训练,Linux 基础训练习题
  5. t4 tornado 模板
  6. linux 建立ftp用户
  7. MkDocs安装、生成文档、风格配置、插件安装
  8. flutter配置高德地图定位
  9. iOS 开发者常用的75 个工具
  10. 配置扩展IP ACL实验
  11. php 判断字数,php 如何统计中文字数
  12. 设计模式之路 | 建造者模式
  13. RK3588平台开发系列讲解(文件系统篇)Linux 文件系统简介
  14. Matlab人形机器人建模与仿真
  15. 绕圈圈面试题(Python经典编程案例)
  16. 100ms的延迟让亚马逊损失1%销量,如何快速降低网站延迟?
  17. 《阿里巴巴java规范》 Result 方式杂谈
  18. Oracle rpad 转 sqlserver的 left 函数
  19. LeetCode - 929 - 独特的电子邮件地址(unique-email-addresses)
  20. 那些著名网站的90年代

热门文章

  1. 保险行业如何实现私域快速增长
  2. php form表单提交方式,form表单提交数据的几种方式
  3. 2D游戏vs3D游戏
  4. 图片使用css3滤镜改变图片颜色
  5. 金行健:Facebook的“元宇宙”不过是一部宣传片?
  6. 数据透视创建传统布局_更改数据透视图布局
  7. slave同步master数据偏移量错误
  8. linux之创建文件命令
  9. 使用Labelme和PaddleSeg实现动漫人物实例分割
  10. 产品经理看哪吒之魔童降世