Python 爬蟲入門(四)—— 驗證碼上篇(主要講述驗證碼驗證流程,不含破解驗證碼)

  本篇主要講述驗證碼的驗證流程,包括如何驗證碼的實現、如何獲取驗證碼、識別驗證碼(這篇是人來識別,機器識別放在下篇)、發送驗證碼。一樣以一個例子來講明。目標網址 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).轉載請註明出處

相關文章
相關標籤/搜索