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是用於自動解析驗證碼的,最後更新時間是去年,我還沒嘗試,如果可以的話,再發博分享。有需要的朋友可以拿去嘗試。