Class 18 - 1 图形验证码的识别
一、图形验证码的识别
- 先将验证码的图片保存到本。
- 打开开发者工具,找到验证码元素。验证码元素是一张图片,src 属性是 CheckCode.aspx。打开链接 http://my.cnki.net/elibregister/CheckCode.aspx,保存并命名为 code.jpg。
- 识别测试
- 新建一个项目,将验证码图片放到项目根目录下,用 tesserocr 库识别验证码,示例:
import tesserocr from PIL import Imageimage = Image.open('code.jpg') result = tesserocr.image_to_text(image) print(result)
这里新建了一个 Image 对象,调用 tesserocr 的 image_to_ text()方法。传入该 Image 对象 即可完成识别。
tesserocr 还有一个更加简单的方法,这个方法可直接将图片文件转为字符串,代码:
import tesserocr print(tesserocr.file_to_text('image.png'))
此方法的识别效果不如上一种方法好。
- 新建一个项目,将验证码图片放到项目根目录下,用 tesserocr 库识别验证码,示例:
验证码处理
换一个验证码带有多线条,命名为 code2.jpg。重新识别和实际结果有偏差。
对于有线条干扰情况,还需要做额外处理,如转灰度、二值化等操作。 可以利用 Image 对象的 convert()方法参数传人 L,即可将图片转化为灰度图像,示例:
image = image.convert('L') image.show()
传入1 即可将图片进行二值化处理:
image = image.convert('1') image.show()
还可以指定二值化的阈值,上面的方法采用的是默认阈(yù)值。以上方法采用默认阔值 127。不能直接转化原图,要将原图先转为灰度图像,再指定二值化阔值,代码:
import tesserocr from PIL import Imageimage = Image.open('code.jpg') image = image.convert('L') threshold = 80 table = [] for i in range(256):if i < threshold:table.append(0)else:table.append(1)image = image.point(table,'1') image.show()
变量 threshold 代表二值化阈值,阈值设置为 80。原来验证码中的线条已经去除,整个验证码将会变得黑向分明。这时再重新识别验证码,代码:
import tesserocr from PIL import Imageimage = Image.open('code2.jpg')image = image.convert('L') threshold = 127 table = [] for i in range(256):if i < threshold:table.append(0)else:table.append(1) image = image.point(table,'1') result = tesserocr.image_to_text(image) print(result)
针对一些有干扰的图片,做一些灰度和二值化处理,会提高图片识别的正确率。
- 阈值:继续遍历图片。如果图片灰度值大于阈值说明是背景图片,将之颜色设置为白色,否则是文字,颜色设置为黑色
大概逻辑:
(计次循环首(图片.取宽度(),x)
计次循环首(图片.取高度(),y)
‘遍历每一个像素点
‘取出遍历到的当前像素点颜色
颜色值=到字节集(图片.取某点颜色值(x-1,y-1))
‘计算灰度值
灰度值=(颜色值[1]+颜色值[2]+颜色值[3])/3
‘将灰度值放回图片
连续赋值(灰度值,颜色值[1],颜色值[2],颜色值[3])
图片.写某点颜色值(x-1,y-1,灰度值)
计次循环尾()
计次循环尾()
图片.取图片数据()
二、极验滑动验证码的识别
- 识别思路
- 首先找到一个带有极验验证的网站,如极验官方后台,链接:https://account.geetest.com/login。
可以使用Selenium来完全模拟人的行为的方式来完成验证。(一般,验证需要三步:)
- 模拟点击验证按钮: 可以直接用Selenium 模拟点击按钮
- 识别滑动缺口的位置:
- 模拟拖动滑块
- 识别滑动缺口的位置的操作比较关键,需要用到图像相关处理方法
- 首先观察图像:缺口的四周边缘有明显的断裂边缘,边缘和边缘周围有明显的区别。可以实现一个边缘检测算法来找出缺口的位置。对于极验验证码来说,可以利用和原图对比检测的方式来识别缺口的位置,因为在没有滑动滑块之前, 缺口并没有呈现。
可以同时获取两张图片。设定一个对比阈值,然后遍历两张图片,找出相同位置像素 RGB 差距超过此阈值的像素点,那么此像素点的位置就是缺口的位置。
- 模拟拖动滑块中需要走注意:极验验证码增加了机器轨迹识别,匀速移动、 随机速度移动等方法都不能通过验证,只有完全模拟人的移动轨迹才可以通过验证。 人的移动轨迹一般是先加速后减速,需要模拟这个过程才能成功。
- 识别滑动缺口的位置的操作比较关键,需要用到图像相关处理方法
初始化
- 选定链接:https://account.geetest.com/login,极验的管理后台登录页面。首先初始化一些配置,如 Selenium 对象的初始化及一些参数的配置,示例:
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWaitEMAIL = 'test@test.com' PASSWORD = '123456'class CrackGeetest():def __init__(self):self.url = 'https://account.geetest.com/login'self.browser = webdriver.Chrome()self.wait = WebDriverWait(self.browser,20)self.email = EMAILself.password = PASSWORD
其中, EMAIL 和 PASSWORD 就是登录极验需要的用户名和密码,如果没有需先注册。
模拟点击
实现第一步的操作,也就是模拟点击初始的验证按钮
定义一个方法来获取这个按钮,利用显式等待的方法来实现,示例:
def get_geetest_button(self):"获取初始验证按钮:return:按钮对象"button = self.wait.unil(EC.element_to_be_clickable((By.CLASS_NAME,'geetest_radar_tip')))return button
获取一个WebElement对象,调用它的click()方法即可模拟点击,示例:
#点击验证按钮 button = get_geetest_button() button.click()
完成第一步模拟点击验证按钮
识别缺口
识别缺口的位置。
首先获取前后两张比对图片,二者不一致的地方即为缺口 。获取不带缺口图片,利用 Selenium选取图片元素,得到其所在位置和宽高,然后获取整个网页的截图,图片裁切出来即可,代码:
def get_position(self):"""获取验证码位置:return:验证码位置元组"""img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_canvas_img')))time.sleep(2)location =img.locationsize = img.sizetop, bottom, left, right = location['y'],location['y']+size['weight'],location['x'],location['x']+size['width']return(top, bottom, left, right)def get_geetest_image(self,name='captcha.png'):"""获取验证码图片:return:图片对象"""top,bottom,left,right = self.get_position()print('验证码位置', top, bottom, left, right)screenshot = self.get_screenshot()captcha = screenshot.crop((left, top, right, bottom))return captcha
这里 get_position()函数首先获取图片对象,获取它的位置和宽高,随后返回其左上角和右下角的坐标 get_geetest_image()方法获取网页截图,调用 crop()方法将图片裁切出来,返回的是 Image 对象
- 接下来需要获取第二张图片,也就是带缺口的图片。要使得图片出现缺口,只需要点击下方的滑块即可。这个动作触发之后,图片中的缺口就会显现,代码:
def get_slider(self):"""获取滑块:return:滑块对象"""slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'greetest_slider_button')))return slider
利用 get_slider()方法获取滑块对象,调用 click()方法即可触发点击,缺口图片即可呈现,代码:
#点按呼出缺口 slider = self.get_slider() slider.click()
调用 get_geetest_image()方法将第二张图片获取下来即可。
- 现在已经得到两张图片对象,分别赋值给变量 image1 和 image2。再对比图片获取缺口。这里遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据。如果二者 RGB 数据差距距在一定范围内,那就代表两个像素相同,继续比对下一个像素点 如果差距超过一定范围,则代表像素点不同,当前位置即为缺口位置,代码:
def is_pixel_equal(self, image1, image2, x, y):"""判断两个像素是否相同:param image1:图片1:param image1:图片1:param x:位置x:param y:位置y:return:像素是否相同"""#取两个图片的像素点pixel1 = image1.load()[x, y]pixel2 = image2.load()[x, y]threshold = 60if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] -pixel2[2])<threshold:return Trueelse:return False def get_gap(self, image1, image2):"""获取缺口偏移量:param image1:不带缺口图片:param image2:带缺口图片:return:"""left = 60for i in range(left,image1.size[0]):for j in range(image1.size[1]):if not self.is_pixel_equal(image1, image2, i, j):left = ireturn leftreturn left
get_gap()方法即获取缺口位置的方法。此方法的参数是两张图片, 一张为带缺口图片,另一张为不带缺口图片。遍历两张图片的每个像素,利用 is_pixel_equal()方法判断两张图片同一位置的 像素是杏相同。比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold。如果绝对值均在阔值之 内,则代表像素点相同,继续遍历。否则代表不相同的像素点,即缺口的位置。
- 滑块的位置会出现在左边位置,缺口会出现在与滑块同一水平线的位置,所以缺口一般会在滑块的右侧。如果要寻找缺口,直接从滑块右侧寻找即可。直接设置遍历的起始横坐标为 60,也就是从滑块的右侧开始识别,这样识别出的结果就是缺口的位置。
模拟拖动
- 完全模拟加速减速的过程也就是人拖动滑块的操作通过验证。前段滑块做匀加速运动,后段滑块做匀减速运动,利用加速度公式即可完成验证。
- 滑块滑动的加速度用 a 表示, 当前速度用 v 表示,初速用 v0 表示,位移用 x 表示,所需时间用t 表示,满足关系:
- x = v0*t + 0.5*a*t*t
- v = v0 + a*t
- 利用这两个公式可以构造轨迹移动算法,计算出先加速后减速的运动轨迹,代码:
def get_track(self, distance):"""根据偏移量获取移动轨迹:param distance:偏移量:return: 移动轨迹"""#移动轨迹track = []#当前位移current = 0#减速阈值mid = distance * 4 / 5#计算间隔t = 0.2#初速度v = 0while current < distance:if current < mid:a = 2else:a = -3#初速度 V0v0 = vv = v0 + a*tmove = v0 *t +1/2 *a *t *t current += move#加入轨迹 track.append(round(move))return track
定义 get_ rack()方法,传人的参数为移动的总距离,返回的是运动轨迹。运动轨迹用 track 表示,是一个列表,列表的每个元素代表每次移动多少距离。
定义变量 mid,即减速的阔值,也就是加速到什么位置开始减速。这里 mid 值为 4/5,即模拟前 4/5 路程是加速过程,后 1/5 路程是减速过程。
- 接着定义当前位移的距离变量 current,初始为 0,然后进入 while 循环,循环的条件是当前位移小于总距离。在循环里我们分段定义了加速度,其中加速过程的加速度定义为 2,减速过程的加速度定义为 -3。之后套用位移公式计算出某个时间段内的位移,将当前位移更新并记录到轨迹里即可。
- 直到运动轨迹达到总距离时,循环终止。最后得到的 track 记录了每个时间间隔移动了多少位移, 这样滑块的运动轨迹就得到了。
- 最后按照该运动轨迹拖动滑块即可,方法实现代码:
def move_to_gap(self, slider,tracks):"""拖动滑块到缺口处:params slider:滑块:params tracks:轨迹:return:"""ActionChains(self,browser).click_and_hold(slider).perform()for x in tracks:ActionChains(self,browser).move_by_offset(xoffset=x,yoffset=0).perform()time.sleep(0.5)ActionChains(self.browser).release().perform()
传人的参数为滑块对象和运动轨迹。首先调用 ActionChains 的 click_and_ hold()方法按住拖动底部滑块,遍历运动轨迹获取每小段位移距离,调用 move_by_offset()方法移动此位移,最后调用 release()方法松开鼠标即可。
- 滑块滑动的加速度用 a 表示, 当前速度用 v 表示,初速用 v0 表示,位移用 x 表示,所需时间用t 表示,满足关系:
完整代码(需适当修改参数):
import time from io import BytesIO from PIL import Image from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as ECEMAIL = 'cqc@cuiqingcai.com' PASSWORD = '' BORDER = 6 INIT_LEFT = 60class CrackGeetest():def __init__(self):self.url = 'https://account.geetest.com/login'self.browser = webdriver.Chrome()self.wait = WebDriverWait(self.browser, 20)self.email = EMAILself.password = PASSWORDdef __del__(self):self.browser.close()def get_geetest_button(self):"""获取初始验证按钮:return:"""button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))return buttondef get_position(self):"""获取验证码位置:return: 验证码位置元组"""img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))time.sleep(2)location = img.locationsize = img.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']return (top, bottom, left, right)def get_screenshot(self):"""获取网页截图:return: 截图对象"""screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))return screenshotdef get_slider(self):"""获取滑块:return: 滑块对象"""slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))return sliderdef get_geetest_image(self, name='captcha.png'):"""获取验证码图片:return: 图片对象"""top, bottom, left, right = self.get_position()print('验证码位置', top, bottom, left, right)screenshot = self.get_screenshot()captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captchadef open(self):"""打开网页输入用户名密码:return: None"""self.browser.get(self.url)email = self.wait.until(EC.presence_of_element_located((By.ID, 'email')))password = self.wait.until(EC.presence_of_element_located((By.ID, 'password')))email.send_keys(self.email)password.send_keys(self.password)def get_gap(self, image1, image2):"""获取缺口偏移量:param image1: 不带缺口图片:param image2: 带缺口图片:return:"""left = 60for i in range(left, image1.size[0]):for j in range(image1.size[1]):if not self.is_pixel_equal(image1, image2, i, j):left = ireturn leftreturn leftdef is_pixel_equal(self, image1, image2, x, y):"""判断两个像素是否相同:param image1: 图片1:param image2: 图片2:param x: 位置x:param y: 位置y:return: 像素是否相同"""# 取两个图片的像素点pixel1 = image1.load()[x, y]pixel2 = image2.load()[x, y]threshold = 60if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] - pixel2[2]) < threshold:return Trueelse:return Falsedef get_track(self, distance):"""根据偏移量获取移动轨迹:param distance: 偏移量:return: 移动轨迹"""# 移动轨迹track = []# 当前位移current = 0# 减速阈值mid = distance * 4 / 5# 计算间隔t = 0.2# 初速度v = 0while current < distance:if current < mid:# 加速度为正2a = 2else:# 加速度为负3a = -3# 初速度v0v0 = v# 当前速度v = v0 + atv = v0 + a * t# 移动距离x = v0t + 1/2 * a * t^2move = v0 * t + 1 / 2 * a * t * t# 当前位移current += move# 加入轨迹 track.append(round(move))return trackdef move_to_gap(self, slider, track):"""拖动滑块到缺口处:param slider: 滑块:param track: 轨迹:return:"""ActionChains(self.browser).click_and_hold(slider).perform()for x in track:ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()time.sleep(0.5)ActionChains(self.browser).release().perform()def login(self):"""登录:return: None"""submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'login-btn')))submit.click()time.sleep(10)print('登录成功')def crack(self):# 输入用户名密码 self.open()# 点击验证按钮button = self.get_geetest_button()button.click()# 获取验证码图片image1 = self.get_geetest_image('captcha1.png')# 点按呼出缺口slider = self.get_slider()slider.click()# 获取带缺口的验证码图片image2 = self.get_geetest_image('captcha2.png')# 获取缺口位置gap = self.get_gap(image1, image2)print('缺口位置', gap)# 减去缺口位移gap -= BORDER# 获取移动轨迹track = self.get_track(gap)print('滑动轨迹', track)# 拖动滑块 self.move_to_gap(slider, track)success = self.wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功'))print(success)# 失败后重试if not success:self.crack()else:self.login()if __name__ == '__main__':crack = CrackGeetest()crack.crack()
View Code
三、点触验证码的识别
- 利用在线付费平台(推荐超级鹰https://www.chaojiying.com)
- 获取API
- 在官网下载对应 Python API,链接:https://www.chaojiying.com/api-14.html。使用requests库来实现。
- 修改后的python3版本的API:
import requests from hashlib import md5class Chaojiying(object):def __init__(self, username, password, soft_id):self.username = usernameself.password = md5(password.encode('utf-8')).hexdigest()self.soft_id = soft_idself.base_params = {'user':self.username,'pass2':self.password,'softid':self.soft_id,}self.headers = {'Connection':'Keep_Alive','User-Agent':'Mozilla/4.0(compatible;MSIE 8.0; Windows NT 5.1; Trident/4.0)',}def post_pic(self, im, codetype):"""im: 图片字节codetype: 题目类型参考 http://www.chaojiying.com/price.html"""params = {'codetype':codetype,}params.update(self.base_params)files = {'userfile':('ccc.jpg',im)}r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,headers=self.headers)return r.json()def report_error(self,im_id):"""im_id:报错题目的图片ID """params = {'id':im_id,}params.update(self.base_params)r = requests.post('http://Upload.chaojiying.net/Upload/ReportError.php',data=params, headers= self.headers)return r.json()
这里定义一个chaojiying类,构造函数接收三个参数,分别是超级鹰用户名、密码以及软件ID,保存以备使用。最重要的一个方法:post_pic(),需要传入图片对象和验证码的代号,该方法会将图片对象和相关信息发给超级鹰后台进行识别,然后将识别成功的JSON返回
- 初始化
- 首先初始化一些变量,如WebDriver、chaojiying对象等,代码实现如下:
EMAIL = 'Mack01' PASSWORD = '超级鹰密码' #超级鹰用户名、密码、软件ID、验证码类型 CHAOJIYING_USERNAME = 'Germey' CHAOJIYING_PASSWORD = '' CHAOJIYING_SOFT_ID = 893590 CHAOJIYING_KIND = 9102class CrackTouClick():def __init__(self):self.url = 'http://admin.touclick.com/login.html'self.browser = webdriver.Chrome()self.wait = WebDriverWait(self.browser,20)self.email = EMAILself.password =PASSWORDself.chaojiying = Chaojiying(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)
- 首先初始化一些变量,如WebDriver、chaojiying对象等,代码实现如下:
获取验证码
完善表单,模拟点击呼出验证码,代码:
def open(self):"""打开网页输入用户名密码 return: None """self.browser.get(self.url)email =self.wait.until(EC.present_of_element_located((By.ID,'email')))password = self.wait.until(EC.present_of_element_located((By.ID,'password')))email.send_keys(self.email)password.send_keys(self.password)def get_touclick_button(self):"""获取初始验证按钮return: """button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'touclick-hod-wrap')))return button
open()方法负责填写表单,get_touclick_button()方法获取验证码按钮,之后触发点击即可。
接下来,类似极验验证码图像获取一样,获取验证码图片的位置和大小,从网页截图里截取相应 的验证码图片,代码实现如下所示:def get_touclick_element(self):"""获取验证图片对象return: 图片对象 """element = self.wait.until(EC.present_of_element_located((By.CLASS_NAME,'touclick-pub-content')))return element def get_position(self):"""获取验证码位置 return: 验证码位置元组"""element =self.get_touclick_element()time.sleep(2)location = element.locationsize = element.sizetop, bottom, left, right = location['y'],location['y']+size['height'],location['x'],location['x']+size['width']return (top, bottom, left, right) def get_screenshot(self):"""获取网页截图 return: 截图对象 """screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))return screenshot def get_touclick_image(self, name='captcha.png'):"""获取验证码图片return: 图片对象"""top,bottom,left,right =self.get_position()print('验证码位置',top,bottom, left ,right)screenshot = self.get_screenshot()captcha = screenshot.crop((left,top,right,bottom))return captcha
get_ touclick_image()方法即为从网页截图中截取对应的验证码图片,其中验证码图片的相对位 置坐标由 get position()方法返回得到。 最后我们得到的是 Image 对象
识别验证码
调用 Chaojiying 对象的 post_pic()方法,即可把图片发送给超级鹰后台,这里发送的图像是字节 流格式,代码实现如下所示:
image = self.get_touclick_image() bytes_array = BytesIO() image.save(bytes_array, format='PNG') #识别验证码 result = self.chaojiying.post_pic(bytes_array.getvalue(),CHAOJIYING_KIND) print(result)
运行之后, result 变量就是超级鹰后台的识别结果。 运行需要等待几秒。
返回的结果是一个 JSON。 如果识别成功,典型的返回结果如下所示 :
{’ err_no’:0, 'err_str’: 'OK', 'pic_id’: ’6002001380949200001', 'pic_str' :'132,127|56, 77 ','md5': '1f8e1d4bef8b11484cb1f1f34299865b'}
其中,pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以|分隔。接下来只需要将其解析,然后模拟点击,代码:def get_points(self, captcha_result):"""解析识别结果param captcha_result: 识别结果 return: 转化后的结果"""groups = captcha_result.get('pic_str').split('|')locations = [[int(number) for number in group.split(',')] for group in groups]return locations def touch_click_words(self,locations):"""点击验证图片 param locations:点击位置 return: None"""for location in locations:print(location)ActionChains(self.browser).move_to_element_with_offset(self.get_touclick_element(),location[0],location[1]).click().perform()time.sleep(1)
这里用 get_points()方法将识别结果变成列表的形式。touch_click_words()方法则通过调用 move_to_element_with_offset()方法依次传入解析后的坐标,点击即可。
这样就模拟完成坐标的点选,最后点击提交验证的按钮, 等待验证通过,再点击登录按钮即可成功登录。
- 获取API
四、微博宫格验证码的识别
一般选用全图匹配的方式来进行识别。找到匹配的模板之后,就可以得到事先为模板定义的拖动顺序,然后模拟拖动即可。
- 获取模板
- 需要做一下准备工作,先保存所有的 24 张验证码全图。因为验证码是随机的,一共有 4!= 24 种。可以写一段程序来批量保存验证码图片,然后从中筛选出需要的图片,代码所示:
import time from io import BytesIO from PIL import Image from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as ECUSERNAME = '' PASSWORD = ''class CrackWeiboSlide():def __init__(self):self.url = 'https://passport.weibo.cn/signin/login'self.browser = webdriver.Chrome()self.wait = WebDriverWait(self.browser,20)self.username = USERNAMEself.password = PASSWORDdef __del__(self):self.browser.close()def open(self):"""打开网页输入用户名密码并点击return: None"""self.browser.get(self.url)username = self.wait.until(EC.presence_of_element_located((By.ID,'loginName')))password = self.wait.until(EC.presence_of_element_located((By.ID,'loginPassword')))submit = self.wait.until(EC.element_to_be_clickable((By.ID,'loginAction')))username.send_keys(self.username)password.send_keys(self.password)submit.click()def get_position(self):"""获取验证码位置return: 验证码位置元组"""global imgtry:img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'Patt-shadow')))except TimeoutException:print('未出现验证码')self.open()time.sleep(2)location = img.locationsize = img.sizetop,bottom, left, right = location['y'],location['y']+size['height'],location['x'],location['x']+size['width']return (top, bottom, left, right)def get_screenshot(self):"""获取网页截图return: 截图对象"""screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))return screenshotdef get_image(self, name='captcha.png'):"""获取验证码吗图片return: 图片对象"""top, bottom, left, right = self.get_position()print('验证码位置', top, bottom,left, right)screenshot = self.get_screenshot()captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captchadef main(self):"""批量获取验证码return: 图片对象"""count = 0while True:self.open()self.get_image(str(count)+'.png')count +=1 if __name__ =='__main__':crack = CrackWeiboSlide()crack.main()
只需要挑选出不同的 24 张验证码图片并命名保存。 名称可以直接取作宫格的滑动的顺序,识别过程只需要遍历模板进行匹配。
- 需要做一下准备工作,先保存所有的 24 张验证码全图。因为验证码是随机的,一共有 4!= 24 种。可以写一段程序来批量保存验证码图片,然后从中筛选出需要的图片,代码所示:
- 模板匹配
调用 get_image()方法,得到验证码图片对象。然后,对验证码图片对象进行模板匹配,定义如下方法:
from os import listdirdef detect_image(self,image):"""匹配图片param image: 图片return: 拖动顺序"""for template_name in listdir(TEMPLATES_FOLDER):print('正在匹配', template_name)template = Image.open(TEMPLATES_FOLDER + template_name)if self.same_image(image, template):#返回顺序numbers = [int(number) for number in list(template_name.split('.')[0])]print('拖动顺序',numbers)return numbers
TEMPLATES_FOLDER 就是模板所在的文件夹。 通过 listdir()方法获取所有模板的文件名称,然后对其进行遍历,通过 same_image()方法对验证码和模板进行比对。 如果匹配成功,就将匹配到的模板文件名转换为列表。例:模板文件 3124.png匹配到,返回结果为[3,1,2,4].
对比的方法实现如下:
def is_pixel_equal(self, image1, image2, x, y):"""判断两个像素是否相同:param image1: 图片1:param image2: 图片2:param x: 位置x:param y: 位置y:return: 像素是否相同"""# 获取两个图片的像素点pixel1 = image1.load()[x, y]pixel2 = image2.load()[x, y]threshold = 20if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] - pixel2[2]) < threshold:return Trueelse:return Falsedef same_image(self, image, template):"""识别相似验证码param image: 待识别验证码param template: 模板return:"""# 相识度阈值threshold = 0.99count = 0for x in range(image.width):for y in range(image.height):# 判断像素是否相同if self.is_pixel_equal(image, template, x, y):count += 1result = float(count) / (image.width * image.height)if result > threshold:print('功能匹配')return Truereturn False
在这里比对图片也利用了遍历像素的方法。 same_image()方法接收两个参数,image 为待检测的验证码图片对象,template 是模板对象。由于二者大小是完全一致的,所以在这里遍历了图片的所有像素点。比对二者同一位置的像素点,如果像素点相同,计数就加 1。最后计算相同的像素点占总像素的比例。如果该比例超过一定阈值,那就判定图片完全相同,则匹配成功。这里阔值设定为 0.99, 即如果二者有 0.99 以上的相似比,则代表匹配成功。 依次匹配 24 个模板。 如果验证码图片正常,我们总能找到一个匹配的模板, 这样就可以得到宫格的滑动顺序了。
模拟拖动
根据滑动顺序拖动鼠标,连接各个宫格,方法实现如下所示:
def move(self, numbers):"""根据顺序拖动 """#获得四个按点circles = self.browser.find_elements_by_css_selector('.patt-wrap. patt-circ')dx = dy =0for index in range(4):circle = circles[numbers[index]-1]#如果是第一次循环if index == 0:# 点击第一个按点ActionChains(self.browser)\.move_to_element_with_offset(circle, circle.size['width']/2, circle.size['height']/2 )\.click_and_hold().perform()else:#小幅移动次数times = 30#拖动for i in range(times):ActionChains(self.browser).move_by_offset(dx/times, dy/times).perform()time.sleep(1/times)# 如果是最后一次循环if index == 3:#松开鼠标 ActionChains(slef.browser).release().perform()else:#计算下一次偏移dx = circles[numbers[index +1]-1].location['x'] - circle.location['x']dy = circles[numbers[index +1]-1].location['y'] - circle.location['y']
这里方法接收的参数就是宫格的点按顺序,如 [3,1,2,4]。首先利用 find_elements_by_css_selector()方法获取到 4 个宫格元素,它是一个列表形式,每个元素代表一个宫格。接下来遍历宫格的点按顺序,做一系列对应操作。
其中如果当前遍历的是第一个宫格,那就直接鼠标点击并保持动作,否则移动到下一个宫格。如果当前遍历的是最后一个宫格,那就松开鼠标,如果不是最后一个宫格,则计算移动到下一个宫格的偏移盘。
通过 4 次循环,便可以成功操作浏览器完成宫格验证码的拖拽填充,松开鼠标之后即可识别成功。 鼠标会慢慢从起始位置移动到终止位置。 最后一个宫格松开之后,验证码的识别便完成了。
转载于:https://www.cnblogs.com/Mack-Yang/p/10225297.html
Class 18 - 1 图形验证码的识别相关推荐
- 爬虫从入门到精通(15) | 使用Python-OCR识别库对图形验证码进行识别
文章目录 一.tesseract破解验证码 1.下载安装tesseract 2.python安装tesseract模块及其配置 3.demo测试 二.muggle_ocr破解和识别验证码 1.介绍 2 ...
- 三十四、使用pytesser3 和pillow完成图形验证码的识别
@Author: Runsen 文章目录 灰度化 二值化 降噪 灰度化 像素点是最小的图片单元,一张图片由很多像素点构成,一个像素点的颜色是由RGB三个值来表现的,所以一个像素点对应三个颜色向量矩阵, ...
- 对登录接口的简单图形验证码进行识别 20221004
Python黑客编程原理与实战--作业(二) 1/编程题 \1. 语言限定为python3 环境准备,使用虚拟python3环境 pip3 install virtualenv virtualenv ...
- 网络爬虫笔记—图形验证码识别
网络爬虫笔记-图形验证码识别 <兄弟们,本文章开启了关注后阅读.大家如不想关注,可直接微信搜索"宏蜘蛛"或文章标题,查看文章.> 1.什么是图形验证码 像知网注册界面的 ...
- 验证码的识别(极验验证码)
本文介绍了几种常见的验证码类型以及它们的识别方法,包括图形验证码.极验滑动验证码.点触验证码和微博宫格验证码等.其中,针对图形验证码的识别方法是使用OCR技术,并且需要安装tesserocr库.我们可 ...
- 第八部分 验证码的识别(极验验证码)
前言: 验证码是众多网站采取的反爬措施.验证码的花样也很多,主要有下面这几种类验证码: 图形验证码:数字.英文字母.混淆曲线组合成的验证码. 行为验证码:识别文字,点击与文字相符的图片验证码. 交互式 ...
- 验证码识别初探——图形验证码为主
目录 一.需求 二.验证码起源 三.验证码分类 纯文本验证码 图形验证码 Gif动画验证码 手机短信验证码 手机语音验证码 视频验证码 手势验证码 四.简单验证码识别流程和一些算法 五.基本案例 案例 ...
- 图形数字的识别算法: 车牌识别及验证码识别的一般思路
图形数字的识别算法: 车牌识别及验证码识别的一般思路 本文源自我之前花了2 天时间做的一个简单的车牌识别系统.那个项目,时间太紧,样本也有限,达不到对方要求的95% 识别率(主要对于车牌来说,D,0 ...
- Python模拟登录,Python识别图形验证码实现自动登陆
前言 利用Python识别图形验证码,selenium模块实现自动登陆.废话不多说. 让我们愉快地开始吧~ 开发工具 Python版本: 3.6.4 相关模块: re: numpy模块: pytess ...
最新文章
- 不支持打开非业务域名htts:....请重新配置
- 史上自定义 JavaScript 函数Top 10
- 花费我一个月实习的工资买了一个软件测试就业班课程,成功转行了算是
- 天线的基本原理和概念
- 小李飞刀:刷题第四弹!
- trietree的一个小练习
- 零基础程序员如何花 8 个月时间获得特斯拉实习机会?
- Poj_2536 Gopher II -二分图建图
- python技术文档的阅读理解
- 入门JSP第一步,看这里~
- 深入探讨apply()方法的作用
- Windows10下下载安装ideaIU
- 阿里云DataV—多字段取满足某一个字段的行,多字段取不满足某一个字段的行(3)
- 短信工具类 SmsUtil
- Layui 表格分页控件
- android七牛云存储,使用七牛云存储上传android本地视频并播放
- 计算机网络技术-Mooc
- 1.2什么是HLSL
- win10键盘全部没反应_win10笔记本键盘全部没反应 win10键盘全部没反应解决方法...
- 使用视频作为网页背景的写法
热门文章
- php 过滤注释,PHP过滤html注释
- php 判断字符串为时间,检查字符串是否为unix时间戳
- 通过连接池无法连接mysql_连接池无法链接数据库
- word文字铺满页面_这5个Word问题,你会解决几个?
- python a除以b_A除以B (Python)
- java stream groupingBy(classifier, downstream, mapFactory)
- Ansible Inventory
- 马化腾说它千年实现李彦宏说新瓶装旧酒,只有马云看好阿里笑了
- 中国金融体系主要指标大全!
- Java虚拟机学习总结(3)——JDK内置工具(jps、jstack、jmap、jstat)使用详解