12306破解!

  • 2020 年 3 月 13 日
  • 筆記

驗證碼(CAPTCHA),是一種區分用戶是電腦還是人的公共全自動程式。對於研究爬蟲來說,這應該是爬蟲的「天敵」。

原因就是在於,現在各大網站為了反爬,都加入了點選圖片驗證碼,滑動驗證碼,英文亂碼等驗證碼干擾,因此想要爬取網站,需要做的第一步就是解決驗證碼的問題。

聊到驗證碼,簡單的看圖輸入型的數字驗證碼,英文驗證碼,中文驗證碼,都已經被爬蟲工程師機靈的攻克了,孕育而生的又有需要按照圖片顯示的數據公式進行計算後輸入結果的驗證碼,以及非常有名的12306魔鬼點選驗證碼。

這一類的驗證碼很難通過簡單的機器學習等方式進行操作,畢竟遇到點選「白百合」還是「王珞丹」,人臉識別也要束手無策。

本篇就針對12306的點選驗證碼進行講解和破解。讓我們一起來學習一下。

點選驗證碼是近代用於驗證是否為人類的一種措施之一。他的驗證方式是通過給出需要點擊的圖片類型,用戶需要根據給出的幾種圖片中,正確點擊出正確的圖片,否則就會視為自動化腳本,不給予通過驗證。這為反爬蟲工作提升了一個新的高度,因為基本上傳統的破解方式都已經不能繞過該驗證碼。若真想破解點選驗證碼,只有使用深度學習網路,訓練出圖片分類器,才可能有機會破解點選驗證碼。

個人觀點: 對於開發周期太短,技術實現太複雜的情況我其實是推薦用服務商的介面。因為如果花太多時間在繞過驗證碼這方面,還不如優化一下程式碼,使得程式碼運行速度更快、魯棒性更強,(另外實在不行的情況下我們也可以讓功能先上線再說…技術的東西慢慢不上),畢竟攻城獅能合理調配資源解決問題也是牛攻城獅;

另外對於點選驗證碼,這種的破解難度十分高,為什麼這麼說呢?

是因為如果要破解點選圖片驗證碼,這就與電腦視覺(CV)的知識有交叉了。我來簡單說下電腦是如何識別圖片的。

首先現在的一般圖片都是由二維的像素點加通道數構成的,在電腦眼裡,就是一個三位數組,每個像素點的值代表的是亮度,0-255之間;舉個例子,對於一張灰度圖片例如長這樣:

它由字母以及雜訊構成,他對應的量化矩陣就只有一個通道,圖片尺寸大小的一個矩陣。如果想要訓練電腦認識這個圖片,人們肯定不想把雜訊也放進去的,人們就會通過矩陣與卷積核進行卷積,得到一張過濾後的圖片,卷積核就是相當於一個濾波器。

常見的卷積核有拉普拉斯運算元,高斯運算元等,用已知的這些運算元可以對圖片進行平滑化的操作,但我們想要的是圖片的關鍵部分啊!!!

這時候深度學習就出來了,通過一個數據池,裡面有很多這種類型的驗證碼,通過計算,不斷地擬合出一個適合這個數據池使用的卷積核,最後通過人工設置的數輪計算後,拿到卷積核,我們便可以拿這個擬合出來的卷積核對新的圖片進行卷積,從而判斷這個圖片是什麼。但這個只是一個通道,尺寸比較小的圖片,對於一些尺寸較大的、彩色的圖片,那麼這個計算量就十分之大了。

回到12306的驗證碼,驗證碼所顯示的圖片類型是完全隨機的,一會是洗潔精圖片,一會是黑板的圖片,一會是瑪雅神殿的圖片,對於一般的開發者來說,不可能通過深度學習訓練出一個適合12306的圖片預測器,因為數據量實在是太大了。

這時我們不妨可以借用第三方介面,來滿足我們的需要,俗話說得好:」永遠不要用自己的業餘挑戰別人的職業」。

別人通過專業的機器、大量的人員來解決專業領域的問題,我們可以通過分享部分的利潤,來實現自己的功能,這個雙贏的局面,也能增加全球的GDP嘛哈哈。直接使用第三方平台吧,直接調用別人的介面快速完成業務是真的爽。

本文使用的第三方服務:

https://2captcha.com/zh?from=8873291

根據本人測試,這是目前識別率最高的平台,而且調用他們家的api也是十分簡單而且響應速度也不錯。另外他們家除了有點選驗證碼外,還有很多的驗證碼解決方案。

下面直接敲程式碼,一起體驗使用api的快感。

首先看下官方文檔:

基本意思是我們得要先把圖片發至:

https://2captcha.com/in.php

後再從

https://2captcha.com/res.php

取得我們所需要的坐標。而這個就是官方提供的參數:

其中說明一下這兩個參數:

  • textinstructions 標識如果圖片中沒有顯示需要點擊的圖片類型,則需要傳送一個欄位過去。用文本形式,即字元串。ps:因為這個是國外網站,我們需要傳送一個英語文本過去,這裡提示一下,可以使用中國的大廠子的翻譯平台api轉換,遇到圖片顯示時,我們也可以使用大廠子的ocr圖片轉文字或者使用python庫pytesseract進行轉換為文本,再用翻譯api對文本進行翻譯再請求。
  • imginstrutions 這個類似上面那個也是用於標識需要點擊的圖片類型,不過這個是用圖片進行標識。

下面,直接封裝一個請求函數,我已經將常用的參數都寫進函數裡面了,以後調用的時候就可以十分方便了,美滋滋!

from requests import Session  from urllib.parse import urlencode  from json import loads      class Captcha:      def__init__(self, key):          self.send_url ='https://2captcha.com/in.php'          self.result_url ='https://2captcha.com/res.php'          self.key = key          self.session = Session()        defclick_image_captcha(self, image_name, method='post', imginstructions='', textinstructions=''):          data ={              'key': self.key,              'method': method,              'coordinatescaptcha':1,              'json':1,              }          if textinstructions:              data['textinstructions']= textinstructions          if textinstructions:              data['imginstructions']= imginstructions          files ={              'file':open(image_name,'rb')          }          params ={              'method':'post',              'url': self.send_url,              'data': data,              'files': files,          }          response = loads(self.session.request(**params).text)          return response        defget_result(self, request_id):          params ={              'key': self.key,              'action':'get',              'id': request_id,              'json':1          }          response = self.session.request(method='get', url=self.result_url +'?'+ urlencode(params))          return response.text

這個是測試用的png,命名為111.png。

根據上面給出的函數介面,直接調用並傳入圖片文件111.png,讓我們一起看看返回結果。

t = Captcha('85c*****************a939')  answer = t.click_image_captcha(image_name='111.png', textinstructions='click on images with seal.')  print(answer)

請求並列印一下得到這個。

而拿到的這個是什麼東西呢?這是平台給出的,用於請求答案的唯一request-id碼,我們將拿到的request值再通過請求函數請求一次。

answer = t.get_result('63283539784')  print(answer)

我們最後對照後發現,我們得到了兩個坐標,我們可以使用圖片工具打開圖片:

通過上圖發現,這兩個坐標的位置正是驗證碼中那兩個符合條件的印章位置,最後我們可以通過selenium模擬登陸,模擬點擊完成驗證碼的突破了。

另外需要提醒的是,selenium模擬點擊的坐標需要另外計算,也就是說先要知道圖片所在瀏覽器的坐標位置,再通過這個位置,與拿到的坐標相加再點擊才能正確點擊該點的位置,還有使用selenium庫點擊後,滑鼠點擊的坐標並不會回到絕對坐標中的0,0位置,而是會在點擊後的原有位置設為絕對坐標(0,0),用人話說就是需要在點擊以後,另外做一個反向移動的操作,這個操作我已經封裝了一個函數了,需要的自取把,在這裡就不演示了 因為下面順便發出封裝好的selenium模擬點擊。

defclick_locxy(dr, x, y, left_click=True):      if left_click:         ActionChains(dr).move_by_offset(x, y).click().perform()      else:         ActionChains(dr).move_by_offset(x, y).context_click().perform()      ActionChains(dr).move_by_offset(-x,-y).perform()

本文介紹到這裡。

總結

在這篇文章里,我們針對「12306」魔鬼驗證碼進行了實驗。可以看到精度還是相當高的,確實是給爬蟲操作解決了大麻煩。

2captcha 本身我看到很多爬虫部落客都在使用,其針對滑動驗證碼,以及Google系列的點選驗證碼的效果都非常好。另外調用api的形式簡單方便,確實是非常值得推薦的。