python识别验证码的思路及解决方案


Posted in Python onSeptember 13, 2020

1、介绍

在爬虫中经常会遇到验证码识别的问题,现在的验证码大多分计算验证码、滑块验证码、识图验证码、语音验证码等四种。本文就是识图验证码,识别的是简单的验证码,要想让识别率更高,识别的更加准确就需要花很多的精力去训练自己的字体库。

识别验证码通常是这几个步骤:

(1)灰度处理

(2)二值化

(3)去除边框(如果有的话)

(4)降噪

(5)切割字符或者倾斜度矫正

(6)训练字体库

(7)识别

这6个步骤中前三个步骤是基本的,4或者5可根据实际情况选择是否需要。

经常用的库有pytesseract(识别库)、OpenCV(高级图像处理库)、imagehash(图片哈希值库)、numpy(开源的、高性能的Python数值计算库)、PIL的 Image,ImageDraw,ImageFile等。

2、实例

以某网站登录的验证码识别为例:具体过程和上述的步骤稍有不同。

python识别验证码的思路及解决方案

首先分析一下,验证码是由4个从0到9等10个数字组成的,那么从0到9这个10个数字没有数字只有第一、第二、第三和第四等4个位置。那么计算下来共有40个数字位置,如下:

python识别验证码的思路及解决方案

那么接下来就要对验证码图片进行降噪、分隔得到上面的图片。以这40个图片集作为基础。

对要验证的验证码图片进行降噪、分隔后获取四个类似上面的数字图片、通过和上面的比对就可以知道该验证码是什么了。

以上面验证码2837为例:

1、图片降噪

python识别验证码的思路及解决方案

2、图片分隔

python识别验证码的思路及解决方案

3、图片比对

通过比验证码降噪、分隔后的四个数字图片,和上面的40个数字图片进行哈希值比对,设置一个误差,max_dif:允许最大hash差值,越小越精确,最小为0。

python识别验证码的思路及解决方案

这样四个数字图片通过比较后获取对应是数字,连起来,就是要获取的验证码。

完整代码如下:

#coding=utf-8
import os
import re
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
from selenium.webdriver.common.action_chains import ActionChains
import collections
import mongoDbBase
import numpy
import imagehash
from PIL import Image,ImageFile
import datetime
class finalNews_IE:
    def __init__(self,strdate,logonUrl,firstUrl,keyword_list,exportPath,codepath,codedir):
        self.iniDriver()
        self.db = mongoDbBase.mongoDbBase()
        self.date = strdate
        self.firstUrl = firstUrl
        self.logonUrl = logonUrl
        self.keyword_list = keyword_list
        self.exportPath = exportPath
        self.codedir = codedir
        self.hash_code_dict ={}
        for f in range(0,10):
            for l in range(1,5):
                file = os.path.join(codedir, "codeLibrary\code" +  str(f) + '_'+str(l) + ".png")
                # print(file)
                hash = self.get_ImageHash(file)
                self.hash_code_dict[hash]= str(f)
    def iniDriver(self):
        # 通过配置文件获取IEDriverServer.exe路径
        IEDriverServer = "C:\Program Files\Internet Explorer\IEDriverServer.exe"
        os.environ["webdriver.ie.driver"] = IEDriverServer
        self.driver = webdriver.Ie(IEDriverServer)
    def WriteData(self, message, fileName):
        fileName = os.path.join(os.getcwd(), self.exportPath + '/' + fileName)
        with open(fileName, 'a') as f:
            f.write(message)
    # 获取图片文件的hash值
    def get_ImageHash(self,imagefile):
        hash = None
        if os.path.exists(imagefile):
            with open(imagefile, 'rb') as fp:
                hash = imagehash.average_hash(Image.open(fp))
        return hash
    # 点降噪
    def clearNoise(self, imageFile, x=0, y=0):
        if os.path.exists(imageFile):
            image = Image.open(imageFile)
            image = image.convert('L')
            image = numpy.asarray(image)
            image = (image > 135) * 255
            image = Image.fromarray(image).convert('RGB')
            # save_name = "D:\work\python36_crawl\Veriycode\mode_5590.png"
            # image.save(save_name)
            image.save(imageFile)
            return image
    #切割验证码
    # rownum:切割行数;colnum:切割列数;dstpath:图片文件路径;img_name:要切割的图片文件
    def splitimage(self, imagePath,imageFile,rownum=1, colnum=4):
        img = Image.open(imageFile)
        w, h = img.size
        if rownum <= h and colnum <= w:
            print('Original image info: %sx%s, %s, %s' % (w, h, img.format, img.mode))
            print('开始处理图片切割, 请稍候...')
            s = os.path.split(imageFile)
            if imagePath == '':
                dstpath = s[0]
            fn = s[1].split('.')
            basename = fn[0]
            ext = fn[-1]
            num = 1
            rowheight = h // rownum
            colwidth = w // colnum
            file_list =[]
            for r in range(rownum):
                index = 0
                for c in range(colnum):
                    # (left, upper, right, lower)
                    # box = (c * colwidth, r * rowheight, (c + 1) * colwidth, (r + 1) * rowheight)
                    if index < 1:
                        colwid = colwidth + 6
                    elif index < 2:
                        colwid = colwidth + 1
                    elif index < 3:
                        colwid = colwidth
                    box = (c * colwid, r * rowheight, (c + 1) * colwid, (r + 1) * rowheight)
                    newfile = os.path.join(imagePath, basename + '_' + str(num) + '.' + ext)
                    file_list.append(newfile)
                    img.crop(box).save(newfile, ext)
                    num = num + 1
                    index += 1
            return file_list
    def compare_image_with_hash(self, image_hash1,image_hash2, max_dif=0):
        """
                max_dif: 允许最大hash差值, 越小越精确,最小为0
                推荐使用
                """
        dif = image_hash1 - image_hash2
        # print(dif)
        if dif < 0:
            dif = -dif
        if dif <= max_dif:
            return True
        else:
            return False
    # 截取验证码图片
    def savePicture(self):
        self.driver.get(self.logonUrl)
        self.driver.maximize_window()
        time.sleep(1)
        self.driver.save_screenshot(self.codedir +"\Temp.png")
        checkcode = self.driver.find_element_by_id("checkcode")
        location = checkcode.location  # 获取验证码x,y轴坐标
        size = checkcode.size  # 获取验证码的长宽
        rangle = (int(location['x']), int(location['y']), int(location['x'] + size['width']),
                  int(location['y'] + size['height']))  # 写成我们需要截取的位置坐标
        i = Image.open(self.codedir +"\Temp.png")  # 打开截图
        result = i.crop(rangle)  # 使用Image的crop函数,从截图中再次截取我们需要的区域
        filename = datetime.datetime.now().strftime("%M%S")
        filename =self.codedir +"\Temp_code.png"
        result.save(filename)
        self.clearNoise(filename)
        file_list = self.splitimage(self.codedir,filename)
        verycode =''
        for f in file_list:
            imageHash = self.get_ImageHash(f)
            for h,code in self.hash_code_dict.items():
                flag = self.compare_image_with_hash(imageHash,h,0)
                if flag:
                    # print(code)
                    verycode+=code
                    break
        print(verycode)
        self.driver.close()
   
    def longon(self):
        self.driver.get(self.logonUrl)
        self.driver.maximize_window()
        time.sleep(1)
        self.savePicture()
        accname = self.driver.find_element_by_id("username")
        # accname = self.driver.find_element_by_id("//input[@id='username']")
        accname.send_keys('ctrchina')
        accpwd = self.driver.find_element_by_id("password")
        # accpwd.send_keys('123456')
        code = self.getVerycode()
        checkcode = self.driver.find_element_by_name("checkcode")
        checkcode.send_keys(code)
        submit = self.driver.find_element_by_name("button")
        submit.click()

实例补充:

# -*- coding: utf-8 -*
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
import re
import requests
import io
import os
import json
from PIL import Image
from PIL import ImageEnhance
from bs4 import BeautifulSoup

import mdata

class Student:
 def __init__(self, user,password):
 self.user = str(user)
 self.password = str(password)
 self.s = requests.Session()

 def login(self):
 url = "http://202.118.31.197/ACTIONLOGON.APPPROCESS?mode=4"
 res = self.s.get(url).text
 imageUrl = 'http://202.118.31.197/'+re.findall('<img src="(.+?)" width="55"',res)[0]
 im = Image.open(io.BytesIO(self.s.get(imageUrl).content))
 enhancer = ImageEnhance.Contrast(im)
 im = enhancer.enhance(7)
 x,y = im.size
 for i in range(y):
  for j in range(x):
  if (im.getpixel((j,i))!=(0,0,0)):
   im.putpixel((j,i),(255,255,255))
 num = [6,19,32,45]
 verifyCode = ""
 for i in range(4):
  a = im.crop((num[i],0,num[i]+13,20))
  l=[]
  x,y = a.size
  for i in range(y):
  for j in range(x):
   if (a.getpixel((j,i))==(0,0,0)):
   l.append(1)
   else:
   l.append(0)
  his=0
  chrr="";
  for i in mdata.data:
  r=0;
  for j in range(260):
   if(l[j]==mdata.data[i][j]):
   r+=1
  if(r>his):
   his=r
   chrr=i
  verifyCode+=chrr
  # print "辅助输入验证码完毕:",verifyCode
 data= {
 'WebUserNO':str(self.user),
 'Password':str(self.password),
 'Agnomen':verifyCode,
 }
 url = "http://202.118.31.197/ACTIONLOGON.APPPROCESS?mode=4"
 t = self.s.post(url,data=data).text
 if re.findall("images/Logout2",t)==[]:
  l = '[0,"'+re.findall('alert((.+?));',t)[1][1][2:-2]+'"]'+" "+self.user+" "+self.password+"\n"
  # print l
  # return '[0,"'+re.findall('alert((.+?));',t)[1][1][2:-2]+'"]'
  return [False,l]
 else:
  l = '登录成功 '+re.findall('! (.+?) ',t)[0]+" "+self.user+" "+self.password+"\n"
  # print l
  return [True,l]

 def getInfo(self):
 imageUrl = 'http://202.118.31.197/ACTIONDSPUSERPHOTO.APPPROCESS'
 data = self.s.get('http://202.118.31.197/ACTIONQUERYBASESTUDENTINFO.APPPROCESS?mode=3').text #学籍信息
 data = BeautifulSoup(data,"lxml")
 q = data.find_all("table",attrs={'align':"left"})
 a = []
 for i in q[0]:
  if type(i)==type(q[0]) :
  for j in i :
   if type(j) ==type(i):
   a.append(j.text)
 for i in q[1]:
  if type(i)==type(q[1]) :
  for j in i :
   if type(j) ==type(i):
   a.append(j.text)
 data = {}
 for i in range(1,len(a),2):
  data[a[i-1]]=a[i]
 # data['照片'] = io.BytesIO(self.s.get(imageUrl).content)
 return json.dumps(data)

 def getPic(self):
 imageUrl = 'http://202.118.31.197/ACTIONDSPUSERPHOTO.APPPROCESS'
 pic = Image.open(io.BytesIO(self.s.get(imageUrl).content))
 return pic

 def getScore(self):
  score = self.s.get('http://202.118.31.197/ACTIONQUERYSTUDENTSCORE.APPPROCESS').text #成绩单
  score = BeautifulSoup(score, "lxml")
  q = score.find_all(attrs={'height':"36"})[0]
  point = q.text
  print point[point.find('平均学分绩点'):]
  table = score.html.body.table
  people = table.find_all(attrs={'height' : '36'})[0].string
  r = table.find_all('table',attrs={'align' : 'left'})[0].find_all('tr')
  subject = []
  lesson = []
  for i in r[0]:
  if type(r[0])==type(i):
   subject.append(i.string)
  for i in r:
  k=0
  temp = {}
  for j in i:
   if type(r[0])==type(j):
   temp[subject[k]] = j.string
   k+=1
  lesson.append(temp)
  lesson.pop()
  lesson.pop(0)
  return json.dumps(lesson)

 def logoff(self):
 return self.s.get('http://202.118.31.197/ACTIONLOGOUT.APPPROCESS').text

if __name__ == "__main__":
 a = Student(20150000,20150000)
 r = a.login()
 print r[1]
 if r[0]:
 r = json.loads(a.getScore())
 for i in r:
  for j in i:
  print i[j],
  print
 q = json.loads(a.getInfo())
 for i in q:
  print i,q[i]
 a.getPic().show()
 a.logoff()

到此这篇关于python识别验证码的思路及解决方案的文章就介绍到这了,更多相关python识别验证码的思路是什么内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python显示天气预报
Mar 02 Python
Django实现表单验证
Sep 08 Python
python在html中插入简单的代码并加上时间戳的方法
Oct 16 Python
python存储16bit和32bit图像的实例
Dec 05 Python
python读取图片的方式,以及将图片以三维数组的形式输出方法
Jul 03 Python
Python 如何优雅的将数字转化为时间格式的方法
Sep 26 Python
python 实现多维数组(array)排序
Feb 28 Python
.img/.hdr格式转.nii格式的操作
Jul 01 Python
基于python实现简单网页服务器代码实例
Sep 14 Python
Python+OpenCV图像处理——实现轮廓发现
Oct 23 Python
Django web自定义通用权限控制实现方法
Nov 24 Python
Python之基础函数案例详解
Aug 30 Python
Python实现敏感词过滤的4种方法
Sep 12 #Python
Python CategoricalDtype自定义排序实现原理解析
Sep 11 #Python
python 如何利用argparse解析命令行参数
Sep 11 #Python
Python Pivot table透视表使用方法解析
Sep 11 #Python
Python extract及contains方法代码实例
Sep 11 #Python
python 利用zmail库发送邮件
Sep 11 #Python
浅析Python 责任链设计模式
Sep 11 #Python
You might like
用PHP将数据导入到Foxmail
2006/10/09 PHP
yii上传文件或图片实例
2014/04/01 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十四)
2014/06/26 PHP
php匹配字符中链接地址的方法
2014/12/22 PHP
laravel config文件配置全局变量的例子
2019/10/13 PHP
JavaScript 学习 - 提高篇
2007/02/02 Javascript
JavaScript中的console.dir()函数介绍
2014/12/29 Javascript
jQuery简单实现遍历数组的方法
2015/04/14 Javascript
JavaScript中用于四舍五入的Math.round()方法讲解
2015/06/15 Javascript
AngularJS使用ocLazyLoad实现js延迟加载
2017/07/05 Javascript
Three.js如何实现雾化效果示例代码
2017/09/27 Javascript
详解vue-cli脚手架build目录中的dev-server.js配置文件
2017/11/24 Javascript
浅谈webpack对样式的处理
2018/01/05 Javascript
详解JavaScript基础知识(JSON、Function对象、原型、引用类型)
2018/01/16 Javascript
jQuery实现碰到边缘反弹的动画效果
2018/02/24 jQuery
js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
2019/01/19 Javascript
js单线程的本质 Event Loop解析
2019/10/29 Javascript
echarts.js 动态生成多个图表 使用vue封装组件操作
2020/07/19 Javascript
[23:18]Spirit vs Liquid Supermajor小组赛A组 BO3 第二场 6.2
2018/06/03 DOTA
python显示天气预报
2014/03/02 Python
《Python学习手册》学习总结
2018/01/17 Python
Python内建模块struct实例详解
2018/02/02 Python
python远程连接服务器MySQL数据库
2018/07/02 Python
python实现本地批量ping多个IP的方法示例
2019/08/07 Python
python中几种自动微分库解析
2019/08/29 Python
OpenCV里的imshow()和Matplotlib.pyplot的imshow()的实现
2019/11/25 Python
pandas 中对特征进行硬编码和onehot编码的实现
2019/12/20 Python
Python3利用openpyxl读写Excel文件的方法实例
2021/02/03 Python
求职简历的自我评价怎样写好
2013/10/07 职场文书
护理专业的自荐信
2013/10/22 职场文书
就业表自我评价分享
2014/02/06 职场文书
推荐信模板
2014/05/09 职场文书
代理人委托书
2014/08/01 职场文书
初三学生语文考试作弊检讨书
2014/12/14 职场文书
小学生优秀评语
2014/12/29 职场文书
督导岗位职责
2015/02/04 职场文书