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的MongoDB模块PyMongo操作方法集锦
Jan 05 Python
深入解析Python编程中super关键字的用法
Jun 24 Python
Python中super()函数简介及用法分享
Jul 11 Python
python3制作捧腹网段子页爬虫
Feb 12 Python
插入排序_Python与PHP的实现版(推荐)
May 11 Python
django模板语法学习之include示例详解
Dec 17 Python
Python扩展内置类型详解
Mar 26 Python
python 请求服务器的实现代码(http请求和https请求)
May 25 Python
python实现Dijkstra算法的最短路径问题
Jun 21 Python
python的几种矩阵相乘的公式详解
Jul 10 Python
wxPython实现带颜色的进度条
Nov 19 Python
Python的几种主动结束程序方式
Nov 22 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生成自己的LOG文件
2006/10/09 PHP
树型结构列出指定目录里所有文件的PHP类
2006/10/09 PHP
php json_encode奇怪问题说明
2011/09/27 PHP
php获取一个变量的名字的方法
2014/09/05 PHP
php操作xml入门之xml标签的属性分析
2015/01/23 PHP
php生成不重复随机数、数组的4种方法分享
2015/03/30 PHP
thinkphp中多表查询中防止数据重复的sql语句(必看)
2016/09/22 PHP
Thinkphp 中 distinct 的用法解析
2016/12/14 PHP
php数组指针操作详解
2017/02/14 PHP
PHP不使用内置函数实现字符串转整型的方法示例
2017/07/03 PHP
PHP XML Expat解析器知识点总结
2019/02/15 PHP
js 中 document.createEvent的用法
2010/08/29 Javascript
javascript开发随笔一 preventDefault的必要
2011/11/25 Javascript
javascript基础知识大全 便于大家学习,也便于我自己查看
2012/08/17 Javascript
Jquery日期选择datepicker插件用法实例分析
2015/06/08 Javascript
在ASP.NET MVC项目中使用RequireJS库的用法示例
2016/02/15 Javascript
微信小程序实现图片轮播及文件上传
2017/04/07 Javascript
vue使用v-if v-show页面闪烁,div闪现的解决方法
2018/10/12 Javascript
如何利用vue+vue-router+elementUI实现简易通讯录
2019/05/13 Javascript
Vue项目总结之webpack常规打包优化方案
2019/06/06 Javascript
Jquery异步上传文件代码实例
2019/11/13 jQuery
原生js实现九宫格拖拽换位
2021/01/26 Javascript
[06:11]2014DOTA2国际邀请赛 专访团结一心的VG战队
2014/07/21 DOTA
[36:41]完美世界DOTA2联赛循环赛FTD vs Magma第一场 10月30日
2020/10/31 DOTA
浅谈django开发者模式中的autoreload是如何实现的
2017/08/18 Python
python获取代理IP的实例分享
2018/05/07 Python
python语音识别实践之百度语音API
2018/08/30 Python
Python将一个Excel拆分为多个Excel
2018/11/07 Python
详解pandas绘制矩阵散点图(scatter_matrix)的方法
2020/04/23 Python
Linux系统下升级pip的完整步骤
2021/01/31 Python
巧用CSS3的calc()宽度计算做响应模式布局的方法
2018/03/22 HTML / CSS
Java面向对象面试题
2016/12/26 面试题
2014年作风建设工作总结
2014/10/29 职场文书
《我在为谁工作》:工作的质量往往决定生活的质量
2019/12/27 职场文书
什么是动态刷新率DRR? Windows11动态刷新率功能介绍
2021/11/21 数码科技
mysql使用instr达到in(字符串)的效果
2022/04/03 MySQL