1. 程式人生 > >python爬蟲遇到驗證碼的處理方法(以爬取中國執行資訊公開網為例)

python爬蟲遇到驗證碼的處理方法(以爬取中國執行資訊公開網為例)

朋友們大家好,python爬蟲是在學習python時比較容易上手的學習方式,爬蟲的思路簡要以下幾點:

    1.獲取需要爬取頁面的網址,並且對網頁內容進行分析。(主要就原始碼討論,如果我們需要的內容沒有在原始碼出現,則需要進行抓包分析)

    2.找到我們需要爬取的內容時我們就要用正則表示式、beautifulsoup或者是xpath進行切割我們需要的欄位。

    3.將爬取到的內容進行儲存。

這是一般網站爬取的基本點,但是我們在遇到需要登陸或者註冊的網站時就會遇到驗證碼,驗證碼的出現就是為了區分人和機器,但是隨著現在人工智慧的發展,這種區分已經不明確了,在python中有PIL庫進行影象處理、機器學習也能更好地解決驗證碼的問題,下面就中國執行資訊公開網(http://shixin.court.gov.cn/)給大家進行詳細的講解。

咳咳   大家注意這一行字!!!!!!!!


一、事前準備

1.python版本    python2.7

2.系統版本   ubantu 16.04

3.所需庫  

    import urllib2
    import urllib
    from bs4 import BeautifulSoup 
    import time
    import re

    import requests(包含了我爬取"中國執行資訊公開網"的所有用到的庫)

使用了beautifulsoup+正則表示式的方式提取我需要的內容

二、檢視網站

推薦使用谷歌瀏覽器     使用F12鍵進入前端除錯(有的電腦需要使用Fn+F12  比如我的)

火狐瀏覽器顯示我實在不喜歡,所以推薦使用谷歌的  有喜歡火狐的童鞋們也可以使用


點選進來我們可以看到一個類似於登陸和註冊的查詢表單,我們通過檢視頁面原始碼可以發現下圖這樣的資訊



可以發現我們需要的是pname、pcardname、pprovince以及我們需要的驗證碼pcode。然後我就想直接寫入這些內容直接post響應,然後我發現以下:


出現錯誤,點開F12可以發現post響應中提交了五個資訊:


那麼最後一個資訊我們應該能夠猜到是和驗證碼繫結的id號(比如說我們兩個人同時登陸網站,同時提交了驗證碼然後進行驗證,系統如何知道我們提交的驗證碼是否正確,就是通過繫結的id號來識別)所以我們現在的工作就是要找到對應的id號。

三、找到對應的id號


這就是一個單獨版的表單嘛!!檢視原始碼:


能夠看到:

onclick="this.src='captchaNew.do?captchaId=39821d3fd1a2470ca658bb18585093fc&random=0.4984367166835566'"

能夠發現id號隱藏在其中的,還有random號,共同組成了我們需要檢視的網頁(感覺就是把原網頁進行了分割)

這一部分程式碼的實現我是採用了beautifulsoup實現的,程式碼如下:

url="http://shixin.court.gov.cn/index_new_form.do"
data=requests.get(url).content.encode('utf-8')
soup=BeautifulSoup(data,'lxml')
captchaId = soup.find('img',attrs={'id':'captchaImg'})['src'].split('?')[1].split('&')[0].split('=')[1]

四、識別驗證碼

扯了這麼多現在回到我們最原始的問題,那麼我們應該怎麼去識別驗證碼呢?

首先我們要了解python的影象識別庫 python image libary,叫做PIL庫

1.首先要安裝pil庫,pip install pil不能安裝的童鞋可以參考這篇文章

2. pytesser(識別驗證碼的庫,需要使用Tesseract這個開源專案) 

3.Tesseract

全部安裝完成後我們就要對網站的驗證碼進行檢視

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
import urllib2
import urllib
import time
import re
reload(sys)
sys.setdefaultencoding('utf8')  

headers=("User-Agent",
"Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/60.0")
opener=urllib2.build_opener()
opener.addheaders=[headers]
urllib2.install_opener(opener)

for i in range(1,1500):
	url="http://shixin.court.gov.cn/captchaNew.do?captchaId=6eb54a8c64f84a84b6490db24671c310&random="+str(i)
	data=urllib2.urlopen(url).read()
	#data=urllib2.quote(data).decode('utf-8')
	file="/home/yang/png/test/"+str(i)+".png"
	playFile = open(file, 'wb')
	playFile.write(data)
	playFile.close()
	time.sleep(1)

通過這部分程式碼可以在執行資訊公開網上下載驗證碼,我們可以發現,其實驗證碼就這幾種:

         

1.第一種看似很簡單,但能夠看到它整體有一種毛糙感,(渾身都是噪點的感覺,具體也不是專業做這方面的,對這塊也不熟悉,反正這個驗證碼還不能解決,希望和大大們一起探討探討)

2.這種驗證碼含有干擾線,但我們能夠發現顏色佔主體的還是四個字元,所以我們可以通過獲取顏色最多的四種顏色來去除干擾線

3.第三種就十分簡單了,整體扭曲的程度也不大,可以直接使用pytesseract.image_to_string()來得到

五、對驗證碼的處理

對驗證碼的處理大致分為:去除噪點、去除干擾線(如果存在干擾線)、二值化(生成灰度圖)

1.去除噪點,直接上程式碼:

def fall (img):#img:圖片地址 
    white = (255,255,255,255)
    black = (0,0,0,255)
    #img = Image.open('/home/yang/png/0.png') # 讀入圖片
    pixdata = img.load()
    X = img.size[0]-1#因為我校的驗證碼二值化後正好剩下一圈寬度為一畫素的白邊,所以這麼處理了
    Y = img.size[1]-1

    def icolor(RGBA):
        if RGBA == white:
            return(1)
        else:
            return(0)

    for y in range(Y):
        for x in range(X):
            if (x<1 or y<1):
                pass
            else:
                if icolor(pixdata[x,y]) == 1:
                    pass
                else:
                    if (
                         icolor(pixdata[x+1,y])+
                         icolor(pixdata[x,y+1])+
                         icolor(pixdata[x-1,y])+
                         icolor(pixdata[x,y-1])+
                         icolor(pixdata[x-1,y-1])+
                         icolor(pixdata[x+1,y-1])+
                         icolor(pixdata[x-1,y+1])+
                         icolor(pixdata[x+1,y+1])
                         )>6: 
                         #如果一個黑色畫素周圍的8個畫素中白色畫素數量大於5個,則判斷其為噪點,填充為白色
                        pixdata[x,y] = white 
    #填充白點       
    for y in range(Y):
        for x in range(X):
            if (x<1 or y<1):
                pass
            else:
                if icolor(pixdata[x,y]) == 0:
                    pass
                else:
                    if (
                         (icolor(pixdata[x+1,y]))+
                         (icolor(pixdata[x,y+1]))+
                         (icolor(pixdata[x-1,y]))+
                         (icolor(pixdata[x,y-1]))
                         )<2:
                         #如果一個白色畫素上下左右4個畫素中黑色畫素的個數大於2個,則判定其為有效畫素,填充為黑色。
                        pixdata[x,y] = black
    #二次去除黑點   
    for y in range(Y):
        for x in range(X):
            if (x<1 or y<1):
                pass
            else:
                if icolor(pixdata[x,y]) == 1:
                    pass
                else:
                    if (
                         icolor(pixdata[x+1,y])+
                         icolor(pixdata[x,y+1])+
                         icolor(pixdata[x-1,y])+
                         icolor(pixdata[x,y-1])
                         )>2:
                        pixdata[x,y] = white
    img.save('儲存地址')

2.去除干擾線

def ganraoxian(img):#img:圖片地址
    #img = Image.open('/home/yang/png/'+str(i)+'.png') # 讀入圖片
    width = img.size[0]
    heigth = img.size[1]  #獲取長寬
    smap={}
    slist=[]
    keylist=[]
    for i in range(0,width):
        for j in range(0,heigth):
            argb = img.getpixel((i,j))
            r = argb[0]
            g = argb[1]
            b = argb[2]
            sum = r + g + b   #得到每一點的rgb
        
            if sum not in smap.keys():  #如果沒有該sum值的點  進行新增  並且給值為1
                smap[sum]=1
            else:
                num=smap[sum]       
                smap[sum]=num+1  #如果有了這個值  在原基礎上+1
    slist=sorted(smap.items(),key=lambda x:x[1],reverse = False)

    if (len(slist) > 4):
        num1 = slist[len(slist) - 5][1]
        num2 = slist[len(slist) - 4][1]
        num3 = slist[len(slist) - 3][1]
        num4 = slist[len(slist) - 2][1]     #獲取畫素點最多的四個點

        for key in smap:
            if smap[key] == num1 or smap[key] == num2 or smap[key] == num3 or smap[key] == num4 :
            
            #if num1 in smap or num2 in smap or num3 in smap or num4 in smap :
                keylist.append(key)      #找到對應顏色的點

    for x in range(0,width):
        for y in range(0,heigth):
            argb = img.getpixel((x,y))
            r = argb[0]
            g = argb[1]
            b = argb[2]
            ssum = r + g + b 
            flag = True
            for i in range(1,3): #px+1
                if y + i < heigth and y - i > 0 and x - i > 0 and x + i < width:

                    upargb = img.getpixel((x,y-i))
                    endargb = img.getpixel((x,y+i))
                    rightupargb = img.getpixel((x+i,y+i))
                    leftupargb = img.getpixel((x-i,y+i))
                    leftdownargb = img.getpixel((x-i,y-i))
                    rightdownargb = img.getpixel((x+i,y-i))
                    r1 = upargb[0]
                    g1 = upargb[1]
                    b1 = upargb[2]
                    sum1 = r1 + g1 + b1

                    r2 = endargb[0]
                    g2 = endargb[1]
                    b2 = endargb[2]
                    sum2 = r2 + g2 + b2

                    r3 = rightupargb[0]
                    g3 = rightupargb[1]
                    b3 = rightupargb[2]
                    sum3 = r3 + g3 + b3

                    r4 = leftupargb[0]
                    g4 = leftupargb[1]
                    b4 = leftupargb[2]
                    sum4 = r4 + g4 + b4

                    r5 = leftdownargb[0]
                    g5 = leftdownargb[1]
                    b5 = leftdownargb[2]
                    sum5 = r5 + g5 + b5

                    r6 = rightdownargb[0]
                    g6 = rightdownargb[1]
                    b6 = rightdownargb[2]
                    sum6 = r6 + g6 + b6
                    if sum1 in keylist or sum2 in keylist or sum3 in keylist or sum4 in keylist or sum5 in keylist or sum6 in keylist:
                        flag = False
            if (ssum not in keylist and flag):
                img.putpixel((x,y),(255,255,255))
    for x in range(0,width):
        for y in range(0,heigth):    
            if img.getpixel((x,y))==(255,255,255,255):
                continue
            else:
                img.putpixel((x,y),(0,0,0,255))
                        #curImg.setRGB(x, y, Color.white.getRGB())  
    img.save('儲存地址‘) 

得到了的結果是

          

我們會發現雖然去掉了干擾線,但是對應的有很多地方會缺失內容,會導致不能直接使用pytesseract.image_to_string()直接進行讀取,就必須採用機器學習的方式來讀取(期待有更好更簡單的方法可憐可憐可憐

注意:這裡因為是通過不同的顏色進行區分,所以我們必須在二值化之前進行去除干擾線的操作。另外一種新增干擾線的方式是新增多條較細的線條,這裡可以根據線條的寬度進行區別實現去除操作。

3.二值化處理

def two(img): #img:圖片地址 
    i = 0
    #img = Image.open('/home/yang/png/0.png') # 讀入圖片
    img = img.convert("RGBA")
    while i < 4:#迴圈次數視情況進行調整
        i = i+1
        pixdata = img.load()
        #一次二值化
        
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][0] < 90:#使RGB值中R小於90的畫素點變成純黑
                    pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][1] < 190:#使RGB值中G小於90的畫素點變成純黑
                        pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][2] > 0:#使RGB值中B大於0的畫素點變成純白
                    pixdata[x, y] = (255, 255, 255, 255)
      
              
    #理論上的二值化程式碼只有上面那些,RGB值的調整閾值需要針對不同驗證碼反覆調整。同時實際中一組閾值往往沒法做到完美,後面的部分是視實際情況新增的類似部分
  

    #二次二值化(除去某些R、G、B值接近255的顏色)                 
    for y in range(img.size[1]):
        for x in range(img.size[0]):
            if pixdata[x, y][0] < 254:
                pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][1] < 254:
                    pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][2] > 0:
                    pixdata[x, y] = (255, 255, 255, 255)

    #三次二值化,懟掉純黃色(實際使用中發現很多圖片最後剩幾個純黃色的畫素點)               
    for y in range(img.size[1]):
        for x in range(img.size[0]):
            if pixdata[x, y] ==(255,255,0,255):
                pixdata[x, y] = (0, 0, 0, 255)

    img.save('儲存地址')

使影象變為黑白方便讀取


在這裡可以看出來,如果要去掉干擾線,必須要在二值化之前進行。

六、讀取驗證碼上的資訊

首先提供一種簡便的方法:

info = Image.open("圖片的路徑")
image_info = pytesseract.image_to_string(info)
print image_info

沒錯,就這麼三行程式碼,就能讀取驗證碼上的資訊,但是這種方法只能夠識別沒有扭曲、沒有變形和沒有干擾線不粘連的驗證碼。


能夠看出來讀取的效率的確很低,經過變形的驗證碼幾乎不能實現,對於扭曲變形的驗證碼,我查閱了很多資料,但是還是沒有什麼解決辦法,希望能夠和相關大牛進行交流交流。

七、利用機器學習進行識別驗證碼(這一部分內容我是根據這篇文章學習的來的,對我幫助很大)

首先我們要解決大小寫字母和數字一個62個矩陣和本身的轉換

#coding: utf-8
import os
import random
from PIL import Image
import numpy as np
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import random
import sys
reload(sys)
sys.setdefaultencoding('utf8')

IMAGE_HEIGHT = 70
IMAGE_WIDTH = 160
MAX_CAPTCHA = 4
CHAR_SET_LEN = 62

def get_name_and_image():
	all_image = os.listdir('/home/yg/study/yanzhengjixiexuexi/captch/images')
	random_file = random.randint(0, 489)
	print random_file
	base = os.path.basename('/home/yg/study/yanzhengjixiexuexi/captch/images/' + all_image[random_file])
	name = os.path.splitext(base)[0] #將檔名和字尾分開
	print name,base
	image = Image.open('/home/yg/study/yanzhengjixiexuexi/captch/images/' + all_image[random_file])
	image = np.array(image)
	print image[35]
	return name, image

#print get_name_and_image()


def name2vec(name):
	vector = np.zeros(MAX_CAPTCHA*CHAR_SET_LEN)
	print len(vector)
	for i, c in enumerate(name):
		print i,c,ord(c)    #大小寫字母和數字分別進行矩陣的轉換
		if ord(c)>58 and ord(c)<=90:
			idx = i * 62 + ord(c) - 65
			vector[idx] = 1
		elif ord(c)>=97 and ord(c)<=122:
			idx = i * 62 + ord(c) - 71
			vector[idx] = 1
		else:
			idx = i * 62 + ord(c) + 4
			vector[idx] = 1
	return vector
print name2vec('AZaz')[0]
print name2vec('az09')[0]

def vec2name(vec):
	x=0
	name=[]
	for one_vec in vec:
		if one_vec==1:
			if x<=25:
				name.append(chr(x+65))
			elif x<=51:
				name.append(chr(x-26+97))
			else:
				name.append(chr(x-52+49))	
		x+=1
		if x>=62:
			x=0
	
	return "".join(name)
	
print vec2name(name2vec('AZaz'))

得到的結果為


基本就根據https://www.jianshu.com/p/26ff7b9075a1?from=timeline該網頁方法完成,總程式碼如下:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from PIL import Image
import numpy as np
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import random
import sys
reload(sys)
sys.setdefaultencoding('utf8')


IMAGE_HEIGHT = 114
IMAGE_WIDTH = 450
MAX_CAPTCHA = 6
CHAR_SET_LEN = 26

'''
IMAGE_HEIGHT = 70
IMAGE_WIDTH = 160
MAX_CAPTCHA = 4
CHAR_SET_LEN = 36

def get_name_and_image():
    all_image = os.listdir('/home/yang/captch/images/')
    random_file = random.randint(0, 3429)
    base = os.path.basename('/home/yang/captch/images/' + all_image[random_file])
    name = os.path.splitext(base)[0]
    image = Image.open('/home/yang/captch/images/' + all_image[random_file])
    image = np.array(image)
    return name, image
'''

def get_name_and_image():
    all_image = os.listdir('/home/yang/桌面/captcha4/')
    random_file = random.randint(0, 3429)
    base = os.path.basename('/home/yang/桌面/captcha4/' + all_image[random_file])
    name = os.path.splitext(base)[0] #將檔名和字尾分開
    image = Image.open('/home/yang/桌面/captcha4/' + all_image[random_file])
    image = np.array(image)
    return name, image



def name2vec(name):
vector = np.zeros(MAX_CAPTCHA*CHAR_SET_LEN)
print len(vector)
for i, c in enumerate(name):
print i,c,ord(c)
if ord(c)>58 and ord(c)<=90:
idx = i * 62 + ord(c) - 65
vector[idx] = 1
elif ord(c)>=97 and ord(c)<=122:
idx = i * 62 + ord(c) - 71
vector[idx] = 1
else:
idx = i * 62 + ord(c) + 4
vector[idx] = 1
return vector
print name2vec('AZaz')[0]
print name2vec('az09')[0]


def vec2name(vec):
x=0
name=[]
for one_vec in vec:
if one_vec==1:
if x<=25:
name.append(chr(x+65))
elif x<=51:
name.append(chr(x-26+97))
else:
name.append(chr(x-52+49))
x+=1
if x>=62:
x=0

return "".join(name)

# 生成一個訓練batch
def get_next_batch(batch_size=64):
    batch_x = np.zeros([batch_size, IMAGE_HEIGHT*IMAGE_WIDTH])
    batch_y = np.zeros([batch_size, MAX_CAPTCHA*CHAR_SET_LEN])

    for i in range(batch_size):
        name, image = get_name_and_image()
        batch_x[i, :] = 1*(image.flatten())
        batch_y[i, :] = name2vec(name)
    return batch_x, batch_y

####################################################

X = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT*IMAGE_WIDTH])
Y = tf.placeholder(tf.float32, [None, MAX_CAPTCHA*CHAR_SET_LEN])
keep_prob = tf.placeholder(tf.float32)


# 定義CNN
def crack_captcha_cnn(w_alpha=0.01, b_alpha=0.1):
    x = tf.reshape(X, shape=[-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1])
    # 3 conv layer
    w_c1 = tf.Variable(w_alpha * tf.random_normal([5, 5, 1, 32]))
    b_c1 = tf.Variable(b_alpha * tf.random_normal([32]))
    conv1 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, w_c1, strides=[1, 1, 1, 1], padding='SAME'), b_c1))
    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv1 = tf.nn.dropout(conv1, keep_prob)

    w_c2 = tf.Variable(w_alpha * tf.random_normal([5, 5, 32, 64]))
    b_c2 = tf.Variable(b_alpha * tf.random_normal([64]))
    conv2 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv1, w_c2, strides=[1, 1, 1, 1], padding='SAME'), b_c2))
    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv2 = tf.nn.dropout(conv2, keep_prob)

    w_c3 = tf.Variable(w_alpha * tf.random_normal([5, 5, 64, 64]))
    b_c3 = tf.Variable(b_alpha * tf.random_normal([64]))
    conv3 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv2, w_c3, strides=[1, 1, 1, 1], padding='SAME'), b_c3))
    conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv3 = tf.nn.dropout(conv3, keep_prob)

    # Fully connected layer
    w_d = tf.Variable(w_alpha * tf.random_normal([15 * 57 * 64, 1024]))
    b_d = tf.Variable(b_alpha * tf.random_normal([1024]))
    dense = tf.reshape(conv3, [-1, w_d.get_shape().as_list()[0]])
    dense = tf.nn.relu(tf.add(tf.matmul(dense, w_d), b_d))
    dense = tf.nn.dropout(dense, keep_prob)

    w_out = tf.Variable(w_alpha * tf.random_normal([1024, MAX_CAPTCHA * CHAR_SET_LEN]))
    b_out = tf.Variable(b_alpha * tf.random_normal([MAX_CAPTCHA * CHAR_SET_LEN]))
    out = tf.add(tf.matmul(dense, w_out), b_out)
    return out


# 訓練
def train_crack_captcha_cnn():
    output = crack_captcha_cnn()
    loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=output, labels=Y))
    optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)

    predict = tf.reshape(output, [-1, MAX_CAPTCHA, CHAR_SET_LEN])
    max_idx_p = tf.argmax(predict, 2)
    max_idx_l = tf.argmax(tf.reshape(Y, [-1, MAX_CAPTCHA, CHAR_SET_LEN]), 2)
    correct_pred = tf.equal(max_idx_p, max_idx_l)
    accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

    saver = tf.train.Saver()
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())

        step = 0
        while True:
            batch_x, batch_y = get_next_batch(64)
            _, loss_ = sess.run([optimizer, loss], feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.5})
            print(step, loss_)

            # 每100 step計算一次準確率
            if step % 100 == 0:
                batch_x_test, batch_y_test = get_next_batch(100)
                acc = sess.run(accuracy, feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1.})
                print("steps=%d, accuracy=%f" % (step, acc))
                # 如果準確率大於50%,儲存模型,完成訓練
                if acc > 0.99:
                    saver.save(sess, "./crack_capcha.model", global_step=step)
                    break

            step += 1

train_crack_captcha_cnn()

#訓練完成後#掉train_crack_captcha_cnn(),取消下面的註釋,開始預測,注意更改預測集目錄

# def crack_captcha():
#     output = crack_captcha_cnn()
#
#     saver = tf.train.Saver()
#     with tf.Session() as sess:
#         saver.restore(sess, tf.train.latest_checkpoint('.'))
#         n = 1
#         while n <= 10:
#             text, image = get_name_and_image()
#             image = 1 * (image.flatten())
#             predict = tf.argmax(tf.reshape(output, [-1, MAX_CAPTCHA, CHAR_SET_LEN]), 2)
#             text_list = sess.run(predict, feed_dict={X: [image], keep_prob: 1})
#             vec = text_list[0].tolist()
#             predict_text = vec2name(vec)
#             print("正確: {}  預測: {}".format(text, predict_text))
#             n += 1
#
# crack_captcha()

附上獲取驗證碼的全部程式碼

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import pytesseract
from PIL import Image
import sys
import re
reload(sys)
sys.setdefaultencoding('utf8')

###############################################
###############################################
##########去除干擾線
def ganraoxian(img,k):
   
    width = img.size[0]
    heigth = img.size[1]  #獲取長寬
    smap={}
    slist=[]
    keylist=[]
    for i in range(0,width):
        for j in range(0,heigth):
            argb = img.getpixel((i,j))
            r = argb[0]
            g = argb[1]
            b = argb[2]
            sum = r + g + b   #得到每一點的rgb
        
            if sum not in smap.keys():  #如果沒有該sum值的點  進行新增  並且給值為1
                smap[sum]=1
            else:
                num=smap[sum]       
                smap[sum]=num+1  #如果有了這個值  在原基礎上+1
    slist=sorted(smap.items(),key=lambda x:x[1],reverse = False)

    if (len(slist) > 4):
        num1 = slist[len(slist) - 5][1]
        num2 = slist[len(slist) - 4][1]
        num3 = slist[len(slist) - 3][1]
        num4 = slist[len(slist) - 2][1]     #獲取畫素點最多的四個點

        for key in smap:
            if smap[key] == num1 or smap[key] == num2 or smap[key] == num3 or smap[key] == num4 :
            
            #if num1 in smap or num2 in smap or num3 in smap or num4 in smap :
                keylist.append(key)      #找到對應顏色的點

    for x in range(0,width):
        for y in range(0,heigth):
            argb = img.getpixel((x,y))
            r = argb[0]
            g = argb[1]
            b = argb[2]
            ssum = r + g + b 
            flag = True
            for i in range(1,3): #px+1
                if y + i < heigth and y - i > 0 and x - i > 0 and x + i < width:

                    upargb = img.getpixel((x,y-i))
                    endargb = img.getpixel((x,y+i))
                    rightupargb = img.getpixel((x+i,y+i))
                    leftupargb = img.getpixel((x-i,y+i))
                    leftdownargb = img.getpixel((x-i,y-i))
                    rightdownargb = img.getpixel((x+i,y-i))
                    r1 = upargb[0]
                    g1 = upargb[1]
                    b1 = upargb[2]
                    sum1 = r1 + g1 + b1

                    r2 = endargb[0]
                    g2 = endargb[1]
                    b2 = endargb[2]
                    sum2 = r2 + g2 + b2

                    r3 = rightupargb[0]
                    g3 = rightupargb[1]
                    b3 = rightupargb[2]
                    sum3 = r3 + g3 + b3

                    r4 = leftupargb[0]
                    g4 = leftupargb[1]
                    b4 = leftupargb[2]
                    sum4 = r4 + g4 + b4

                    r5 = leftdownargb[0]
                    g5 = leftdownargb[1]
                    b5 = leftdownargb[2]
                    sum5 = r5 + g5 + b5

                    r6 = rightdownargb[0]
                    g6 = rightdownargb[1]
                    b6 = rightdownargb[2]
                    sum6 = r6 + g6 + b6
                    if sum1 in keylist or sum2 in keylist or sum3 in keylist or sum4 in keylist or sum5 in keylist or sum6 in keylist:
                        flag = False
            if (ssum not in keylist and flag):
                img.putpixel((x,y),(255,255,255))
    for x in range(0,width):
        for y in range(0,heigth):    
            if img.getpixel((x,y))==(255,255,255,255):
                continue
            else:
                img.putpixel((x,y),(0,0,0,255))
                        #curImg.setRGB(x, y, Color.white.getRGB())  
    img.save('圖片地址') 
        
'''
#############################################
#############################################
########讀取驗證碼
def readcCode():
    try:
        img = Image.open('圖片地址')
        text = pytesseract.image_to_string (img)
        text = text.replace(' ', '')
        if text == "":#如果識別結果為空,則識別失敗
            tip = False

        if re.search(r'[0-9a-zA-Z]{4}',text):
            pass
        else:
            tip = False#如果識別結果中出現了了字母數字之外的字元,則識別失敗
        if len(text) !=4:
            tip = False#如果識別結果不足四位(因為有部分字元粘連的驗證碼),則識別失敗

    except UnicodeDecodeError as e:
        tip = False #如果報字元編碼錯誤,則識別失敗,需要捕捉錯誤


    if tip == False:
        #識別失敗
        return (readcCode())#如果識別失敗,迭代、重新識別(實際使用中需要呼叫驗證碼獲取函式重新獲取驗證碼)
    else:
        return(text)
             
readcCode()
'''

##################################################
##################################################
###################二值化
def two(img,j):
    i = 0
    
    img = img.convert("RGBA")
    while i < 4:#迴圈次數視情況進行調整
        i = i+1
        pixdata = img.load()
        #一次二值化
        
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][0] < 90:#使RGB值中R小於90的畫素點變成純黑
                    pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][1] < 190:#使RGB值中G小於90的畫素點變成純黑
                        pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][2] > 0:#使RGB值中B大於0的畫素點變成純白
                    pixdata[x, y] = (255, 255, 255, 255)
      
              
    #理論上的二值化程式碼只有上面那些,RGB值的調整閾值需要針對不同驗證碼反覆調整。同時實際中一組閾值往往沒法做到完美,後面的部分是視實際情況新增的類似部分
  

    #二次二值化(除去某些R、G、B值接近255的顏色)                 
    for y in range(img.size[1]):
        for x in range(img.size[0]):
            if pixdata[x, y][0] < 254:
                pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][1] < 254:
                    pixdata[x, y] = (0, 0, 0, 255)
        for y in range(img.size[1]):
            for x in range(img.size[0]):
                if pixdata[x, y][2] > 0:
                    pixdata[x, y] = (255, 255, 255, 255)

    #三次二值化,懟掉純黃色(實際使用中發現很多圖片最後剩幾個純黃色的畫素點)               
    for y in range(img.size[1]):
        for x in range(img.size[0]):
            if pixdata[x, y] ==(255,255,0,255):
                pixdata[x, y] = (0, 0, 0, 255)

    img.save(’圖片地址‘)

####################################################
####################################################
########################去噪
#一次清除黑點
def fall (img,j):
    white = (255,255,255,255)
    black = (0,0,0,255)
    pixdata = img.load()
    X = img.size[0]-1#因為我校的驗證碼二值化後正好剩下一圈寬度為一畫素的白邊,所以這麼處理了
    Y = img.size[1]-1

    def icolor(RGBA):
        if RGBA == white:
            return(1)
        else:
            return(0)

    for y in range(Y):
        for x in range(X):
            if (x<1 or y<1):
                pass
            else:
                if icolor(pixdata[x,y]) == 1:
                    pass
                else:
                    if (
                         icolor(pixdata[x+1,y])+
                         icolor(pixdata[x,y+1])+
                         icolor(pixdata[x-1,y])+
                         icolor(pixdata[x,y-1])+
                         icolor(pixdata[x-1,y-1])+
                         icolor(pixdata[x+1,y-1])+
                         icolor(pixdata[x-1,y+1])+
                         icolor(pixdata[x+1,y+1])
                         )>6: 
                         #如果一個黑色畫素周圍的8個畫素中白色畫素數量大於5個,則判斷其為噪點,填充為白色
                        pixdata[x,y] = white 
    #填充白點       
    for y in range(Y):
        for x in range(X):
            if (x<1 or y<1):
                pass
            else:
                if icolor(pixdata[x,y]) == 0:
                    pass
                else:
                    if (
                         (icolor(pixdata[x+1,y]))+
                         (icolor(pixdata[x,y+1]))+
                         (icolor(pixdata[x-1,y]))+
                         (icolor(pixdata[x,y-1]))
                         )<2:
                         #如果一個白色畫素上下左右4個畫素中黑色畫素的個數大於2個,則判定其為有效畫素,填充為黑色。
                        pixdata[x,y] = black
    #二次去除黑點   
    for y in range(Y):
        for x in range(X):
            if (x<1 or y<1):
                pass
            else:
                if icolor(pixdata[x,y]) == 1:
                    pass
                else:
                    if (
                         icolor(pixdata[x+1,y])+
                         icolor(pixdata[x,y+1])+
                         icolor(pixdata[x-1,y])+
                         icolor(pixdata[x,y-1])
                         )>2:
                        pixdata[x,y] = white
    img.save('圖片地址')

#########################################################
#########################################################
#####
for i in range (541,542):
    im = Image.open('圖片地址') # 讀入圖片
    fall(im,i)
    ganraoxian(im,i)
    #time.sleep(1)
    two(im,i)
'''
info = Image.open("/home/yang/png/elx6.png")
image_info = pytesseract.image_to_string(info)
print image_info
'''

進行到這裡基本就完成了驗證碼的識別,作為快進入大四的學生,越學習相關知識越發現自己還有很多東西要學習,這篇部落格是我第一篇部落格,存在很多不足的地方,希望大家指正。最後感謝小魚乾幫我修改羨慕羨慕羨慕

最後附上直接執行就能獲取驗證碼的程式碼:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
import urllib2
import urllib
import time
import re
reload(sys)
sys.setdefaultencoding('utf8')  

headers=("User-Agent",
"Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/60.0")
opener=urllib2.build_opener()
opener.addheaders=[headers]
urllib2.install_opener(opener)

for i in range(659,1500):
	url="http://shixin.court.gov.cn/captchaNew.do?captchaId=6eb54a8c64f84a84b6490db24671c310&random="+str(i)
	data=urllib2.urlopen(url).read()
	#data=urllib2.quote(data).decode('utf-8')
	file="/home/yang/png/test/"+str(i)+".png"
	playFile = open(file, 'wb')
	playFile.write(data)
	playFile.close()
	time.sleep(1)