1. 程式人生 > >驗證碼的爬取和識別詳解

驗證碼的爬取和識別詳解

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

 

執行平臺:Windows

 

Python版本:Python3.6

 

IDE: Sublime Text

 

其他:Chrome瀏覽器

 

驗證碼的爬取和識別詳解

簡述流程

 

步驟1:簡單介紹驗證碼

 

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

 

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

 

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

 

步驟5:簡單影象處理

 

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

 

再來簡單說下圖形驗證碼吧,就像這張:

 

驗證碼的爬取和識別詳解

 

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

 

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

 

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

 

首先開啟Chrome瀏覽器,訪問剛剛介紹的網站,裡面有一個captcha影象樣本連結:https://captcha.com/captcha-examples.html?cst=corg,網頁裡有60張不同型別的圖形驗證碼,足夠我們用來識別試驗了。

 

驗證碼的爬取和識別詳解

 

直接來看程式碼吧:

 

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分為左右兩欄,驗證碼分別位於兩欄中。

 

驗證碼的爬取和識別詳解

 

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

 

這裡我使用了Xpath中的路徑選擇,在路徑表示式中使用“|”表示選取若干路徑,例如這裡表示的就是選取class為"captcha_images_left"或者"captcha_images_right"的區塊。再來看下執行結果:

 

驗證碼的爬取和識別詳解

 

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

 

驗證碼的爬取和識別詳解

 

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

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

 

驗證碼的爬取和識別詳解

 

使用百度OCR的話,首先註冊使用者,然後下載安裝介面模組,直接終端輸入pip install baidu-aip即可。然後建立文字識別應用,獲取相關Appid,API Key以及Secret Key,需要了解一下的是百度AI每日提供50000次免費呼叫通用文字識別介面的使用次數,足夠我們揮霍了。

 

驗證碼的爬取和識別詳解

 

然後就可以直接呼叫程式碼了。

 

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'])

 

這裡我們識別的是這張圖

 

驗證碼的爬取和識別詳解

 

可以看一下識別結果

 

驗證碼的爬取和識別詳解

 

上面是識別後直接輸出的結果,下面是單獨提取出來的文字部分。可以看到,除了破折號沒有輸出外,文字部分都全部正確輸出了。這裡我們使用的圖片是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”欄位判斷是否成功識別出文本,識別出文本則列印結果,未識別出來的用“----”代替,並結合檔名對應識別結果 。最後統計識別結果數量,再來看下識別結果。

 

驗證碼的爬取和識別詳解

 

看到結果,只能說Amazing!60張圖片居然識別出了65張,並且還有27張為未識別出文本的,這不是我想要的結果~先來簡單看下問題出在哪裡,看到“Vertigo Captcha Image.jpg"這張圖名出現了兩次,懷疑是在識別過程中由於被幹擾,所以識別成兩行文字輸出了,這樣就很好解釋為什麼多出來5張驗證碼圖片了。可是!為什麼會有這麼多未識別出文本呢,而且英文數字組成的驗證碼識別成中文了,看樣子,不對驗證碼圖片進行去幹擾處理,僅靠OCR來識別的想法果然還是行不通啊。那麼接下來我們便使用影象處理的方法來重新識別驗證碼吧。

 

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

 

驗證碼的爬取和識別詳解

 

驗證碼的爬取和識別詳解

 

這張圖也沒能被識別出來,讓人頭禿。接下來就對這張圖片進行一定處理,看能不能讓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()

 

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

 

驗證碼的爬取和識別詳解

 

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

 

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方法構建新的驗證碼圖片。

 

驗證碼的爬取和識別詳解

 

驗證碼的爬取和識別詳解

 

現在已經識別到文字了,雖然我不知道為啥識別成了“珍”,分析之後發現是因為z我在設定引數設定了“language_type”為“CHN_ENG”,中英文混合模式,於是我修改成“ENG”英文型別,發現可以識別成字元了,但依然沒有識別成功,嘗試其他我所知道的方法後,我表示很無語,我決定繼續嘗試PIL庫的其他方法試試。

 

# 找到邊緣
image = image.filter(ImageFilter.FIND_EDGES)
# image.show()
# 邊緣增強
image = image.filter(ImageFilter.EDGE_ENHANCE)
image.show()

 

驗證碼的爬取和識別詳解

 

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

 

驗證碼的爬取和識別詳解

 

我找了這張帶有陰影的

 

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爬蟲基礎:驗證識別

今天要給大家介紹的是驗證碼的爬取和識別,不過只涉及到最簡單的圖形驗證碼,也是現在比較常見的一種型別。 執行平臺:Windows Python版本:Python3.6 IDE: Sublime Text 其他:Chrome瀏覽器 簡述流程: 步驟1:簡單介紹驗證碼 步驟2:

驗證識別

今天要給大家介紹的是驗證碼的爬取和識別,不過只涉及到最簡單的圖形驗證碼,也是現在比較常見的一種型別。   執行平臺:Windows   Python版本:Python3.6   IDE: Sublime Text   其他:Chrome瀏覽器 &nbs

網路資訊保安攻防學習平臺--指令碼關 11關 驗證識別 分值: 350 驗證識別 Tips:驗證依然是3位數

涉及到驗證碼和資料獲取,暴力破解是直觀選擇。可以百度,有好幾個部落格發了文章。本處提兩個操作方法的區別問題:urllib.request.urlretrieve('http://lab1.xseclab.com/vcode7_f7947d56f22133dbc85dda4f2

python網路爬蟲(7)靜態資料

目的 爬取http://seputu.com/資料並存儲csv檔案 匯入庫 lxml用於解析解析網頁HTML等原始碼,提取資料。一些參考:https://www.cnblogs.com/zhangxinqi/p/9210211.html requests請求網頁 chardet用於判斷網頁中的字元編

JAVAWEB開發之HttpServletResponseHttpServletRequest(上)(各種亂碼、驗證、重定向轉發)

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 禁止瀏覽器快取 驗證碼圖片 // response.setH

java的UDPTCP北京-賽車平臺出租源分析

ati 消息 byte ide 一段 pack catch 打包 println 1、需求分析北京-賽車平臺出租Q1446595067 最近在和硬件做網口的傳輸協議,一開始告訴我說用TCP的socket進行傳輸,我說沒問題,就寫了個socket的發送和接收方法。but過了沒

[Python] [爬蟲] 2.批量政府網站的招投標、中標資訊推送的自動化爬蟲——驗證模組

目錄 1.Intro 2.Source 1.Intro 檔名:authentication.py 模組名:驗證模組 引用庫: urllib2 requests pymongo socket

http狀態301302及區別——辛酸的探索之路

一直對http狀態碼301和302的理解比較模糊,在遇到實際的問題和翻閱各種資料瞭解後,算是有了一定的理解。這裡記錄下,希望能有新的認識。大家也共勉。 官方的比較簡潔的說明:         301 redirect: 301 代表永久性轉移(Permanently

微信小程式中form 表單提交值例項

我們知道,如果我們直接給 input 新增 bindinput,比如:<input bindinput="onUsernameInput" />,那麼可以在 onUsernameInput 中直接使用 e.detail.value,即: onUsernameInput : function(e)

http狀態301302及區別

伺服器的隱含意思是網址A 隨時有可能改主意,重新顯示本身的內容或轉向其他的地方。大部分的搜尋引擎在大部分情況下,當收到302 重定向時,一般只要去抓取目標網址就可以了,也就是說網址B。如果搜尋引擎在遇到302 轉向時,百分之百的都抓取目標網址B 的話,就不用擔心網址URL 劫持了。問題就在於,有的時候搜尋引擎

R數據可視化----ggplot2之標度、坐標軸圖例

abs 調整 所有 不同的 size n) 默認 表達 idt 標度控制著數據到圖形屬性的映射,當有需要時,ggplot2會自動添加一個默認的標度。我們確實可以在不了解標度運行原理的情況下畫出許多圖形,但理解標度並學會如何操縱它們則將賦予我們對圖形更強的控制能力。 每一種圖

xargs exec

linux xargs exec cpxargs 和 exec詳解 exec主要和find一起配合使用,xargs比exec用的地方要多。xargs應用把管道符前面的輸出作為xargs後面的命令的輸入。好處在於可以簡化步驟。常常和find一起使用,#find . -mtime +10 |xargs rm

pjlib深入剖析使用

china lis sim framework 非線程安全 還要 cts 線程本地存儲 獲取 1. PJSIP簡介 PJSIP的實現是為了能在嵌入式設備上高效實現SIP/VOIP.其主要特征包括: 1).極具移植性.(Extremely portable)

java PatternMatcher

而且 建議 exc regex 示例 aabb 工廠 sta 數字 結論:Pattern與Matcher一起合作.Matcher類提供了對正則表達式的分組支持,以及對正則表達式的多次匹配支持. 單獨用Pattern只能使用Pattern.matcher(String reg

mapreduce shuffle sort

改變 struct 堆內存 傳輸 工具 默認 臨時 arc 快速排序 MapReduce 框架的核心步驟主要分兩部分:Map 和Reduce。當你向MapReduce 框架提交一個計算作業時,它會首先把計算作業拆分成若幹個Map 任務,然後分配到不同的節點上去執

Redis系列--7、RedisTemplate Serializer

redistemplate serializer詳解<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionF

HTML embed標簽使用方法屬性

利用 是否 parent als ase sig 默認 eve sel 一、基本語法 代碼如下: embed src=url 說明:embed可以用來插入各種多媒體,格式可以是 Midi、Wav、AIFF、AU、MP3等等,Netscape及新版的IE 都支持。url為

es6 map()filter()【轉】

低版本 window get 簡約 push foreach 沒有 數值 length 原文地址:http://www.zhangxinxu.com/wordpress/2013/04/es5%e6%96%b0%e5%a2%9e%e6%95%b0%e7%bb%84%e

robots.txt的語法寫法

html txt 訪問 isa 字符 包含 all 屏蔽 有道 robots.txt是一個純文本文件,是搜索引擎蜘蛛爬行網站的時候要訪問的第一個文件,當蜘蛛訪問一個站點時,它會首先檢查該站點根目錄下是否存在robots.txt,如果存在,搜索機器人就會按照該文件中的內容來確

IEnumerableIEnumerator

col 列表框 詳細講解 分配 false 叠代 http 數據 應該 IEnumerable接口是非常的簡單,只包含一個抽象的方法GetEnumerator(),它返回一個可用於循環訪問集合的IEnumerator對象。IEnumerator對象有什麽呢?它是一個真正的集