1. 程式人生 > >爬蟲技術之規避驗證碼

爬蟲技術之規避驗證碼

各地工商網站(全稱國家企業信用資訊公示系統)因為包含大量企業真實資訊,金融貸款徵信等都用得到,天然吸引了很大部分來自爬蟲的火力,因此反爬蟲措施格外嚴格。一般的網站僅在登入註冊等環節,或者訪問頻繁後才彈出驗證碼,而工商網站查詢無需登入,每查一次關鍵字就需要一次驗證碼。同時各地工商網站由於各自獨立開發,自主採用了各種不同的驗證碼機制,更是給全量爬取的爬蟲增加了更多的障礙。因此,工商網站的驗證碼特別具有代表性。 首先,從最簡單的分頁角度入手。 分頁的處理可以放在前端也可以放在後端,如果只放在後端,每次點選頁碼就需要傳送一次查詢請求,而一次驗證碼通常只能服務於一次請求,再次請求需要獲取新的驗證碼,直接觀察翻頁操作是否彈出驗證碼輸入框即可判斷是否可以繞過。 如何判斷分頁處理放在前端還是後端的呢?很簡單,F12 開啟瀏覽器開發者工具,頁面點下一頁,有新的請求就說明放在後端,反之就是前端。 實踐發現,四川和上海的工商網站翻頁都放在後端,且翻頁沒有驗證碼輸入框。說明這裡的驗證碼可以繞過,但是繞過的原理卻有些不同: 對比四川工商網站翻頁前後請求的引數,除了頁碼引數外,多了一項:yzmYesOrNo=no。從變數名也猜得到,後端根據該引數的值判斷是否需要檢查驗證碼。 而上海工商網站的對比結果發現,除了頁碼引數外,少了驗證碼欄位,因此我們可以大膽猜測:驗證碼的校驗僅放在了前端,後端沒有做二次校驗,從頁面上操作是繞不過的,但是不帶驗證碼欄位直接向後端傳送請求,資料就拿到了。 嚴格來說,上海工商網站這種算是漏洞,對待這種這種漏洞,爬蟲工程師應有的態度是:悄悄的進村,打槍的不要。不過也不要利用的太狠了,筆者實驗時沒加限制,爬了十萬資料後,ip 被封了。很長時間後才解封,同時頁面改版並修復了漏洞。

其次,觀察目標網站是否有多套驗證碼。 有些網站不知道出於什麼樣的考慮,會在不同的頁面使用不同的驗證碼。一旦遇到這種情況,我們可就要撿軟柿子捏了--從簡單的驗證碼入手,移花接木,將識別的結果作為引數來向後端傳送請求,從而達到繞過複雜驗證碼的目的。 舉例來說,湖北工商的查詢頁面驗證碼是類似下圖的九宮格驗證碼:

而電子營業執照登陸介面的驗證碼是這樣的:

即便要識別,也明顯是後者的驗證碼要容易些,例項程式碼如下:

再者,可以考慮從資料的儲存id入手 專門針對移動端開發的 wap 頁面限制一般要少得多。以北京工商網站來說,wap 介面不帶驗證碼引數直接傳送請求就可以得到資料的,原理類似於上海工商網站,但是其對單個ip日訪問次數做了嚴格限制,因此該方式可以用但不好用。 繼續觀察,以搜尋“山水集團”為例,從搜尋頁到列表頁時,需要輸入驗證碼,而從列表頁進入詳情頁的時候,是不需要驗證碼的。最終詳情頁如圖:

通常沒人記得住各地工商網站的網址,我們會去搜索引擎裡搜。當搜北京企業信用資訊時,發現兩個有價值的結果,除了國家企業信用資訊公示系統外,還有個北京市企業信用資訊資訊網。進後者再操作一番可以得到下面的詳情頁:

觀察發現,企業 id 都是相同的,嘿嘿,這不是“兩塊牌子,一套班子”嘛!後者的訪問量要小一些,雖有驗證碼,但是可以採用上海工商一樣的方式繞過,只是返回的結果欄位不太符合我們的要求。不過,我們可以去企業資訊網獲得企業id,再次採用移花接木的方式,去資訊公示系統構造連結獲得最終的詳情頁嘛! 用過資料庫同學都曉得,資料庫裡的資料 id,預設是自增的。如果有個網站引用的資料是 xxx.com?id=1234567, 那我們很容易猜得到構造類似 1234568 這樣的 id 去嘗試!通常,使用這種 id 規律能夠輕易猜出來的網站並不多,但不代表沒有。比如甘肅省工商網站,結果頁是拿企業註冊號來查詢的。 最後,談談滑動驗證碼。 目前,工商網站已經全面改版,全部採用了滑動驗證碼,上面絕大多數思路都失效了。對於滑動驗證碼,網上能搜到的解決方案基本都是下載圖片,還原圖片,算出滑動距離,然後模擬 js 來進行拖動解決,我們來看下能否不模擬拖動來解決這個問題。 以雲南工商網站為例,首先抓包看過程。 1. http://yn.gsxt.gov.cn/notice/pc-geetest/register?t=147991678609,response:

2. 下載驗證碼圖片

3. http://yn.gsxt.gov.cn/notice/pc-geetest/validate, post 如下資料:

4. http://yn.gsxt.gov.cn/notice/search/ent_info_list,post 如下資料:

仔細分析,我們發現兩處疑點: 第一步並沒有返回需要下載的圖片地址,那麼前端怎麼知道要下載哪些圖片? 第三步驗證時,並沒有告知後端下載了那些圖片,後端是怎麼驗證 post 過去的資料是有效性的? 仔細閱讀前端混淆的 js 程式碼,我們發現前端資料處理過程是這樣的:

從 0 到 6(不含)中取隨機整數,賦值給 d, d=5; 從 0 到 300(不含)中取隨機整數,賦值給 e, e=293; 將d轉化為字串並作 MD5 加密,加密字串取前 9 位賦值給 f, f='e4da3b7fb'; 將 e 轉化為字串並作 MD5 加密,加密字串從第 11 位開始取 9 位賦值給 g, g='43be4f209'; 取 f 的偶數位和 g 的奇數位組成新的 9 位字串給 h, h='e3de3f70b'; 取 h 的後 4 位與 200 做 MOD 運算,其結果小於 40,則取 40,否則取其本身賦值給 x,x=51; 取 [x-3, x+3] 以內隨機數賦值給 c,c=51; 分別取 c,d,e 跟 challenge 做 t 加密 (t(c,challenge), t(d, challenge), t(e,challenge)) 並用(_)拼接即為 geetest_validate, '9ccccc997288_999c9ccaa83_999cc9c9999990d' 這裡 f, g 引數決定了下載圖片地址:

而 x 為滑塊拖動的橫向偏移量,至此解答了疑問 1 中圖片下載地址怎麼來的問題。 前邊提到的 t 加密過程是這樣的:

t(a,b),此處以 a=51 演示 challenge 為 34 位 16 進位制字串,取前 32 位賦值給 prefix,後 2 位賦值給 suffix,prefix='34173cb38f07f89ddbebc2ac9128303f', suffix='a8' prefix 去重並保持原順序,得到列表  ['3', '4', '1', '7', 'c', 'b', '8', 'f', '0', '9', 'd', 'e', '2', 'a'] 將 2 中列表迴圈順序放入包含 5 個子列表的列表中,得到random_key_list:  [['3', 'b', 'd'], ['4', '8', 'e'], ['1', 'f', '2'], ['7', '0', 'a'], ['c', '9']] 將 suffix 字串(16 進位制)逐位轉化為 10 進位制,得到 [10, 8] 將 4 中列表逐位與 [36,0] 做乘法和運算並與 a 的四捨五入結果相加, n= 51 + 36*10 + 0*8=419 q=[1,2,5,10,50], 用 q 對 n 做分解(n=50*13+10*0+5*1+2*1+1*1),將其因數倒序賦值給 p,p=[0,2,1,1,8] 從 random_key_list 右側開始隨機取值,次數為 p 中數值,拼成字串 sub_key,sub_key='9ccccc997288' 至此,我們完成了整個分析過程,我們又有了新發現: 按照前邊的抓包過程,其實不需要真的下載圖片,只需執行 1、3、4 步就可以得到目標資料了。步驟 1 也可以不要,只需 3、4 即可,但是少了步驟 1, 我們還需要額外請求一次 cookie,所以還是保留 1, 這樣也偽裝的像一點嘛。 相同的 challenge,每次運算都可以得出不同的 validate 和 seccode,那麼問題來了:到底服務端是怎麼根據 challenge 驗證其他資料是否有效呢? 總結一下,對於驗證碼,本文只是提供了一種新的思路,利用了網站開發過程中的一點小疏漏,而最後的滑動驗證碼也只是分析了 offline 模式的驗證方法。不要指望所有驗證碼都可以能繞過,沒有阿登高地,二戰德國就不打法國了麼?只要覺得有價值,即使正面面對驗證碼,作為爬蟲工程師建議也就一句話:不要慫,就是幹!