大家好,我是不温卜火,是一名计算机学院大数据专业大三的学生,昵称来源于成语—
不温不火,本意是希望自己性情温和。作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己所犯的错误希望能够帮助到很多和自己一样处于起步阶段的萌新。但由于水平有限,博客中难免会有一些错误出现,有纰漏之处恳请各位大佬不吝赐教!暂时只在csdn这一个平台进行更新,博客主页:https://buwenbuhuo.blog.csdn.net/。
PS:由于现在越来越多的人未经本人同意直接爬取博主本人文章,博主在此特别声明:未经本人允许,禁止转载!!!
目录

推荐

♥各位如果想要交流的话,可以加下QQ交流群:974178910,里面有各种你想要的学习资料。♥
♥欢迎大家关注公众号【不温卜火】,关注公众号即可以提前阅读又可以获取各种干货哦,同时公众号每满1024及1024倍数则会抽奖赠送机械键盘一份+IT书籍1份哟~♥

上两篇文章中学长分别破解了文字验证码和简单的滑动验证码,接下来咱们升级破解,破解难度更大的滑动验证码,目前很多企业的用的第三方开发好的验证码,比如极验。

在此,博主给出极验的滑动验证网站进行测试。网址如下:
https://captcha1.scrape.center/

我们打开这个网址点击登录,可以发现验证码要比豆瓣的验证码复杂多了。

一、验证码分析

滑动验证码可以通过分析js,这个难度过大。我们这里还是使用selenium模拟滑动解决。
需要解决两个问题:
第1是滑动的距离。
第2是模拟滑动。
模拟滑动已经可以解决,现在就需要解决滑动距离。
首先,我们先来获取完整图片,先找到验证码的位置,然后取消选取display和opacity即可得到完整的图片

或者这none改成block也可以显示完整图片

下面我们来对不一下这两张图片


其实除了这种方案之外,往上是有其他方案的,比如通过opencv找到缺口位置。

🆗既然这样,那么接下来思路就很清晰了
- (1)访问网页,保存全屏为图片。
- (2)截图验证码
- (3)通过js修改css样式,显示完整图
- (4)截图验证码
- (5)对比找到缺口位置来判断出位移
- (6)移动
注意:截图验证码的时候需要获取位置,这个时候需要本地屏幕设置100%和浏览器设置100%不缩放。


二、代码的逐步实现
实现的集体过程
- (1)访问网页,保存全屏为图片。
- (2)截图验证码
- (3)通过js修改css样式,显示完整图
- (4)截图验证码
- (5)对比找到缺口位置来判断出位移
- (6)移动
2.1 访问网页,使其能够出现验证码

- 1. 代码如下
1from selenium import webdriver 2from PIL import Image 3import time 4 5url = "https://captcha1.scrape.center/" 6driver = webdriver.Chrome("./chromedriver/chromedriver.exe") 7driver.maximize_window() 8driver.get(url) 9 10driver.find_elements_by_class_name("el-input__inner")[0].send_keys("老王") 11driver.find_elements_by_class_name("el-input__inner")[1].send_keys("123456") 12driver.find_element_by_class_name("el-button").click() 13 14time.sleep(5) 15driver.quit()
- 2. 效果如下

2.2 截取验证码
1. 分析

- 1. 确定图片位置

- 2. 截取验证码
我们的截图,其实就是确定左上和右下的位置
2. 代码实现
- 1. 先截取整个屏幕
1#保存屏幕 2driver.save_screenshot("./screen1.png") 3time.sleep(2)
- 2. 截取验证码
1#获取验证码的定位 2img = driver.find_element_by_class_name("geetest_canvas_img") 3#print(img1.location,img1.size)#{'x': 590, 'y': 239} {'height': 160, 'width': 260} 4#坐标(左上角,右下角) 5rectangle = (img.location["x"],img.location["y"],img.location["x"]+img.size["width"],img.location["y"]+img.size["height"]) 6print(rectangle) 7#打开图片 8img_obj = Image.open("./screen1.png") 9#截图 10img_new = img_obj.crop(rectangle) 11#保存 12img_new.save("./img1.png") 13 14driver.quit()
3. 最终结果(截图)

2.3 截取完整图片
1. 执行js,显示完整图片
代码如下
document.getElementsByClassName("geetest_canvas_fullbg")[0].style = "display:block"
效果图如下

2. 代码实现
1#执行js,显示完整图片 2driver.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].style = "display:block"') 3time.sleep(2) 4 5#保存屏幕 6driver.save_screenshot("./screen2.png") 7time.sleep(2) 8 9#截图 10img_obj = Image.open("./screen2.png") 11img_new = img_obj.crop(rectangle) 12img_new.save("./img2.png")

2.4 验证码缺口和完整截图
此部分,为上述源码的整合
1from selenium import webdriver 2from PIL import Image 3import time 4 5url = "https://captcha1.scrape.center/" 6driver = webdriver.Chrome("./chromedriver/chromedriver.exe") 7driver.maximize_window() 8driver.get(url) 9 10driver.find_elements_by_class_name("el-input__inner")[0].send_keys("老王") 11driver.find_elements_by_class_name("el-input__inner")[1].send_keys("123456") 12driver.find_element_by_class_name("el-button").click() 13 14time.sleep(5) 15 16#保存屏幕 17driver.save_screenshot("./screen1.png") 18 19#获取验证码的定位 20img = driver.find_element_by_class_name("geetest_canvas_img") 21#print(img1.location,img1.size)#{'x': 590, 'y': 239} {'height': 160, 'width': 260} 22#坐标(左上角,右下角) 23rectangle = (img.location["x"],img.location["y"],img.location["x"]+img.size["width"],img.location["y"]+img.size["height"]) 24print(rectangle) 25#打开图片 26img_obj = Image.open("./screen1.png") 27#截图 28img_new = img_obj.crop(rectangle) 29#保存 30img_new.save("./img1.png") 31 32#执行js,显示完整图片 33driver.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].style = "display:block"') 34time.sleep(2) 35 36#保存屏幕 37driver.save_screenshot("./screen2.png") 38time.sleep(2) 39 40#截图 41img_obj = Image.open("./screen2.png") 42img_new = img_obj.crop(rectangle) 43img_new.save("./img2.png") 44 45driver.quit()
2.5 对比缺口验证

接下来,对比两张图的不同,从而找到缺口的位置,就是我们要移动的位置。
这里发现滑块的定位是固定的,距左面大概55左右。这两个图在截图的时候应该分别向右移动55像素,比对找到缺口后再加上原来的55就是要移动的位移。
分析完毕,下面我们只需修改下列一行代码
1#坐标(左上角,右下角) 2rectangle = (img.location["x"]+55,img.location["y"],img.location["x"]+img.size["width"],img.location["y"]+img.size["height"])

接着要对比像素,找到二者不同的像素,这里需要注意,极验图上还加入了阴影,增加了对比难度。

经过多张图的对比,发现差值临界点30左右可以既去掉灰色阴影的干扰块又能识别出缺口位置
通过代码,对比像素的不同
1#对比像素的不同 2 3from PIL import Image 4img1 = Image.open("./img1.png") 5img2 = Image.open("./img2.png") 6 7for i in range(img1.width): 8 for j in range(img1.height): 9 if img1.getpixel((i,j))!=img2.getpixel((i,j)): 10 print(i,j,img1.getpixel((i,j)),img2.getpixel((i,j)))

以上面的图为例,x就是31,加上原来的55,就是86,所以确定了滑块的位移就是86左右。
2.6 滑动滑块(此部分以后有时间会修复,在此只给出代码)

此部分直接使用上一篇博文的匀加速匀减速代码
1def get_tracks(distance, rate=0.5, t=0.2, v=0): 2 """ 3 将distance分割成小段的距离 4 :param distance: 总距离 5 :param rate: 加速减速的临界比例 6 :param a1: 加速度 7 :param a2: 减速度 8 :param t: 单位时间 9 :param t: 初始速度 10 :return: 小段的距离集合 11 """ 12 tracks = [] 13 # 加速减速的临界值 14 mid = rate * distance 15 # 当前位移 16 s = 0 17 # 循环 18 while s < distance: 19 # 初始速度 20 v0 = v 21 if s < mid: 22 a = 3 23 else: 24 a = -2 25 # 计算当前t时间段走的距离 26 s0 = v0 * t + 0.5 * a * t * t 27 # 计算当前速度 28 v = v0 + a * t 29 # 四舍五入距离,因为像素没有小数 30 tracks.append(round(s0)) 31 # 计算当前距离 32 s += s0 33 34 return tracks
接下来就完成滑动,发现如下图所示,每次滑动都会超过大概5个像素左右,圆形的滑块与上面方形的滑块有像素偏差,需要在滑动距离上减去这个5个像素就可以解决。


三、完整代码

1# encoding: utf-8 2''' 3 @author 李华鑫 4 @create 2020-10-11 16:03 5 Mycsdn:https://buwenbuhuo.blog.csdn.net/ 6 @contact: 459804692@qq.com 7 @software: Pycharm 8 @file: 验证码升级2.py 9 @Version:1.0 10 11''' 12""" 13大概的思路就出来了: 14(1)访问网页,保存全屏为图片。 15(2)截图验证码 16(3)通过js修改css样式,显示完整图 17(4)截图验证码 18(5)对比找到缺口位置来判断出位移 19(6)移动 20""" 21 22from selenium import webdriver 23from selenium.webdriver.common.action_chains import ActionChains 24from PIL import Image 25import time 26 27#网址 28url = "https://captcha1.scrape.center/" 29#截屏的图片路径 30screen1 = "./screen1.png" 31screen2 = "./screen2.png" 32#滑动验证的图片路径 33img1 = "./img1.png" 34img2 = "./img2.png" 35#截图的偏移量 36distance_offset = 55 37distance_offset2 = 5 38#位移 39distance = 0 40 41def get_tracks(distance, rate=0.5, t=0.2, v=0): 42 """ 43 将distance分割成小段的距离 44 :param distance: 总距离 45 :param rate: 加速减速的临界比例 46 :param a1: 加速度 47 :param a2: 减速度 48 :param t: 单位时间 49 :param t: 初始速度 50 :return: 小段的距离集合 51 """ 52 tracks = [] 53 # 加速减速的临界值 54 mid = rate * distance 55 # 当前位移 56 s = 0 57 # 循环 58 while s < distance: 59 # 初始速度 60 v0 = v 61 if s < mid: 62 a = 3 63 else: 64 a = -2 65 # 计算当前t时间段走的距离 66 s0 = v0 * t + 0.5 * a * t * t 67 # 计算当前速度 68 v = v0 + a * t 69 # 四舍五入距离,因为像素没有小数 70 tracks.append(round(s0)) 71 # 计算当前距离 72 s += s0 73 74 return tracks 75 76def save_img(driver): 77 """保存图片""" 78 79 time.sleep(2) 80 #保存屏幕 81 driver.save_screenshot(screen1) 82 #获取验证码的定位 83 img = driver.find_element_by_class_name("geetest_canvas_img") 84 #截图的区域 85 rectangle = (img.location["x"]+distance_offset,img.location["y"],img.location["x"]+img.size["width"],img.location["y"]+img.size["height"]) 86 #打开图片 87 img_obj = Image.open(screen1) 88 #截图 89 img_new = img_obj.crop(rectangle) 90 #保存 91 img_new.save(img1) 92 93 #执行js,显示完整图片 94 driver.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].style = "display:block"') 95 time.sleep(2) 96 #保存屏幕 97 driver.save_screenshot(screen2) 98 #执行js,隐藏完整图片 99 driver.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].style = "display:none"') 100 time.sleep(2) 101 102 #截图 103 img_obj = Image.open(screen2) 104 img_new = img_obj.crop(rectangle) 105 img_new.save(img2) 106 107def discern_distance(): 108 """识别距离""" 109 global distance 110 111 #打开图像,得到图像对象 112 img1_obj = Image.open(img1) 113 img2_obj = Image.open(img2) 114 #循环获取像素点 115 for i in range(img1_obj.width): 116 for j in range(img1_obj.height): 117 #获取像素点 118 p1 = img1_obj.getpixel((i, j)) 119 p2= img2_obj.getpixel((i, j)) 120 #比较 121 if abs(p1[0]-p2[0])>30 and abs(p1[1]-p2[1])>30 and abs(p1[2]-p2[2])>30: 122 #计算位移 123 distance = distance_offset+i-distance_offset2 124 return 125 126def slide(driver): 127 """滑动""" 128 # 找到滑块 129 block = driver.find_element_by_class_name('geetest_slider_button') 130 # 循环 131 while True: 132 print("distance:",distance) 133 # 摁下滑块 134 ActionChains(driver).click_and_hold(block).perform() 135 # 移动 136 ActionChains(driver).move_by_offset(distance*0.8, 0).perform() 137 # 获取位移 138 tracks = get_tracks(distance*0.2) 139 # 循环 140 for track in tracks: 141 # 移动 142 ActionChains(driver).move_by_offset(track, 0).perform() 143 # 释放 144 ActionChains(driver).release().perform() 145 # 停一下 146 time.sleep(5) 147 # 判断 148 if driver.current_url == url: 149 print("失败...再来一次...") 150 # 单击刷新按钮刷新 151 driver.execute_script('document.getElementsByClassName("geetest_refresh_1")[0].click()') 152 # 停一下 153 time.sleep(2) 154 #截图 155 save_img(driver) 156 #识别 157 discern_distance() 158 else: 159 print("成功") 160 break 161 162def main(): 163 """主程序""" 164 165 #开始访问网址 166 driver = webdriver.Chrome("./chromedriver/chromedriver.exe") 167 driver.maximize_window() 168 driver.get(url) 169 170 #找到用户名、密码、单击登录 弹出极验 171 driver.find_elements_by_class_name("el-input__inner")[0].send_keys("admin") 172 driver.find_elements_by_class_name("el-input__inner")[1].send_keys("admin") 173 driver.find_element_by_class_name("el-button").click() 174 175 time.sleep(5) 176 177 #保存图片 178 save_img(driver) 179 #识别位移 180 discern_distance() 181 #滑动 182 slide(driver) 183 #退出 184 driver.quit() 185 186if __name__ == '__main__': 187 main()
四、最终运行结果


美好的日子总是短暂的,虽然还想继续与大家畅谈,但是本篇博文到此已经结束了,如果还嫌不够过瘾,不用担心,我们下篇见!

好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请“点赞” “评论”“收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注我哦!


本文转自 https://buwenbuhuo.blog.csdn.net/article/details/109182895,如有侵权,请联系删除。
