JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

jb發表於2018-06-14

前言

最近在看怎麼繞過圖形驗證碼,雖然有思路,但是有點吃力,估計還要折騰幾天,心裡就想吐槽:驗證碼真難搞的;
這不,既然現在還沒搞定驗證碼,那要不看看驗證碼是怎麼生成的?原理是怎麼處理的?
於是乎就有了此篇,寫的不是很好,求各大神指教下;

前幾天本來也想自己搞個網站,就去了解下備案是什麼鬼,然後就找到這麼一個網站:
http://icp.alexa.cn/index.php

用來查詢備案的,這裡面也有驗證碼,那就用這個網站先玩玩吧;

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

對了,這裡的驗證碼指的是圖形驗證碼

流程

正常的流程:
驗證碼於伺服器端生成,傳送給客戶端,並以影象格式顯示。
客戶端提交所顯示的驗證碼,客戶端接收並進行比較,若比對失敗則不能實現登入或註冊,反之成功後跳轉相應介面;

如果真這麼簡單,是不是用窮舉的方式就能破解了?

有web基礎的人可能會知道,每個瀏覽器都會有cookie,是作為這次回話的唯一標示;

cookie

防止有同學不知道,先簡單介紹下cookie:

1. 什麼是Cookie,它的用途是什麼?
Cookies是一些儲存在使用者電腦上的小檔案。
它是被設計用來儲存一些站點的使用者資料,這樣能夠讓伺服器為這樣的使用者定製內容,後者頁面程式碼能夠獲取到Cookie值然後傳送給伺服器。
比如Cookie中儲存了所在地理位置,以後每次進入地圖就預設定位到改地點即可。

2. Cookie是什麼時候生成的,完整的生成、傳遞和使用過程是怎麼樣的?
寫資料到Cookie中通常是在一個頁面被載入的時候,比如提交按鈕被按下,後臺處理完請求跳轉到相應頁面後會把Cookie值帶回來;
如下是一個例子:

  • 在瀏覽器位址列輸入了一個站點,然後瀏覽器會傳送請求到站點的Web伺服器請求該頁面
  • 與此同時,瀏覽器會在個人電腦上行查詢和該站點對應的Cookie檔案,如果發現了就會把裡面的鍵值對內容全部傳送給Web伺服器,如果沒找到則不傳送。
  • 頁面伺服器接收到了Cookie的資料後,可以利用這些資料決定返回到前臺的內容
  • 如果沒鍵值對被Web伺服器接收到,站點就知道該使用者沒有訪問過,就會為這個訪問地址新建一個ID並且傳送一些鍵值對給前臺,這些值會被放在這次響應中的Header帶給瀏覽器,瀏覽器端於是有了Cookie的值
  • 任何到服務的訪問和資料請求都可能很對Cookie中的鍵值對進行修改

實現

大致瞭解下cookie,不至於那麼不清不楚;那就繼續拉;

一般來說,每次訪問網址,瀏覽器都會把cookie傳送給伺服器,而驗證碼就是和這個cookie繫結在一起的;
舉個例子:

  • 現在有網址T,有使用者A和B兩個人同事訪問T
  • T給A返回的驗證碼是X,給B返回的驗證碼是Y,這兩個驗證碼都正確
  • 如果A輸入B的驗證碼,是驗證不通過的

那伺服器怎麼區分A和B?那就是用cookie;

cookie是標示唯一身份的,比如有些網站,登入一次後會自動登入,但是如果清除了cookie,就無法自動登入了,而且這cookie是個別人不一樣的;

說到這裡,伺服器後臺生成驗證碼的流程就很容易理解了:

  • 先隨機生產一個隨機字串
  • 然後和cookie繫結
  • 再寫到圖片上返回給你

那,怎麼生成一個圖片驗證碼?

生成圖片驗證碼

from PIL import Image,ImageDraw,ImageFont
#PIL是python的圖片庫模組
import random
#隨機函式

width = 80
height = 40
font = ImageFont.truetype('C:\\Windows\\Fonts\\STFANGSO.ttf', 28)
#選擇字型
image = Image.new("RGB",(width,height),(120,10,200))
#新建一個Image,背景色是黑色(000),如果需要別的顏色,自己修改即可;圖片大小就是開始定義的80X40
draw = ImageDraw.Draw(image)
#建立一個可以在給定影象上繪圖的物件
for t in range(4):
    #4是代表幾位,需要需要4位數字的驗證碼,這裡輸入4,需要10位,就10
    draw.text((20*t,10), repr(random.randint(0, 9)), font=font, fill=(255, 255, 255))
    #random.randint(0, 9),隨機數,0-9
    #第一個引數代表位置,第二個代表內容,第三方代表字型,第四個代表字型顏色
image.show()
#顯示圖片
複製程式碼

效果圖:

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

驗證碼就這樣生成啦~

驗證碼的獲取

回到一開始的網站:http://icp.alexa.cn/index.php

開啟後發現,網頁自動就顯示驗證碼了,有辦法獲取到這個驗證碼嗎?F12重新整理一波試試

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

有一個這樣的請求,點選後的確就是驗證碼的圖片了;

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

連結如下:
http://www.alexa.cn/api/icp/vcode?host=hcainfo&flag=f419510445e39e61cc32a7efd7552ed1&R=0.37766116804135375

從連結分析,有3個引數:

  • host=hcainfo
  • flag = f419510445e39e61cc32a7efd7552ed1
  • R=0.37766116804135375

嗯,一臉懵逼,看不懂,先不管,我們來測試下,看看這3個引數哪個是必須的;
怎麼測試?簡單,直接copy上面的url,在瀏覽器開啟,然後一個個引數刪除,如:

  • http://www.alexa.cn/api/icp/vcode?host=gxcainfo&flag=8f7e4e73eb36c20033ed825238266823
  • http://www.alexa.cn/api/icp/vcode?host=gxcainfo
  • http://www.alexa.cn/api/icp/vcode
  • http://www.alexa.cn/api/icp/vcode?host=hcainfo&flag=&R=

還可以多試試排列組合,最終得出一個結論:host=gxcainfo是必須的,flag跟R不是必須的,空或者沒有引數,都不影響獲取驗證碼;

但是,能獲取到驗證碼,不代表該驗證碼是可用的,因為如果沒有與某些類似cookie的東西繫結在一起的話,那它本身就只是圖片而已,沒特殊含義,也不具備驗證功能;

那host的值呢?嘗試了一下,把host的值改成其他亂七八糟的,也一樣獲取不到驗證碼,這裡面啥情況;

把網頁重新整理,每次重新整理,host的值都在變化,比如ahcainfo、gxcainfo、ecainfo,如果不重新整理網頁,host的值則一直不變;
通過上面3個host的值,發現一點疑問,都是8個字母,而且後面六位都是cainfo,只有前面兩個在變;ah\gx,給人感覺就是安徽跟廣西?但是ec想不出,怎麼感覺,有點像省份的意思?
親自試了下,修改成gd,hn也能獲取到驗證碼,莫非真是省份?那ec是啥??

這裡繼續猜就沒意義了,但可知的是:
1)沒有host這個值就獲取不到驗證碼;
2)及時有host,值不對也是獲取不到驗證碼;
3)那是不是說明,驗證碼是跟這個值繫結的?

url引數就到這裡了,接下來請求頭吧~
沒太特別,直接把整個請求頭copy過來用就行了:

import requests
from PIL import Image
import io

getCode_url = "http://www.alexa.cn/api/icp/vcode?host=hcainfo&flag=f419510445e39e61cc32a7efd7552ed1&R=0.37766116804135375"
header = {
    "Referer":"http://icp.alexa.cn/",
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
}

response = requests.get(getCode_url,headers=header).content
image = Image.open(io.BytesIO(response))
image.show()
複製程式碼

這上面的程式碼,好像沒什麼特別,應該不需要說明的;

執行的結果就是:

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

整體的程式碼:

import requests
from PIL import Image
import io

def getCode(url):
    print("獲取驗證碼")
    getCode_url = "http://www.alexa.cn/api/icp/vcode?host=jlcainfo&flag=61684048f21350aa2767b82315a0f487&R=0.5917477648057996"
    header = {
        "Referer":"http://icp.alexa.cn/",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
    }

    response = requests.get(getCode_url,headers=header).content
    image = Image.open(io.BytesIO(response))
    print("獲取驗證碼成功")
    image.show()

def checkcode(url,code):
    print("開始檢查驗證碼")
    checkcode_url = "http://www.alexa.cn/home/index/query?token=1a63a83b2xwDdXlEmmIldi-Cx729izemOE54BDjY8jnT-JCvU3Atg0W1gCrBVbMSs-O&flag=61684048f21350aa2767b82315a0f487&host=jlcainfo&flag&domain=qq.com&type=icp&type=icp&vcode="+code
    header = {
        "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Encoding":"gzip, deflate",
        "Accept-Language":"zh-CN,zh;q=0.9",
        "Connection":"keep-alive",
        "Cookie":"SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1; exi_query_history=nseyfqfp4SgNKMagNO-DctlkEDqameDvLLDbwUIuYCiHNkoLJPniHTUs0RIAq5jNEZ8ojjeoe8W8y1Df6vuMiy8r-H37690i99d0LZ3iyTWMVmstIOcGqb5H-DY1k2Gn3FNdj02TwpVtlca1b1lrvrdSfE-HbUGwP3Lfex0D9Hzeu48-N",
        "Host":"www.alexa.cn",
        "Referer":"http://icp.alexa.cn",
        "Upgrade-Insecure-Requests":"1",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
    }
    response = requests.get(checkcode_url,headers=header)
    print(response.content)
    print(response.status_code)


if __name__ == "__main__":
    getCode("qq.com")
    code = input("請輸入驗證碼:")
    checkcode("qq.com",code)
複製程式碼

本來是想弄個,自動拉取驗證碼圖片,手動輸入後自動拉取結果,但是執行的時候,還是被網站識別出來了,想了很久還沒想明白,算了留下了一個爛攤子吧~

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

小結

本文主要介紹了驗證碼是怎麼生成的,本來也想結合做個機器識別的,但在除錯過程,依然被網站識別出來了,折騰半天,目前無果,先棄療,後面想到原因再來更新吧~

謝謝大家~

JB的Python之旅-爬蟲篇-圖形驗證碼(3)-- 驗證碼的生成了解下

相關文章