本篇主要講述驗證碼的驗證流程,包括如何驗證碼的實現、如何獲取驗證碼、識別驗證碼(這篇是人來識別,機器識別放在下篇)、發送驗證碼。同樣以一個例子來說明。目標網址 http://icp.alexa.cn/index.php(查詢域名備案信息)
1.驗證碼的實現:
簡單的說,驗證碼就是一張圖片,圖片上有字符串。網站是如何實現的呢?有WEB基礎的人可能會知道,每個瀏覽器基本都有cookie,作爲這次回話的唯一標示。每次訪問網站,瀏覽器都會把這個cookie發送給服務器。驗證碼就是和這個cookie綁定到一起的。如何理解呢?舉個例子,現在有網站W,有A和B兩個人,同時訪問W,W給A返回的驗證碼是X,給B返回的驗證碼是Y,這兩個驗證碼都是正確的,但是如果A輸入了B的驗證碼,肯定驗證不通過。那服務器是怎麼區分A和B呢,就是用到的cookie。再舉個例子,有些網站你登錄一次之後,下次繼續訪問可能就自動登陸了,也是用cookie來標示唯一身份的,如果清除了cookie也就無法自動登陸了。cookie具體是什麼生成的,我們不必關心,只需要知道是一長串字符串就行了,你的和別人的都不一樣。(例子中的目標網址並不是用cookie,而是用的其他方式,所以可能會存在一些BUG)
服務器後臺生成驗證碼的流程就很容易理解了:首先,生成一個隨機字符串,然後和cookie綁定,然後寫到圖片上返回給你。那麼,如何生成一個圖片驗證碼呢?下面是一個簡單的生成驗證碼源碼:
from PIL import Image import ImageFilter,ImageDraw,ImageFont import random width = 80 height = 40 font = ImageFont.truetype('C:\\Windows\\Fonts\\AdobeFangsongStd-Regular.otf', 28) image = Image.new("RGB",(width,height),(0,0,0)) draw = ImageDraw.Draw(image) for t in range(4): draw.text((20*t,10),`random.randint(0,9)`,font=font,fill=(255,255,255)) image.show()
代碼說明:
a).PIL是python的圖片庫模塊,需要自己安裝
b).ImageFont.truetype()是選擇字體
c).Image.new("RGB",(width,height),(0,0,0))新建一個Image,背景色是白色((0,0,0)就代表的顏色),如果需要別的顏色,可自己查詢顏色代碼。window自帶的畫板就可以看到:
d).random.randint(0,9)隨機數 範圍大於等於0,小於等於9
e).draw.text((20*t,10),`random.randint(0,9)`,font=font,fill=(255,255,255),anchor=False) 第一個參數代表位置,帶二個代表內容,第三個代表字體,第四個代表字體顏色
f).image.show()顯示圖片,第一詞會提示選擇默認圖片查看器。
運行結果如下圖:
2).驗證碼的獲取
a).分析目標網站,可以看到當鼠標點擊驗證碼那個輸入框時會顯示驗證碼,如圖:
那麼獲取驗證碼的請求是什麼?以及請求發送的時間?(驗證碼顯示的時間不一定是驗證碼獲取的時間,雖然這個例子中是,以爲驗證碼可能是頁面剛開始的時候一起加載的,只是一直被隱藏)。火狐瀏覽器F12打開控制檯,找到網絡標籤,刷新頁面,可以看到如下圖所示:
並沒有發現獲取驗證碼的請求,那麼我們點擊驗證碼的那個輸入框,發現多了一個請求,沒錯,這就是獲取驗證碼的請求。
b).下面我們開始分析這個請求,首先點擊這個請求,可以看到如下圖所示:
完整URL:http://icp.alexa.cn/captcha.php?q=sina.com.cn&sid=82&icp_host=sxcainfo。可以看到三個參數:
q=sina.com.cn:查詢的域名
sid=82:這個ID暫時不知道是什麼,後面查看JS源代碼會看到
icp_host:這個暫時也不知道。
這三個參數,那些是必須的呢?可以一個一個測試。測試方法就是刪掉某個元素,然後再發請求。測試發現,三個參數缺省都可以獲取到驗證碼,獲取到驗證碼,不代表驗證碼可用,因爲,沒有與某些類似cookie的值綁定到一起,它就和圖片沒有任何區別,不具備驗證的功能。經我測試,(測試很簡單,不過要用到後面的東西,看完這篇,就知道怎麼測了),這三個參數都需要。在測試的過程中,我發現了sid就是一個隨機數,沒有什麼特殊含義,基本可以確定可以隨便輸入:js代碼如下:
icp_host的取值有很多:sccainfo、ahcainfo、jscainfo......有沒有發現什麼規律?首先八個字母,最後六個都是cainfo,那麼前面兩個代表什麼?sc=四川、ah=安徽、js=j江蘇。所以,我們可以猜測這個是省份的簡寫。那這個值有什麼用呢?作用一,如果不按這個規則輸入字符串(比如,aaaaaaaa),就獲取不到驗證碼;作用二,驗證碼就是和這個綁定的。也就是說,你獲取驗證碼的時候用sccainfo,那麼驗證的時候也要用sccainfo。
分析完參數,然後再分析請求頭,方法和參數的分析方法一樣,一個個刪除,看能不能獲取正確的結果。這個時候,可以自己寫python代碼測試,具體代碼如下:
#encoding=utf8 import urllib2 from PIL import Image import cStringIO getCode_url = "http://icp.alexa.cn/captcha.php?q=163.com&sid=0&icp_host=hncainfo" header={"Referer":"http://icp.alexa.cn/captcha.php?q=163.com&sid=0&icp_host=hncainfo"} # header['Host']="icp.alexa.cn" # header['User-Agent']="Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0" header['Cache-Control']="max-age=0" request = urllib2.Request(getCode_url,headers=header) res = urllib2.urlopen(request).read() image = Image.open(cStringIO.StringIO(res)) image.show()
代碼說明:
a).cStringIO python的流模塊,無論是圖片、文本、音頻、視頻都是流文件,可以相互轉化。這裏的作用是將圖片流還原成圖片
b).header中添加參數可直接用header['']="",這樣就可以測試了。具體哪些參數必須,自己測試。
運行結果:
3).檢驗驗證碼
a).分析目標網站,尋找檢驗驗證碼的請求。我們在輸入框輸入正確的驗證碼,點擊備案查詢,如圖所示:
可以看到控制檯中多了一個請求
點擊請求,查看請求詳情:http://icp.alexa.cn/index.php?q=163.com&code=65a89c&icp_host=lncainfo
三個參數:
q=163.com:查詢的域名,必不可少
code=65a89c:驗證碼,必不可少
icp_host=lncainfo:和獲取驗證碼相對應
然後再分析header,與上面的方法一樣。這兩次檢驗不同的是:檢驗獲取驗證碼時,是自己寫代碼獲取驗證碼,然後放到網站上檢驗,驗證碼的正確性(必須保證icp_host一致);檢驗檢查驗證碼時,是用網站獲取驗證碼,填到代碼裏面,看看參數對不對。
驗證代碼如下:
#encoding=utf8 import urllib2 checkcode_url = "http://icp.alexa.cn/index.php?q=163.com&code=N3PE37&icp_host=hncainfo" header={} # header['Pragma']="Pragma" # header['Referer']="http://icp.alexa.cn/index.php?q=163.com&code=CUXWDV&icp_host=sccainfo" header['User-Agent']="Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0" request = urllib2.Request(checkcode_url,headers=header) res = urllib2.urlopen(request).read() print res
代碼說明:icp_host=hncainfo這個參數必須和你獲取驗證碼時候的參數一致
運行結果:
如果驗證碼不正確或者別的地方不一致,會返回:
到此,我們就分析完了,不過現在是把獲取和驗證放在兩個代碼中運行,怎麼放在一起呢?代碼如下:
#encoding=utf8 import urllib2 from PIL import Image import cStringIO import BeautifulSoup def getCode(domain): print "獲取驗證碼...." getcode_url="http://icp.alexa.cn/captcha.php?q="+domain+"&sid=0&icp_host=hncainfo" getcode_headers = {} getcode_headers['Referer']="http://icp.alexa.cn/captcha.php?q=163.com&sid=0&icp_host=hncainfo" getcode_headers['Cache-Control']="max-age=0" getcode_request = urllib2.Request(getcode_url,headers=getcode_headers) getcode_res = urllib2.urlopen(getcode_request).read() image = Image.open(cStringIO.StringIO( getcode_res)) print "獲取驗證碼成功" image.show() def checkcode(domain,code): # print "您輸入的驗證碼爲:"+`code` print "開始檢查驗證碼..." checkcode_url = "http://icp.alexa.cn/index.php?q="+domain+"&code="+code+"&icp_host=hncainfo" checkcode_headers={} checkcode_headers['User-Agent']="Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0" checkcode_request = urllib2.Request(checkcode_url,headers=checkcode_headers) checkcode_res = urllib2.urlopen(checkcode_request).read() if(checkcode_res.count("主辦單位名稱")>0): print "驗證成功" checkcode_soup = BeautifulSoup.BeautifulSoup(checkcode_res) print "所屬單位名稱:"+checkcode_soup.findAll("table")[0].findAll("tr")[0].findAll("td")[1].text.encode("utf8") else: print "驗證失敗" domain = raw_input("請輸入域名:") getCode(domain) code = raw_input("請輸入驗證碼:") checkcode(domain,code)
代碼說明:
a).def getCode(domain) 聲明一個函數,getCode是函數名,domain是參數
b).raw_input() 獲取用戶輸入
c).在獲取和驗證的時候,我把icp_host都寫成了hncainfo,這樣就可以保證一致。
d).encode("utf8") 對變量以utf8格式編碼
e).驗證碼要人工識別輸入
運行結果:
到此,整個驗證碼的獲取,驗證都講述完了,驗證碼的識別放在下一節。
說明:
a).代碼僅供學習交流
b).如有錯誤,多多指教
c).轉載請註明出處