漲姿式了。原文:12306 售票網站新版驗證碼識別對抗php
近段時間 12306 訂票網站驗證碼升級爲用戶識別圖像內容,而後選取符合條件的圖片爲驗證碼,好比這樣:html
很多媒體新聞大呼搶票工具集體失效、12306終極驗證碼等新聞,這種驗證碼的推出有好一樣也有壞處:機器識別困難,一樣人眼識別也輕鬆不到哪裏去。linux
用這種方式做爲驗證碼最大的擔心就是怕腳本或人工對其圖片進行爬蟲遍歷,而後將全部的圖片保存後與關鍵字進行對比並關聯入庫,固然前提是這些圖片都是靜態的。git
12306 驗證碼到底是靜態仍是動態,昨晚對這個疑問進行了實踐:http://linux.im/2015/03/17/12306-captcha-md5-go.html ,簡單的說測試後發現這整張圖片是在服務器後端動態生成的,因此不難理解爲何生成驗證碼頁面時會比較慢。github
一樣上午咱們又進行了第二個實踐,將整張驗證碼中的八張圖像拆分爲8張小圖而後進行感知hash處理,得到樣本總數72225張,不重複的圖庫爲15478張,重複最高爲869次,繪製成圖以下:後端
既然不是靜態的圖像(對比過近10w條圖像hash),那咱們就不浪費功夫爬取靜態圖片進行數據關聯入庫了,但咱們仍然須要「破」掉這個驗證碼,沒有什麼理由。服務器
最後,下文出現的全部的片斷代碼將會開源,無需擔憂。app
驗證碼流程:函數
例如上面的驗證碼圖,他是一整張圖片,識別其關鍵字首先要對關鍵字區域進行圖像截取,隨後識別成文字。工具
這裏使用 Python 的 PIL 圖像處理庫來進行區域的選擇:
def imgCut(): pic_file = downloadImg() pic_path = "./12306_pic/%s.jpg" % pic_file pic_text_path = './12306_pic/%s_text.jpg' % pic_file pic_obj = Image.open(pic_path) box = (120,0,290,25) region = pic_obj.crop(box) region.save(pic_text_path) print '[*] Picture Text Picture: {}'.format(pic_text_path) return pic_path, pic_text_path
imgGut函數首先會下載這張驗證碼大圖(其中包括提示字、關鍵字、8張圖片等),而後保存至 ./12306_pic/ 目錄進行存儲,隨後使用 PIL 庫對圖像的 (120,0,290,25) 區域切割,也就是獲取關鍵字圖像區域。
如今咱們已經可以將驗證碼下載並切割出想要的關鍵字區域了,下面咱們要識別關鍵字,而後轉換爲文本文字。
使用一些開源的光學字符識別模塊應該就能進行識別,但這不方便使用者運行,因此我選擇了一款在線網站OCR識別,他可以對你上傳的圖像(咱們剛剛切割好的圖像)進行文字識別轉換,固然準確率並無那麼高,必定得記住這一點!
這裏貼出部分代碼,功能實現(傳入圖像返回關鍵字的文本內容):
upload_pic_url = "http://cn.docs88.com/pdftowordupload2.php" filename_tmp = filename.split('/')[-1] pic_text_content = open(filename).read() para = {'Filename': filename_tmp, 'sourcename': filename_tmp, 'sourcelanguage': 'cn', 'desttype': 'txt', 'Upload': 'Submit Query',} upload_pic = requests.post(upload_pic_url, data=para, files={"Filedata" : open(filename, 'rb')}) text_result_url = 'http://cn.docs88.com/' + upload_pic.content[3:] text_result = requests.get(text_result_url) return text_result.content
咱們運行試試效果:
[+] Download Picture: https://kyfw.12306.cn/otn/passcode... [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg [*] Text: 襯 衫 [+] Download Picture: https://kyfw.12306.cn/otn/passcod... [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg [*] Text: )帽子 [+] Download Picture: https://kyfw.12306.cn/otn/passcod... [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg [*] Text: 春聯
效果還不錯,足夠咱們測試使用,還記得他的準確率嗎?
以前關於圖像識別我在 Buzz 發表過相關文章:使用CloudSight API進行圖像識別的Python腳本,此次咱們不使用這個腳本,緣由是雖然識別準確度較高但速度略慢,因此我並非很鍾愛這一套,恰巧知乎上有位朋友寫了一篇利用百度識圖來進行圖像識別的文章及代碼,Google識圖固然也不錯,但恰好在這咱們會用到,因此沒必要糾結。
橫向兩行,每行四個,而後對其進行圖像識別並返回:
dict_list = {} count = 0 for y in range(2): for x in range(4): count += 1 im2 = get_sub_img(pic_path, x, y) result = baidu_stu_lookup(im2) dict_list[count] = result print (y,x), result
其中函數因文章長度緣由暫不在這貼出,識別效果以下:
(0, 0) 冰雕|建築夜景 (0, 1) 炸暑條|快餐 (0, 2) 燈塔|高塔 (0, 3) 漢堡|麥當勞薯條|開店 (1, 0) 運動外套|防禦服|運動服 (1, 1) 銀灰色|手機|移動版 (1, 2) 標書製做|規劃 (1, 3) 手機
好,如今咱們可以識別出關鍵字,也能識別出驗證碼8個圖像了,咱們還須要機器幫助咱們確認,究竟選擇哪幾個圖。
前面兩次提到使用的 OCR 在線識別準確度並無那麼高,因此爲了方便程序可以聰明的幫咱們思考這道選擇題,咱們進行結果僞分詞對比。
首先將關鍵字進行拆分,而後循環對比結果,這樣就能將未準確識別的文字忽略並識別相應識圖結果,這裏我將8個圖像結果按照1-8區分,第一行從左到右(1-4),第二行(5-8):
if captcha_text.strip() > 2: print '\n[*] Maybe the result of the:' maybe_result = [] for v in dict_list: for c in range(len(unicode(captcha_text.strip(), 'utf8'))): text = unicode(captcha_text, 'utf8')[c] if text in dict_list[v]: _str_res = '%s --- %s' % (v, dict_list[v]) maybe_result.append(_str_res) for r in list(set(maybe_result)): print r else: print '[-] False'
好了,這樣一來就算識別率沒有那麼高咱們也能儘量的將答案尋找出來了,看下效果:
結束了嗎? 其實沒有。
咱們使用腳本進行了大量的測試,成功率可喜的足夠令一些邪惡的人作些事兒了,但驗證碼對抗一直在進行,固然也愈來愈有趣:)
文中完整代碼連接:https://gist.github.com/Evi1m0/fbbdb1ba7c66cc4e1bb2