1. 程式人生 > >python實現12306驗證和登入

python實現12306驗證和登入

1.獲取驗證碼


分析:這裡可以看出驗證碼的獲取地址,最後一個引數不知道是什麼意思,我們直接去掉,然後發現在瀏覽器中仍然能請求到驗證碼。

驗證碼連線:https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand

請求到的驗證碼如下:由於每次請求驗證碼地址驗證碼都會改變,所以看到的和上圖驗證碼不同,請知曉


2.登入按鈕點選

登入按鈕點選的時候會首先執行驗證碼的驗證操作,只有驗證成功後才會執行登入操作,是兩個介面

2.1 驗證碼驗證介面

通過chrome抓包工具,抓到的驗證碼驗證介面及引數介紹

url = "https://kyfw.12306.cn/passport/captcha/captcha-check"
data = {
    'login_site':'E',                     #固定的
    'rand':'sjrand',                     #固定的
    'answer':'35,100,240,35'    #驗證碼對應的座標,兩個為一組,跟選擇順序有關,有幾個正確的,輸入幾個
}

2.2 驗證碼詳細介紹

一共有8個驗證碼,驗證碼我們編號從做到右從上到下,依次為0,1,2,3,4,5,6,7(程式設計師思維,見諒),為序號轉座標做準備

# --------------------------------------------------
#              |               |                |
#      0      |       1      |        2      |       3
#              |               |                |
# --------------------------------------------------
#              |               |                |
#      4      |       5      |        6      |       7
#              |               |                |
# --------------------------------------------------


大約一個驗證碼的寬高在70左右,所以每個驗證碼的座標中點大約為,注意大約,請勿上綱上線
-------------------------------------------------------
              |                |               |
  35,35   |   105,35  |  175,35  |  245,35
              |                |               |
-------------------------------------------------------
              |                |               |
 35,105  | 105,105  | 175,105 |  245,105
              |                |               |
-------------------------------------------------------

2.3 驗證碼的驗證結果,json形式的字串

    <HashMap>
    <result_message>驗證碼校驗失敗</result_message>
    <result_code>5</result_code>
    </HashMap>

result_code結果:
4=驗證成功
5=驗證失敗
7=驗證碼過期
其他結果暫時不清楚,待嘗試

3.登入請求

3.1 登入介面及引數

loginUrl = "https://kyfw.12306.cn/passport/web/login"
    data = {
        'username':'xxxxxxxx',      #使用者名稱,這個沒什麼好說的
        'password':'oooooooo',     #登入密碼
        'appid':'otn'                       #不知道是什麼,是固定值
    }

咦?發現了什麼問題?沒有跟驗證碼有關的任何資訊,為什麼?由於12306是用session資訊驗證的,所以驗證結果資訊在session中,具體看後面的程式碼詳細註釋

3.2 登入返回結果,依然是json形式的字串

 <HashMap>
    <result_message>登入成功</result_message>
    <result_code>0</result_code>
    <uamtk>zdVRma2Gm5lZE-CJx5Lbn8rJJrQ_20-ghfTSkgpls2s0</uamtk>
    </HashMap>

4. 詳細程式碼及註釋

#_*_coding:utf-8_*_
'''
>>> time:2017-08-04
>>> __author__:swh
'''
import requests
from PIL import Image
from json import loads
import getpass
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# 禁用安全請求警告
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class LoginTic(object):
    def __init__(self):
        self.headers = {
            "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
        }
        # 建立一個網路請求session實現登入驗證
        self.session = requests.session()

    # 獲取驗證碼圖片
    def getImg(self):
        url = "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand";
        response = self.session.get(url=url,headers=self.headers,verify=False)
        # 把驗證碼圖片儲存到本地
        with open('img.jpg','wb') as f:
            f.write(response.content)
        # 用pillow模組開啟並解析驗證碼,這裡是假的,自動解析以後學會了再實現
        try:
            im = Image.open('img.jpg')
            # 展示驗證碼圖片,會呼叫系統自帶的圖片瀏覽器開啟圖片,執行緒阻塞
            im.show()
            # 關閉,只是程式碼關閉,實際上圖片瀏覽器沒有關閉,但是終端已經可以進行互動了(結束阻塞)
            im.close()
        except:
            print u'請輸入驗證碼'
        #=======================================================================
        # 根據開啟的圖片識別驗證碼後手動輸入,輸入正確驗證碼對應的位置,例如:2,5
        # ---------------------------------------
        #         |         |         |
        #    0    |    1    |    2    |     3
        #         |         |         |
        # ---------------------------------------
        #         |         |         |
        #    4    |    5    |    6    |     7
        #         |         |         |
        # ---------------------------------------
        #=======================================================================
        captcha_solution = raw_input('請輸入驗證碼位置,以","分割[例如2,5]:')
        return captcha_solution

    # 驗證結果
    def checkYanZheng(self,solution):
        # 分割使用者輸入的驗證碼位置
        soList = solution.split(',')
        # 由於12306官方驗證碼是驗證正確驗證碼的座標範圍,我們取每個驗證碼中點的座標(大約值)
        yanSol = ['35,35','105,35','175,35','245,35','35,105','105,105','175,105','245,105']
        yanList = []
        for item in soList:
            print item
            yanList.append(yanSol[int(item)])
        # 正確驗證碼的座標拼接成字串,作為網路請求時的引數
        yanStr = ','.join(yanList)
        checkUrl = "https://kyfw.12306.cn/passport/captcha/captcha-check"
        data = {
            'login_site':'E',           #固定的
            'rand':'sjrand',            #固定的
            'answer':yanStr    #驗證碼對應的座標,兩個為一組,跟選擇順序有關,有幾個正確的,輸入幾個
        }
        # 傳送驗證
        cont = self.session.post(url=checkUrl,data=data,headers=self.headers,verify=False)
        # 返回json格式的字串,用json模組解析
        dic = loads(cont.content)
        code = dic['result_code']
        # 取出驗證結果,4:成功  5:驗證失敗  7:過期
        if str(code) == '4':
            return True
        else:
            return False

    # 傳送登入請求的方法
    def loginTo(self):
        # 使用者輸入使用者名稱,這裡可以直接給定字串
        userName = raw_input('Please input your userName:')
        # 使用者輸入密碼,這裡也可以直接給定
        # pwd = raw_input('Please input your password:')
        # 輸入的內容不顯示,但是會接收,一般用於密碼隱藏
        pwd = getpass.getpass('Please input your password:')
        loginUrl = "https://kyfw.12306.cn/passport/web/login"
        data = {
            'username':userName,
            'password':pwd,
            'appid':'otn'
        }
        result = self.session.post(url=loginUrl,data=data,headers=self.headers,verify=False)
        dic = loads(result.content)
        print result.content
        mes = dic['result_message']
        # 結果的編碼方式是Unicode編碼,所以對比的時候字串前面加u,或者mes.encode('utf-8') == '登入成功'進行判斷,否則報錯
        if mes == u'登入成功':
            print '恭喜你,登入成功,可以購票!'
        else:
            print '對不起,登入失敗,請檢查登入資訊!'

if __name__ == '__main__':
    # checkYanZheng('0,3')
    login = LoginTic()
    yan = login.getImg()
    chek = False
    #只有驗證成功後才能執行登入操作
    while not chek:
        chek = login.checkYanZheng(yan)
        if chek:
            print '驗證通過!'
        else:
            print '驗證失敗,請重新驗證!'
    login.loginTo()

說明:我這裡寫的只是實現12306的整個登入流程,驗證碼需要手動輸入正確驗證碼的位置,不能自動識別驗證碼,我在網上看了一個大牛寫的fuck12306.py是用於自動解析驗證碼的,最後更新時間是去年,我還沒嘗試,如果可以的話,再發博分享。有需要的朋友可以拿去嘗試。