【2022 年】Python3 爬蟲教程 - OCR 識別圖形驗證碼
各類網站采用了各種各樣的措施來反爬蟲,其中一個措施便是使用驗證碼。隨著技術的發展,驗證碼的花樣越來越多。驗證碼最初是幾個數字組合的簡單的圖形,后來加入了英文字母和混淆曲線。還有一些網站使用了中文字符驗證碼,這使得識別愈發困難。
12306 驗證碼的出現使得行為驗證碼開始發展起來,用過 12306 的用戶肯定多少為它的驗證碼頭疼過,我們需要識別文字,點擊與文字描述相符的圖片,驗證碼完全正確,驗證才能通過。隨著技術的發展,現在這種交互式驗證碼越來越多,如滑動驗證碼需要將對應的滑塊拖動到指定位置才能完成驗證,點選驗證碼則需要點擊正確的圖形或文字才能通過驗證。
驗證碼變得越來越復雜,爬蟲的工作也變得越發艱難,有時候我們必須通過驗證碼的驗證才可以訪問頁面。
本章就針對驗證碼的識別進行統一講解,涉及的驗證碼有普通圖形驗證碼、滑動驗證碼、點選驗證碼、手機驗證碼等,這些驗證碼識別的方式和思路各有不同,有直接使用圖像處理庫完成的,有的則是借助于深度學習技術完成的,有的則是借助于一些工具和平臺完成的。雖然說技術各有不同,但了解這些驗證碼的識別方式之后,我們可以舉一反三,用類似的方法識別其他類型驗證碼。
我們首先來看最簡單的一種驗證碼,即圖形驗證碼,這種驗證碼最早出現,現在依然也很常見,一般由 4 位左右字母或者數字組成。
例如這個案例網站 https://captcha7.scrape.center/ 就可以看到類似的驗證碼,如圖所示:
這類驗證碼整體上比較規整,沒有過多干擾線和干擾點,且文字沒有大幅度的變形和旋轉。
對于這一類的驗證碼我們就可以使用 OCR 技術來進行識別。
1. OCR 技術
OCR,即 Optical Character Recognition,中文翻譯叫做光學字符識別。它是指電子設備(例如掃描儀或數碼相機)檢查紙上打印的字符,通過檢測暗、亮的模式確定其形狀,然后用字符識別方法將形狀翻譯成計算機文字的過程。OCR 現在已經廣泛應用于生產生活中,如文檔識別、證件識別、字幕識別、文檔檢索等等。當然對于本節所述的圖形驗證碼的識別也沒有問題。
本節我們會以當前示例網站的驗證碼為例來講解利用 OCR 來識別圖形驗證碼的流程,輸入上是一上圖驗證碼的圖片,輸出就是驗證碼識別結果。
2. 準備工作
識別圖形驗證碼需要 Tesserocr 庫,本庫的安裝相對沒有那么簡單,可以參考 https://setup.scrape.center/tesserocr
另外在本節學習過程中還需要安裝 Selenium、Pillow、Numpy,Retrying 庫用作模擬登錄、圖像處理和操作重試,我們可以使用 pip3 來進行安裝:
1
pip3 install selenium pillow numpy retrying
如果某個庫安裝有問題,可以參考如下鏈接:
Selenium:https://setup.scrape.center/selenium
Pillow:https://setup.scrape.center/pillow
Numpy:https://setup.scrape.center/numpy
retrying:https://setup.scrape.center/retrying
安裝好了如上庫之后,我們就可以開始本節的學習了。
3. 獲取驗證碼
為了便于實驗,我們先將驗證碼的圖片保存到本地。
我們可以在瀏覽器中打開上述示例網站,然后右鍵點擊這張驗證碼圖片,將其保存到本地,命名為 captcha.png,示例如圖所示:
這樣我們就可以得到一張驗證碼圖片,以供測試識別使用。
4. 識別測試
接下來新建一個項目,將驗證碼圖片放到項目根目錄下,用 tesserocr 庫識別該驗證碼,代碼如下所示:
1
2
3
4
5
6
import tesserocr
from PIL import Image
image = Image.open('captcha.png')
result = tesserocr.image_to_text(image)
print(result)
在這里我們新建了一個 Image 對象,調用了 tesserocr 的 image_to_text 方法。傳入該 Image 對象即可完成識別,實現過程非常簡單,結果如下所示:
1
d241
另外,tesserocr 還有一個更加簡單的方法,這個方法可直接將圖片文件轉為字符串,代碼如下所示:
1
2
import tesserocr
print(tesserocr.file_to_text('captcha.png'))
可以得到同樣的輸出結果。
這時候我們可以看到,通過 OCR 技術我們便可以成功識別出驗證碼的內容了。
5. 驗證碼處理
接下來我們換一個驗證碼,將其命名為 captcha2.png,如圖所示。
重新用下面的代碼來測試:
1
2
3
4
5
6
import tesserocr
from PIL import Image
image = Image.open('captcha2.png')
result = tesserocr.image_to_text(image)
print(result)
可以看到如下輸出結果:
1
-b32d
這次識別和實際結果有偏差,多了一些干擾結果,這是因為驗證碼內的多余的點干擾了圖像的識別,導致出現了一些多余的內容。
對于這種情況,我們可以需要做一下額外的處理,把一些干擾信息去掉。
這里觀察到圖片里面其實有一些雜亂的點,而這些點的顏色大都比文本更淺一點,因此我們可以做一些預處理,將干擾的點通過顏色來排除掉。
我們可以首先將原來的圖像轉化為數組看下維度:
1
2
3
4
5
6
7
import tesserocr
from PIL import Image
import numpy as np
image = Image.open('captcha2.png')
print(np.array(image).shape)
print(image.mode)
運行結果如下:
1
2
(38, 112, 4)
RGBA
可以發現這個圖片其實是一個三維數組,前兩維 38 和 112 代表其高和寬,最后一維 4 則是每個像素點的表示向量。為什么是 4 呢,因為最后一維是一個長度為 4 的數組,分別代表 R(紅色)、G(綠色)、B(藍色)、A(透明度),即一個像素點有四個數字表示。那為什么是 RGBA 四個數字而不是 RGB 或其他呢?這是因為 image 的模式 mode 是 RGBA,即有透明通道的真彩色,我們看到第二行輸出也印證了這一點。
模式 mode 定義了圖像的類型和像素的位寬,一共有 9 種類型:
1:像素用 1 位表示,Python 中表示為 True 或 False,即二值化。
L:像素用 8 位表示,取值 0-255,表示灰度圖像,數字越小,顏色越黑。
P:像素用 8 位表示,即調色板數據。
RGB:像素用 3x8 位表示,即真彩色。
RGBA:像素用 4x8 位表示,即有透明通道的真彩色。
CMYK:像素用 4x8 位表示,即印刷四色模式。
YCbCr:像素用 3x8 位表示,即彩-格式。
I:像素用 32 位整型表示。
F:像素用 32 位浮點型表示。
為了方便處理,我們可以將 RGBA 模式轉為更簡單的 L 模式,即灰度圖像。
我們可以利用 Image 對象的 convert 方法參數傳入 L,即可將圖片轉化為灰度圖像,代碼如下所示:
1
2
image = image.convert('L')
image.show()
或者傳入 1 即可將圖片進行二值化處理,如下所示:
1
2
image = image.convert('1')
image.show()
在這里我們就轉為灰度圖像,然后根據閾值篩選掉圖片中的干擾點,代碼如下:
1
2
3
4
5
6
7
8
9
10
from PIL import Image
import numpy as np
image = Image.open('captcha2.png')
image = image.convert('L')
threshold = 50
array = np.array(image)
array = np.where(array > threshold, 255, 0)
image = Image.fromarray(array.astype('uint8'))
image.show()
在這里,變量 threshold 代表灰度的閾值,這里設置為 50。接著我們將圖片 image 轉化為了 Numpy 數組,接著利用 Numpy 的 where 方法對數組進行篩選和處理,這里指定了大于閾值的就設置為 255,即白色,否則就是 0,即黑色。
最后看下圖片處理完之后是什么結果:
我們發現原來驗證碼中的很多點已經被去掉了,整個驗證碼變得黑白分明。這時重新識別驗證碼,代碼如下所示:
1
2
3
4
5
6
7
8
9
10
11
import tesserocr
from PIL import Image
import numpy as np
image = Image.open('captcha2.png')
image = image.convert('L')
threshold = 50
array = np.array(image)
array = np.where(array > threshold, 255, 0)
image = Image.fromarray(array.astype('uint8'))
print(tesserocr.image_to_text(image))
即可發現運行結果變成如下所示:
1
b32d
所以,針對一些有干擾的圖片,我們可以做一些去噪處理,這會提高圖片識別的正確率。
6. 識別實戰
最后,我們可以來嘗試下用自動化的方式來對案例進行驗證碼識別處理,這里我們使用 Selenium 來完成這個操作,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import time
import re
import tesserocr
from selenium import webdriver
from io import BytesIO
from PIL import Image
from retrying import retry
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
import numpy as np
def preprocess(image):
image = image.convert('L')
array = np.array(image)
array = np.where(array > 50, 255, 0)
image = Image.fromarray(array.astype('uint8'))
return image
@retry(stop_max_attempt_number=10, retry_on_result=lambda x: x is False)
def login():
browser.get('https://captcha7.scrape.center/')
browser.find_element_by_css_selector('.username input[type="text"]').send_keys('admin')
browser.find_element_by_css_selector('.password input[type="password"]').send_keys('admin')
captcha = browser.find_element_by_css_selector('#captcha')
image = Image.open(BytesIO(captcha.screenshot_as_png))
image = preprocess(image)
captcha = tesserocr.image_to_text(image)
captcha = re.sub('[^A-Za-z0-9]', '', captcha)
browser.find_element_by_css_selector('.captcha input[type="text"]').send_keys(captcha)
browser.find_element_by_css_selector('.login').click()
try:
WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, '//h2[contains(., "登錄成功")]')))
time.sleep(10)
browser.close()
return True
except TimeoutException:
return False
if __name__ == '__main__':
browser = webdriver.Chrome()
login()
在這里我們首先定義了一個 preprocess 方法,用于驗證碼的噪聲處理,邏輯就和前面說的是一樣的。
接著我們定義了一個 login 方法,其邏輯執行步驟是:
打開樣例網站
找到用戶名輸入框,輸入用戶名
找到密碼輸入框,輸入密碼
找到驗證碼圖片并截取,轉化為 Image 對象
預處理驗證碼,去除噪聲
對驗證碼進行識別,得到識別結果
識別結果去除一些非字母和數字字符
找到驗證碼輸入框,輸入驗證碼結果
點擊登錄按鈕
等待「登錄成功」字樣的出現,如果出現則證明登錄成功,否則重復以上步驟重試。
在這里我們還用到了 retrying 來指定了重試條件和重試次數,以保證在識別出錯的情況下反復重試,增加總的成功概率。
運行代碼我們可以觀察到瀏覽器彈出并執行以上流程,可能重試幾次后得到登錄成功的頁面,運行過程如圖所示:
登錄成功后的結果如圖所示:
到這里,我們就能成功通過 OCR 技術識別成功驗證碼,并將其應用到模擬登錄的過程中了。
7. 總結
本節我們了解了利用 Tesserocr 識別驗證碼的過程并將其應用于實戰案例中實現了模擬登錄。為了提高 Tesserocr 的識別準確率,我們可以對驗證碼圖像進行預處理去除一些干擾,識別準確率會大大提高。但總歸來說 Tesserocr 識別驗證碼的準確率并不是很高,下一節我們來介紹其他識別驗證碼的方案。
本節代碼:https://github.com/Python3WebSpider/CrackImageCaptcha
本文參考資料:
文檔 - OCR - 百度百科:https://baike.baidu.com/item/OCR
OCR Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。