利用python实现自动通过滑动验证码

1.自动填写用户名和密码

1.工具:selenium 库

在搜索引擎的帮助下,我发现selenium 库可以编写自动填写账号密码的爬虫。

阅读selenium 库的doc,很快就能理解使用selenium 进行自动化网页操作的基本需求:

image-20200929193000968.png

2.伟大的第一步,打开浏览器!

选取WebDriver后,就从启动浏览器开始,一开始我使用chromedriver访问网页,指定chromedriver的目录:

chromedriver的配置其实是相对麻烦的,首先需要找到对应版本的chromedriver.exe,还需要放置到chrome程序目录下以及python\scripts目录下以确保可以被正常使用,实际上在csdn上还发现有需要单独将chromedriver.exe添加到path的情况。

接下来尝试打开一些网站:

测试后正常,然而在指定访问信息门户登录界面时,发现了异常情况:

image-20200929194419338.png

似乎所有的图片都没有正常加载,尝试刷新后发现页面变得完全空白。起初我认为是chromedriver与chrome版本不匹配,但是再三确认后发现其他网站的访问都是正常的,于是想到信息门户可能启用了反爬虫。

由于我还没有使用selenium 在页面元素上做出其他任何操作,我在csdn上一番搜寻后看到了一种可能:

image-20200929195115271.png

推测是Chromedriver的特征码被信息门户识别然后拒绝了我的访问。

于是抱着试一试的心态,尝试换用firefox

测试发现能够成功访问,开始思考接下来的问题。

3.获取用户名框和密码框的元素

为了获取到信息门户登录的用户名和密码的元素,我使用了函数find_element_by_xpath

使用浏览器的调试工具,很容易可以获取到用户名和密码的Xpath:

image-20200929200602676.png

//[@id="username"]

//[@id="password"]

然后用同样的方法获取到登录按钮的xpath,使用click()点击完成登录:

流程就完成了,不出意料,接下来弹出了滑动验证码。

4.关键部分:通过滑动验证码

1.思路分析

  1. 检查网页元素,确定能够获取到的资源

    在一番查找后发现,信息门户的验证码图片的分两张存储,id分别为:img1,img2(我也没想到就是这么朴素的两个名字)为缺口图形和背景图形(已挖去缺口),

    示例如图:

  2. 依据可获取的资源思考解决方案

    与早期的三张图片的形式不同,现在的验证码只给出了两张图片,没有原图,这加大了匹配的难度。在一番查找后,发现了opencv库有matchTemplate函数可以做粗略的模板匹配:

    百度了一番之后,大致了解了该函数的工作方式,也看到了应用的实例,最终确定尝试使用opencv来完成匹配的重任

2.尝试实现的过程和遭遇的困难

1.匹配原图位置并定位到网页上图片的位置

完成登录操作后,首先获取两张图片到本地,定义一个get_pics函数,传入参数为当前使用的浏览器driver:

此时两个png已经保存到了程序目录下,然后使用opencv尝试匹配并获取xy轴距离:

(这里x,y与背景的xy恰好互异,实际上用y作为我们直观看到的x坐标)

匹配的结果并不很理想,我选择等待后续优化,先尝试实现正确地拖动滑块

测试发现,信息门户使用的图片大小原图为背景590x360和滑块93x361

然而图像在网页上的比例发生了变化,背景变化为280*155

所以我们需要把获得的x坐标再做一次处理得到*280/590得到对应的坐标位置,最后再补上滑块的一半宽度20来补偿初始位置:

经过验证,这样的数据处理使得在网页定位的结果和opencv匹配的位置基本相同

2.模拟滑动操作

这里的操作我选择了ActionChains函数来模拟鼠标操作,首先用Xpath定位滑块

然后调用函数ActionChains模拟鼠标按住

设置移动路径,一开始使用匀速运动,后来发现有些网站存在匀速检测的功能,所以改成了先变速拖动,超出范围再往回拖动来模拟人手的行为:

for循环中调用ActionChains实现拖动(事实上并不连续):

此时在测试样例里已经可以正确的滑动滑块了,模拟滑动功能实现且不会被识别为机器

将代码组合后,已经可以完成基本的登录、识别、滑动的操作

代码存档:

3.尝试优化匹配方法,提高匹配率

opencv自带的模板匹配在图片滑块区域颜色较深或明暗对比不强烈的情况下识别率较低,为优化识别率,提高识别效率,优化使用以下算法:

{%note info icon%}

匹 配 思 路

{%endnote%}

考虑到滑块本身有明显的线段交点的固有属性,寻找直线交点或许可以做到较高的识别率,代码实现如下:

最终得到结果的识别率明显提高,能满足通过验证码的需求。

2020-10-13 20:25更新,压缩了代码量,代码更加简洁,从180+行压缩到113行,增强了可读性

{%note primary icon%}5.另一种思路{%endnote%}

 

还有一种更简洁的算法,来自亲爱的@Closer

去除注释和空行可以做到代码只有70行左右,更加简洁,唯一的缺点是对于缺口旋转的验证码需要判断直线位置,直线颜色也需要接近白色,但大多数情况下可以做到精准的识别。