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使用zip合并相邻列表项的方法示例
Mar 17 Python
Appium+Python自动化测试之运行App程序示例
Jan 23 Python
10 分钟快速入门 Python3的教程
Jan 29 Python
Pyinstaller打包.py生成.exe的方法和报错总结
Apr 02 Python
Python面向对象程序设计之私有属性及私有方法示例
Apr 08 Python
django重新生成数据库中的某张表方法
Aug 28 Python
python2与python3爬虫中get与post对比解析
Sep 18 Python
PyTorch 对应点相乘、矩阵相乘实例
Dec 27 Python
PyTorch中的C++扩展实现
Apr 02 Python
解决pyqt5异常退出无提示信息的问题
Apr 08 Python
Django 构建模板form表单的两种方法
Jun 14 Python
完美解决TensorFlow和Keras大数据量内存溢出的问题
Jul 03 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
根德Grundig S400/S500/S700电路分析
2021/03/02 无线电
PHP中防止直接访问或查看或下载config.php文件的方法
2012/07/07 PHP
PHP多维数组遍历方法(2种实现方法)
2015/12/10 PHP
yii2 数据库读写分离配置示例
2017/02/10 PHP
JavaScript中字符串分割函数split用法实例
2015/04/07 Javascript
使用C++为node.js写扩展模块
2015/04/22 Javascript
jQuery中hover与mouseover和mouseout的区别分析
2015/12/24 Javascript
基于jquery实现简单的分页控件
2016/03/17 Javascript
基于JS实现EOS隐藏错误提示层代码
2016/04/25 Javascript
微信JS接口大全
2016/08/25 Javascript
js基本算法:冒泡排序,二分查找的简单实例
2016/10/08 Javascript
Node.js的环境安装配置(使用nvm方式)
2016/10/11 Javascript
jquery checkbox的相关操作总结
2016/10/17 Javascript
Jquery AJAX POST与GET之间的区别详细介绍
2016/10/17 Javascript
微信小程序  生命周期详解
2016/10/27 Javascript
vue日期组件 支持vue1.0和2.0
2017/01/09 Javascript
深入浅析JavaScript中的RegExp对象
2017/09/18 Javascript
详解NODEJS基于FFMPEG视频推流测试
2017/11/17 NodeJs
Jquery遍历筛选数组的几种方法和遍历解析json对象,Map()方法详解以及数组中查询某值是否存在
2019/01/18 jQuery
JavaScript作用域链实例详解
2019/01/21 Javascript
Django模板导入母版继承和自定义返回Html片段过程解析
2019/09/18 Python
Python hashlib加密模块常用方法解析
2019/12/18 Python
Keras-多输入多输出实例(多任务)
2020/06/22 Python
利用CSS3的transform做的动态时钟效果
2011/09/21 HTML / CSS
英国最大的宠物商店:Pets at Home
2019/04/17 全球购物
万代美国官网:PREMIUM BANDAI USA
2020/09/11 全球购物
金融专业毕业生推荐信
2013/11/26 职场文书
五年级英语教学反思
2014/01/31 职场文书
公益广告语集锦
2014/03/13 职场文书
《从现在开始》教学反思
2014/04/15 职场文书
理想点亮人生演讲稿
2014/05/21 职场文书
政风行风建设责任书
2014/07/23 职场文书
单位推荐信范文
2015/03/27 职场文书
2015年妇幼保健工作总结
2015/05/19 职场文书
Nginx反向代理多个服务器的实现方法
2021/03/31 Servers
Java常用工具类汇总 附示例代码
2021/06/26 Java/Android