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)