本篇主要講述驗證碼的驗證流程,包括如何驗證碼的實現、如何獲取驗證碼、識別驗證碼(這篇是人來識別,機器識別放在下篇)、發送驗證碼。一樣以一個例子來講明。目標網址 http://icp.alexa.cn/index.php(查詢域名備案信息)php
1.驗證碼的實現:python
簡單的說,驗證碼就是一張圖片,圖片上有字符串。網站是如何實現的呢?有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()
代碼說明:cookie
a).PIL是python的圖片庫模塊,須要本身安裝網絡
b).ImageFont.truetype()是選擇字體dom
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).轉載請註明出處