项目背景:
之所以做这个其实很久就有这个想法了,因为搞爬虫有大半年了,怕热很多网站,也模拟登陆了许多网站,包括知乎微博等,但是有个问题是,当我们遇到验证码的时候,就需要人工打验证码了,特别是用selenium登录新浪手机端的时候,每次都需要人为打验证码,感觉还不够全自动。所以就有了这个摸索。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第一步:(爬取验证码)
我们首先需要获取足够的验证码样本,因为从简单做起,所以本次的验证码都是由数字组成且都比较规则;http://smart.gzeis.edu.cn:8081/Content/AuthCode.aspx
#-*- coding:utf-8 -*- import requests import time # 文件下载,主要下载训练集 def download_pics(pic_name): url = 'http://smart.gzeis.edu.cn:8081/Content/AuthCode.aspx' res = requests.get(url,stream=True) with open(u'J:/数据分析学习/python/机器学习之验证码识别/pics/%s.jpg'%(pic_name),'wb') as f: for chunk in res.iter_content(chunk_size=1024): if chunk: f.write(chunk) f.flush() f.close() if __name__ == '__main__': for i in xrange(100): pic_name = int(time.time()*1000000) download_pics(pic_name)
第二步:(切割验证码)
因为每个验证码是4个数字组成,我的思路是把验证码切割成四个,然后逐一识别,这样的准确率应该比较高一些;这里值得一提的就是:切割图片的参数(通过PS可以看到相隔的像素值,然后可以推算出一个公式)、将图片二值化,把图片变成黑白图片、我们需要读取某个文件夹下的所有图片,然后保存下来。
# -*- coding: utf-8 -*- from PIL import Image,ImageEnhance from PIL import * import time # 图片切割 def segment(im): s = 12 w = 40 h = 81 t = 0 im_new = [] for i in range(4): im1 = im.crop((s+w*i,t,s+w*(i+1),h)) im_new.append(im1) return im_new # 图片预处理,二值化,图片增强 def imgTransfer(f_name): im = Image.open(f_name) im = im.filter(ImageFilter.MedianFilter()) #enhancer = ImageEnhance.Contrast(im) #im = enhancer.enhancer(1) im = im.convert('L') return im def cutPictures(img): im = imgTransfer(img) pics = segment(im) for pic in pics: pic.save(u'J:/数据分析学习/python/机器学习之验证码识别/test/%s.jpeg'%(int(time.time()*1000000)),'jpeg') # 读取某文件夹下的所有图片 import os def getAllImages(folder): assert os.path.exists(folder) assert os.path.isdir(folder) imageList = os.listdir(folder) imageList = [os.path.abspath(item) for item in imageList if os.path.isfile(os.path.join(folder, item))] return imageList if __name__ == '__main__': files_name = getAllImages(u'J:/数据分析学习/python/机器学习之验证码识别/pics//') for i in files_name: #cutPictures() files = i.replace('\\','/') s = files.split('/') name = '' for j in s[:-1]: name = name + j + '/' name = name + 'pics/' + s[-1] cutPictures(name)
第三步:对图片预分类
这里值得一提的是,首先你得知道文件夹的命名不可以出现特殊符号(/ \ * ! | ? < >)等,而机器识别可能会把切割后图片识别成这些特殊字符,所以得加上一个判断;然后机器的识别正确率大概是50%,所有后面还需自己人工分类,变成准确的分类。(有个奇怪的现象,1200张图片,竟然没有一个9)
# -*- coding: utf-8 -*- """ Created on Thu Mar 23 14:19:13 2017 对切割后的图片进行分类,及0-9 @author: onlyyo """ import sys sys.path.append('C:\Users\onlyyo\Desktop\pytesseract-0.1.6\src') sys.path.append('C:\Python27\Lib\site-packages\pytesser') from pytesser import * from pytesseract import * import pytesseract from PIL import Image import os import shutil #ocr图像识别 def ocr(img): try: img = Image.open(img) rs = image_to_string(img) except: return 'none' return rs #使用ocr进行训练的预分类 def category(originfile,dirs,filename): if not os.path.exists(dirs): os.makedirs(dirs) shutil.copyfile(originfile,dirs+filename) if __name__ == '__main__': dirs = u'J:/数据分析学习/python/机器学习之验证码识别/test/' # 将ocr识别的文件按照数组编号存放在相应的文件夹中 for fr in os.listdir(dirs): f = dirs+fr if f.rfind(u'.DS_Store') == -1: rs = ocr(f) if '|' not in rs and '*' not in rs : if '?' not in rs and '<'not in rs and '>' not in rs: category(f,u'J:/数据分析学习/python/机器学习之验证码识别/category/%s/'%rs,fr)
第四步:(提取特征值)
批量将切割后并且已经分好类的图像,得到的图片进行二值化(0,1)处理,变成像素值,然后保存在TXT文件下;这里要注意的是保存的格式以及每一个图片的像素值后面要加上它的标签即是什么数字。为下一步的模型训练做准备。
# -*- coding: utf-8 -*- """ Created on Thu Mar 23 15:46:59 2017 @author: onlyyo 批量将切割后并且已经分好类的图像,得到的图片进行二值化处理,变成像素值,然后保存在TXT文件下 """ from PIL import Image import numpy as np import os # 特征提取,获取图像二值化数学值 def getBinaryPix(im): im = Image.open(im) img = np.array(im) rows,cols = img.shape for i in range(rows): for j in range(cols): if (img[i,j]<= 128): img[i,j] = 0 else: img[i,j] = 1 binpix = np.ravel(img) return binpix def getfiles(dirs): fs = [] for fr in os.listdir(dirs): f = dirs + fr if f.rfind(u'.DS_Store') == -1: fs.append(f) return fs def writeFile(content): with open(u'J:/数据分析学习/python/机器学习之验证码识别/traindata/train_data.txt','a+') as f: f.write(content) f.write('\n') f.close() if __name__ == '__main__': dirs = u'J:/数据分析学习/python/机器学习之验证码识别/category/%s/' for i in range(9): for f in getfiles(dirs %(i)): pixs = getBinaryPix(f).tolist() pixs.append(i) pixs = [str(i) for i in pixs] content = ','.join(pixs) writeFile(content) 第五步:(模型训练)使用sklearn中的SVM(支持向量机)对第四步得到的数据进行训练,SVM是有监督分类,通过调一些参数可以改善它的预测正确率,比如说核函数-rbf、poly、sigmoid、linear等;
# -*- coding: utf-8 -*- """ Created on Thu Mar 23 16:18:39 2017 @author: onlyyo 根据之前得到的验证码像素数组,以及他们的标签,用SVM对其进行模型训练 """ from sklearn.svm import SVC from sklearn import grid_search import numpy as np import numpy as np from sklearn import cross_validation as cs from sklearn.externals import joblib from picPreprocessing import loadPredict import warnings import time def load_data(): dataset = np.loadtxt(u'J:/数据分析学习/python/机器学习之验证码识别/traindata/train_data.txt',delimiter=',') return dataset # 交叉验证 def cross_validation(): dataset = load_data() row,col = dataset.shape X = dataset[:,:col-1] Y = dataset[:,-1] clf = SVC(kernel='rbf',C=1000) clf.fit(X,Y) scores = cs.cross_val_score(clf,X,Y,cv=5) print "Accuracy: %0.2f (+- %0.2f)" % (scores.mean(),scores.std()) return clf t0 = time.time() cross_validation() #print "fit time:",round(time.time()-t0,3),"s" def searchBestParameter(): parameters = {'kernel':('linear','poly','rbf','sigmoid'),'C':[1,100]} dataset = load_data() row,col = dataset.shape X = dataset[:,:col-1] Y = dataset[:,-1] svr = SVC() clf = grid_search.GridSearchCV(svr,parameters) clf.fit(X,Y) print clf.best_params_ #searchBestParameter() print "fit time:",round(time.time()-t0,3),"s" 第六步:预测验证码我们的预测模型出来了,接下来就是验证咯!
# -*- coding: utf-8 -*- """ Created on Thu Mar 23 17:20:09 2017 @author: onlyyo 最后一步,对于要测试的验证码处理,然后进行预测,输出结果 """ from split_pic import * from write_img import * import os from cross_svc import cross_validation def cutPictures2(name): im = imgTransfer(name) pics = segment(im) for pic in pics: pic.save(u'J:/数据分析学习/python/机器学习之验证码识别/test_picture/%s.jpeg'%(int(time.time()*1000000)),'jpeg') def load_Predict(name): # cutPictures2(name) #切割图片 dirs = u'J:/数据分析学习/python/机器学习之验证码识别/test_picture/' fs = os.listdir(dirs) # 获取图片名称 clf = cross_validation() predictValue = [] for fname in fs: fn = dirs + fname binpix = getBinaryPix(fn) predictValue.append(clf.predict(binpix)) predictValue = [str(int(i)) for i in predictValue] print "the picture number is :" ,"".join(predictValue) name = u'J:/数据分析学习/python/机器学习之验证码识别/8473.jpg' load_Predict(name)
总结:
做验证码识别的话,我觉得思路应该是这样的:
首选得获取足够的样本,
图片切割、图片二值化、降噪增强、
预分类,读取图片像素值即特征值以及标签;
模型训练--SVM、神经网络等。
