赞
踩
感谢崔大佬的《python3网络爬虫开发实战》这本书让我开了眼界。最近学习到验证码识别这一块,发现随着网站防爬手段的不断更新,要填的坑真是越来越多,这篇文章就分享一下近段时间的填坑之旅。
首先来说一下书上的缺口识别函数,大概原理是通过两张图片的像素对比,识别像素不同的点确定缺口位置。然而这种识别方法的前提条件是必须要有两张图,一张图是不含缺口的图片,另一张则是带缺口的图片。
但由于时间原因,我接触书上的代码时,极验证网站的滑动识别机制已经更新。对的,网站上截不到第一张不带缺口的图片了,所以在我手动copy崔大佬的代码运行时结果必然是失败的。
所以我自己写了两种解决滑动验证码的方法与大家分享,希望能擦出思想的火花。
第一种方法大概不算识别方法吧,因为里面有些投机取巧的地方。
投机就投机在我偶然发现了绕过验证码可以直接登录的地方。
偶然间发现极验证那个智能验证按钮,按钮左边的小圆圈里的指针,会随着鼠标的坐标而转动,然后我就发现这个小圈圈其实就是所谓智能验证的核心。也就是只要在这个按钮加载出来后,有鼠标在周围移动时,就会被视为是人为操作;而如果是按钮加载出来后代码直接点击就会被视为爬虫,然后就会有滑动验证码来作为反爬虫的一道大坎。
所以要解决这个问题其实很简单,我能想到的就是用代码在按钮附近移动鼠标,巧妙地绕过验证。
所以我导入了py的pyautogui 模块,在确认了按钮坐标后根据坐标位置让鼠标在其周围稍微移动。
核心代码是:pyautogui.moveTo(x,y,duration=0.1)
pyautogui.moveTo(x+20,y+20,duration=5)
其中x,y为按钮的坐标,duration为移动延时。延时尽量调整在5秒左右,因为移动速度过快那个小圈圈不买账。
上面代码执行后再点击按钮就会出现如下情况(嘿嘿):
第一种解决方法真的是绝妙,绕过了复杂的缺口识别,轻而易举就完事了。
但,这似乎有点胜之不武,是男人就应该正面解决问题(钢),在别人背后偷偷插一刀不是男人的作风。所以,重点来了,第二种方法就是正面解决问题的正路!
先讲讲思路吧。
在经过一番观察后,我发现我想要找到那个缺口的位置似乎是黑乎乎的,这个黑,就成为了解决问题的切入点。
是的,基本思路就是通过像素的rgb值找出那块黑乎乎的目标位置的x坐标。
然后我展开了我的一系列仔细的观察,还能发现,那个滑块的周围的颜色似乎是固定的,
固定的橙色系(瞎说)。
下面是研究对象:
然后我就开始寻找这个橙色系的rgb值的浮动范围。
下面是经过一系列计算后的结果:
上面的计算包括rgb三个值的浮动范围,三个值两两相减的绝对值的浮动范围以及rgb值的比率。
后来我就把这个橙色系的rgb范围大概计算出来了:
r区间:[159,247]
g区间:[154,249]
b区间:[102,231]
abs(r-g)区间:[0,10]
abs(g-b)区间:[18,117]
计算三个值的比例我是通过rgb各值除以r值,然后取的g值和b值之和,浮动区间为:
[1.4,1.9]
有了这些数据之后就很容易能识别出滑块周围的那一圈橙色系(似乎不是很专业,嘿嘿)。
接下来就是发现滑块的位置的y坐标的那条直线必定经过缺口。
能够定位滑块边缘坐标之后,为了减少其他因素的影响,通过减少识别范围提高识别率,对上面的图进行范围缩小处理(截图)。
接下来要处理的就是如何识别出上面红框框里面的缺口的x坐标。
黑是识别的关键,也就是上面红线标注旁边的那条竖线像素rgb三值之和是图像中以x坐标为直线所有像素点rgb三个值和的最小值。
解决方法来了,也就是以x坐标划竖线,遍历计算出rgb三值之和最小的那条,然后确定x坐标,x坐标即为缺口位置。
如下为截取的图片:
在ps上放大分析:
大功告成!!!
- from io import BytesIO
- from PIL import Image
- from selenium import webdriver
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
- import time
- url = 'https://account.geetest.com/login'
- EMAIL = '*********'
- PASSWORD = '*********'
- def open_firefox(url,email,password):
- '''
- :param url: 极验证登录页面地址
- :param email: 登录账号
- :param password: 密码
- :return:
- '''
- browers=webdriver.Firefox()
- browers.get(url)
- wait=WebDriverWait(browers,10)
- email_block=wait.until(EC.presence_of_element_located((By.ID, 'email')))
- password_block=wait.until((EC.presence_of_element_located((By.ID, 'password'))))
- email_block.send_keys(email)
- password_block.send_keys(password)
- button =wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
- button.click()
- return browers,wait
- def identify_gap(browers,wait):
- '''
- :param browers: 浏览器对象
- :param wait: wait对象
- :return: 缺口位置x坐标
- '''
- #定位验证码图片
- small_img=wait.until(EC.presence_of_element_located((By.XPATH, '//canvas[@class="geetest_canvas_bg geetest_absolute"]')))
- location=small_img.location #获取图片位置,及大小
- size=small_img.size
- top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
- 'width'] #确定截图位置
- time.sleep(5)
- screenshot=browers.get_screenshot_as_png()
- screenshot = Image.open(BytesIO(screenshot))
-
- captcha = screenshot.crop((left, top, right, bottom))
- captcha.save(r'first.png') #保存图片
- first=Image.open(r'first.png')
- xsize,ysize=first.size
- pool=[] #保存符合条件的像素点的坐标信息的数据池
- pix=first.load()
- for i in range(xsize): #颜色识别区域
- for j in range(ysize):
- if 159<=(pix[i,j])[0]<=247 and 154<=(pix[i,j])[1] <=249 and 102 <=(pix[i,j])[2]<=231:
- if 0<=abs((pix[i,j])[0]-(pix[i,j])[1])<=10:
- if 18<=abs((pix[i,j])[1]-(pix[i,j])[2])<=117 :
- if 1.4<=(pix[i,j])[1]/(pix[i,j])[0]+(pix[i,j])[2]/(pix[i,j])[0]<=1.97:
- pool.append((i,j))
- #print(pool)
- x,y=(pool[0])[0],(pool[0])[1] #获取第一个符合条件的像素点的位置
- captcha1=screenshot.crop((left+x,top+y,right,top+y+5)) #进行截图
- captcha1.save(r'second.png')
- Pool=[] # 第二张截图根据x坐标的每一条竖线的rgb值和的数据池
- second=Image.open(r'second.png')
- Xsize,Ysize=second.size
- pix1=second.load()
- for i in range(Xsize):
- sum=0
- for j in range(Ysize):
- sum+=(pix1[i,j])[0]+(pix1[i,j])[1]+(pix1[i,j])[1]
- Pool.append((i,sum))
- Pool=sorted(Pool,key=lambda x:x[1]) #排序,找出rgb值得和的最低竖线的x坐标
- #print(Pool)
- return (Pool[0])[0]+x #返回偏移值
-
-
-
- if __name__=='__main__':
- browers,wait=open_firefox(url=url,email=EMAIL,password=PASSWORD)
-
- print(identify_gap(browers,wait))
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。