天天看點

Python爬蟲基礎:驗證碼的爬取和識别詳解

今天要給大家介紹的是驗證碼的爬取和識别,不過隻涉及到最簡單的圖形驗證碼,也是現在比較常見的一種類型。

運作平台:Windows

Python版本:Python3.6

IDE: Sublime Text

其他:Chrome浏覽器

簡述流程:

步驟1:簡單介紹驗證碼

步驟2:爬取少量驗證碼圖檔

步驟3:介紹百度文字識别OCR

步驟4:識别爬取的驗證碼

步驟5:簡單圖像處理

目前,很多網站會采取各種各樣的措施來反爬蟲,驗證碼就是其中一種,比如當檢測到通路頻率過高時會彈出驗證碼讓你輸入,确認通路網站的不是機器人。但随着爬蟲技術的發展,驗證碼的花樣也越來越多,從最開始簡單的幾個數字或字母構成的圖形驗證碼(也就是我們今天要涉及的)發展到需要點選倒立文字字母的、與文字相符合的圖檔的點觸型驗證碼,需要滑動到合适位置的極驗滑動驗證碼,以及計算題驗證碼等等,總之花樣百出,讓人頭秃。驗證碼其他的相關知識大家可以看下這個網站:

captcha.org

再來簡單說下圖形驗證碼吧,就像這張:
Python爬蟲基礎:驗證碼的爬取和識别詳解

由字母和數字組成,再加上一些噪點,但為了防止被識别,簡單的圖形驗證碼現在也變得複雜,有的加了幹擾線,有的加噪點,有的加上背景,字型扭曲、粘連、镂空、混用等等,甚至有時候人眼都難以識别,隻能默默點選“看不清,再來一張”。

驗證碼難度的提高随之帶來的就是識别的成本也需要提高,在接下來的識别過程中,我會先直接使用百度文字識别OCR,來測試識别準确度,再确認是否選擇轉灰階、二值化以及去幹擾等圖像操作優化識别率。

接下來我們就來爬取少量驗證碼圖檔存入檔案。

首先打開Chrome浏覽器,通路剛剛介紹的網站,裡面有一個captcha圖像樣本連結:

https://captcha.com/captcha-examples.html?cst=corg

,網頁裡有60張不同類型的圖形驗證碼,足夠我們用來識别試驗了。
Python爬蟲基礎:驗證碼的爬取和識别詳解
直接來看代碼吧:

import requests

import os

import time

from lxml import etree

def get_Page(url,headers):

response = requests.get(url,headers=headers)

if response.status_code == 200:

# print(response.text)

return response.text

return None

def parse_Page(html,headers):

html_lxml = etree.HTML(html)

datas = html_lxml.xpath('.//div[@class="captcha_images_left"]|.//div[@class="captcha_images_right"]')

item= {}

# 建立儲存驗證碼檔案夾

file = 'D:/******'

if os.path.exists(file):

os.chdir(file)

else:

os.mkdir(file)

os.chdir(file)

for data in datas:

# 驗證碼名稱

name = data.xpath('.//h3')

# print(len(name))

# 驗證碼連結

src = data.xpath('.//div/img/@src')

# print(len(src))

count = 0

for i in range(len(name)):

# 驗證碼圖檔檔案名

filename = name[i].text + '.jpg'

img_url = 'https://captcha.com/' + src[i]

response = requests.get(img_url,headers=headers)

if response.status_code == 200:

image = response.content

with open(filename,'wb') as f:

f.write(image)

count += 1

print('儲存第{}張驗證碼成功'.format(count))

time.sleep(1)

def main():

url = 'https://captcha.com/captcha-examples.html?cst=corg'

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36'}

html = get_Page(url,headers)

parse_Page(html,headers)

if __name__ == '__main__':

main()

仍然使用Xpath爬取,在右鍵檢查圖檔時可以發現,網頁分為兩欄,如下圖紅框所示,根據class分為左右兩欄,驗證碼分别位于兩欄中。

Python爬蟲基礎:驗證碼的爬取和識别詳解
datas = html_lxml.xpath('.//div[@class="captcha_images_left"]|.//div[@class="captcha_images_right"]')
           

這裡我使用了Xpath中的路徑選擇,在路徑表達式中使用

“|”

表示選取若幹路徑,例如這裡表示的就是選取

class

"captcha_images_left"

或者

"captcha_images_right"

的區塊。再來看下運作結果:

Python爬蟲基礎:驗證碼的爬取和識别詳解

由于每爬取一張驗證碼圖檔都強制等待了1秒,最後這個運作時間确實讓人絕望,看樣子還是需要多線程來加快速度的,關于多程序多線程我們下次再說,這裡我們先來看下爬取到的驗證碼圖檔。

Python爬蟲基礎:驗證碼的爬取和識别詳解

圖檔到手了,接下來就是調用百度文字識别的OCR來識别這些圖檔了,在識别之前,先簡單介紹一下百度OCR的使用方法,因為很多識别驗證碼的教程用的都是tesserocr庫,是以一開始我也嘗試過,安裝過程中就遇到了很多坑,後來還是沒有繼續使用,而是選擇了百度OCR來識别。百度OCR接口提供了自然場景下圖檔文字檢測、定位、識别等功能。文字識别的結果可以用于翻譯、搜尋、驗證碼等代替使用者輸入的場景。另外還有其他視覺、語音技術方面的識别功能,大家可以直接閱讀文檔了解:百度OCR-API文檔

https://ai.baidu.com/docs#/OCR-API/top

Python爬蟲基礎:驗證碼的爬取和識别詳解

使用百度OCR的話,首先注冊使用者,然後下載下傳安裝接口子產品,直接終端輸入

pip install baidu-aip

即可。然後建立文字識别應用,擷取相關

Appid

,

API Key

以及

Secret Key

,需要了解一下的是百度AI每日提供50000次免費調用通用文字識别接口的使用次數,足夠我們揮霍了。

Python爬蟲基礎:驗證碼的爬取和識别詳解

然後就可以直接調用代碼了。

from aip import AipOcr

# 你的 APPID AK SK

APP_ID = '你的 APP_ID '

API_KEY = '你的API_KEY'

SECRET_KEY = '你的SECRET_KEY'

client = AipOcr(APP_ID, API_KEY, SECRET_KEY)

# 讀取圖檔

def get_file_content(filePath):

with open(filePath, 'rb') as fp:

return fp.read()

image = get_file_content('test.jpg')

# 調用通用文字識别, 圖檔參數為本地圖檔

result = client.basicGeneral(image)

# 定義參數變量

options = {

# 定義圖像方向

'detect_direction' : 'true',

# 識别語言類型,預設為'CHN_ENG'中英文混合

'language_type' : 'CHN_ENG',

}

# 調用通用文字識别接口

result = client.basicGeneral(image,options)

print(result)

for word in result['words_result']:

print(word['words'])

這裡我們識别的是這張圖

Python爬蟲基礎:驗證碼的爬取和識别詳解

可以看一下識别結果

Python爬蟲基礎:驗證碼的爬取和識别詳解

上面是識别後直接輸出的結果,下面是單獨提取出來的文字部分。可以看到,除了破折号沒有輸出外,文字部分都全部正确輸出了。這裡我們使用的圖檔是jpg格式,文字識别傳入的圖像支援jpg/png/bmp格式,但在技術文檔中有提到,使用jpg格式的圖檔上傳會提高一定準确率,這也是我們爬取驗證碼時使用jpg格式儲存的原因。

輸出結果中,各字段分别代表:

 ●  log_id : 唯一的log id,用于定位問題

 ●  direction : 圖像方向,傳入參數時定義為true表示檢測,0表示正向,1表示逆時針90度,2表示逆時針180度,3表示逆時針270度,-1表示未定義。

 ●  words_result_num : 識别的結果數,即word_result的元素個數

 ●  word_result : 定義和識别元素數組

 ●  words : 識别出的字元串

還有一些非必選字段大家可以去文檔裡熟悉一下。

接下來,我們要做的,就是将我們之前爬取到的驗證碼用剛介紹的OCR來識别,看看究竟能不能得到正确結果。

from aip import AipOcr

import os

i = 0

j = 0

APP_ID = '你的 APP_ID '

API_KEY = '你的API_KEY'

SECRET_KEY = '你的SECRET_KEY'

client = AipOcr(APP_ID, API_KEY, SECRET_KEY)

# 讀取圖檔

file_path = 'D:\******\驗證碼圖檔'

filenames = os.listdir(file_path)

# print(filenames)

for filename in filenames:

# 将路徑與檔案名結合起來就是每個檔案的完整路徑

info = os.path.join(file_path,filename)

with open(info, 'rb') as fp:

# 擷取檔案夾的路徑

image = fp.read()

# 調用通用文字識别, 圖檔參數為本地圖檔

result = client.basicGeneral(image)

# 定義參數變量

options = {

'detect_direction' : 'true',

'language_type' : 'CHN_ENG',

}

# 調用通用文字識别接口

result = client.basicGeneral(image,options)

# print(result)

if result['words_result_num'] == 0:

print(filename + ':' + '----')

i += 1

else:

for word in result['words_result']:

print(filename + ' : ' +word['words'])

j += 1

print('共識别驗證碼{}張'.format(i+j))

print('未識别出文本{}張'.format(i))

print('已識别出文本{}張'.format(j))

和識别圖檔一樣,這裡我們将檔案夾驗證碼圖檔裡的圖檔全部讀取出來,依次讓OCR識别,并依據

“word_result_num”

字段判斷是否成功識别出文本,識别出文本則列印結果,未識别出來的用

“----”

代替,并結合檔案名對應識别結果 。最後統計識别結果數量,再來看下識别結果。

Python爬蟲基礎:驗證碼的爬取和識别詳解

看到結果,隻能說Amazing!60張圖檔居然識别出了65張,并且還有27張為未識别出文本的,這不是我想要的結果~先來簡單看下問題出在哪裡,看到

“Vertigo Captcha Image.jpg"

這張圖名出現了兩次,懷疑是在識别過程中由于被幹擾,是以識别成兩行文字輸出了,這樣就很好解釋為什麼多出來5張驗證碼圖檔了。可是!為什麼會有這麼多未識别出文本呢,而且英文數字組成的驗證碼識别成中文了,看樣子,不對驗證碼圖檔進行去幹擾處理,僅靠OCR來識别的想法果然還是行不通啊。那麼接下來我們便使用圖像處理的方法來重新識别驗證碼吧。

還是介紹驗證碼時用的這張圖

Python爬蟲基礎:驗證碼的爬取和識别詳解

這張圖也沒能被識别出來,讓人頭秃。接下來就對這張圖檔進行一定處理,看能不能讓OCR正确識别

from PIL import Image

filepath = 'D:\******\驗證碼圖檔\AncientMosaic Captcha Image.jpg'

image = Image.open(filepath)

# 傳入'L'将圖檔轉化為灰階圖像

image = image.convert('L')

# 傳入'1'将圖檔進行二值化處理

image = image.convert('1')

image.show()

這樣子轉化後再來看下圖檔變成什麼樣了?

Python爬蟲基礎:驗證碼的爬取和識别詳解

确實有些不同了,趕緊拿去試試能不能識别,還是失敗了~~繼續修改

from PIL import Image

filepath = 'D:\******\驗證碼圖檔\AncientMosaic Captcha Image.bmp'

image = Image.open(filepath)

# 傳入'L'将圖檔轉化為灰階圖像

image = image.convert('L')

# 傳入'l'将圖檔進行二值化處理,預設二值化門檻值為127

# 指定門檻值進行轉化

count= 170

table = []

for i in range(256):

if i < count:

table.append(0)

else:

table.append(1 )

image = image.point(table,'1')

image.show()

這裡我将圖檔儲存成了bmp模式,然後指定二值化的門檻值,不指定的話預設為127,我們需要先轉化原圖為灰階圖像,不能直接在原圖上轉化。然後将構成驗證碼的所需像素添加到一個table中,然後再使用point方法建構新的驗證碼圖檔。

Python爬蟲基礎:驗證碼的爬取和識别詳解

現在已經識别到文字了,雖然我不知道為啥識别成了“珍”,分析之後發現是因為z我在設定參數設定了

“language_type”

“CHN_ENG”

,中英文混合模式,于是我修改成“ENG”英文類型,發現可以識别成字元了,但依然沒有識别成功,嘗試其他我所知道的方法後,我表示很無語,我決定繼續嘗試PIL庫的其他方法試試。

# 找到邊緣

image = image.filter(ImageFilter.FIND_EDGES)

# image.show()

# 邊緣增強

image = image.filter(ImageFilter.EDGE_ENHANCE)

image.show()

Python爬蟲基礎:驗證碼的爬取和識别詳解

還是不能正确識别,我決定換個驗證碼試試。。。。。。

Python爬蟲基礎:驗證碼的爬取和識别詳解

我找了這張帶有陰影的

from PIL import Image,ImageFilter

filepath = 'D:\******\驗證碼圖檔\CrossShadow2 Captcha Image.jpg'

image = Image.open(filepath)

# 傳入'L'将圖檔轉化為灰階圖像

image = image.convert('L')

# 傳入'l'将圖檔進行二值化處理,預設二值化門檻值為127

# 指定門檻值進行轉化

count= 230

table = []

for i in range(256):

if i < count:

table.append(1)

else:

table.append(0)

image = image.point(table,'1')

image.show()

簡單處理後,得到這樣的圖檔:

Python爬蟲基礎:驗證碼的爬取和識别詳解

識别結果為:

Python爬蟲基礎:驗證碼的爬取和識别詳解

識别成功了,老淚縱橫!!!看樣子百度OCR還是可以識别出驗證碼的,不過識别率還是有點低,需要對圖像進行一定處理,才能增加識别的準确率。不過百度OCR對規範文本的識别還是很準确的。

那麼與其他驗證碼相比,究竟是什麼讓這個驗證碼更容易被OCR讀懂呢?

 ●  字母沒有互相疊加在一起,在水準方向上也沒有彼此交叉。也就是說,可以在每一個字 母外面畫一個方框,而不會重疊在一起。

 ●  圖檔沒有背景色、線條或其他對 OCR 程式産生幹擾的噪點。

 ●  白色背景色與深色字母之間的對比度很高。

這樣的驗證碼相對識别起來較容易,另外,像識别圖檔時的白底黑字就屬于很标準的規範文本了,是以識别的準确度較高。至于更複雜的圖形驗證碼,就需要更深的圖像處理技術或者訓練好的OCR來完成了,如果隻是簡單識别一個驗證碼的話,不如人工檢視圖檔輸入,更多一點的話,也可以交給打碼平台來識别。

原文釋出時間為:2018-11-9

本文作者:HDMI

本文來自雲栖社群合作夥伴“

Python中文社群

”,了解相關資訊可以關注“

”。

繼續閱讀