12306自動刷票下單-登錄篇


12306網站推出圖片驗證碼以后,對於搶票軟件就提出了更高的要求,本篇並不涉及自動識別驗證碼登錄(主要是博主能力所限),提供一個途徑-打碼平台,這個幾乎是可以破解所有驗證碼了,本篇主要是分享一下12306網站登錄的流程的學習,勿吐槽,有問題請指正,博主也是剛開始接觸爬蟲,大家共勉共勉。

廢話不多說了,直接干吧

 這里寫圖片描述

首先打開12306登錄頁面https://kyfw.12306.cn/otn/login/init

 這里寫圖片描述

輸入賬號密碼直接登錄,會轉入到下面的頁面https://kyfw.12306.cn/otn/index/initMy12306

 這里寫圖片描述

紅線划掉的就是用戶名,那么我們最終就是要訪問這個網頁查找到我們的用戶名,簡單吧

 這里寫圖片描述

好了,不鬧了,看一下我們整個登錄過程中的請求吧

 這里寫圖片描述

這里缺少了打開登錄頁面的請求,如果你沒有發現,那我剛說了你就應該發現,這個登錄頁面是必須的,這里說一下我的理解,登錄過程中我們是要發送驗證碼的,驗證碼作為獨立的請求發送,那么服務器是要知道這個驗證碼是由張三發過來的還是由李四發過來的,所以當我們打開登錄頁面的時候,服務器會記錄session或者cookie,我們之后的請求都通過攜帶cookie讓服務器知道是我張三發送的請求,而不是李四。大概就是這么回事,可能說的不太對,我也就理解了這么點,見諒見諒。

這里我們使用requests庫,不要太方便

import requests # 一個提供UserAgent的庫,不用自己再去搞那么多了,方便 from fake_useragent import UserAgent # 禁用安全請求警告 from urllib3 import disable_warnings from urllib3.exceptions import InsecureRequestWarning disable_warnings(InsecureRequestWarning) session = requests.session() # 設置不驗證SSL,你應該看到了HTTPS session.verify = False ua = UserAgent(verify_ssl=False) # 請求頭,最最基礎的反爬偽裝 headers = { "User-Agent": ua.random, "Host":"kyfw.12306.cn", "Referer":"https://kyfw.12306.cn/otn/passport?redirect=/otn/" } # 打開登錄頁面 url = "https://kyfw.12306.cn/otn/login/init" session.get(url, headers=headers) 

requests庫的session會為我們保存cookie信息,只要我們繼續使用session請求即可。

這里要先解釋一下,我使用的是Chrome瀏覽器,但是很多請求里面確看不到response數據,真的很操蛋,我寫的時候每一個請求都用代碼打印出來,很痛苦,

因為想寫篇博客,要給大家截圖,所有裝了個虛擬機Windows xp,安裝了Fiddler才看到一些信息,我還是習慣用Chrome,我盡量按照Chrome的方式去說明。 這里寫圖片描述

在所有的請求里面,我們主要關注Type等於document和xhr的請求,按照從上到下的方式就是整個流程中請求的先后順序。我們可以看到init后面uamtk和captcha_js.js?_=1510993251087, 這里寫圖片描述

很明顯這個是我們打開登錄頁面時發送的請求,服務端告訴我們:你還沒登錄呢,廢話我只是打開登錄界面,當然沒登錄了。不過我們大概知道了,發送https://kyfw.12306.cn/passport/web/auth/uamtk這個請求,服務器會給我們反饋一些登錄信息。后面那個請求很明顯是js,我們暫時不用管。

下面是這個xhr請求:https://kyfw.12306.cn/passport/captcha/captcha-check 這里寫圖片描述

這個是發送驗證碼的請求,12306的驗證碼屬於坐標型驗證碼,所有我們看到發送的是一段坐標,在這個請求上面我們看到https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.9919795512111436 這里寫圖片描述

哦,這個是請求驗證碼的,要發送驗證碼請求,自然要先獲取驗證碼嘍,多請求幾次發現表單里除了最后一個隨機數以外,其他的數據沒有變化。接下來就是驗證碼的坐標了

 這里寫圖片描述 

首先是找原點坐標,這個跟正常登錄發送的坐標對比一下,大概就能確定,然后是確定坐標的拼接,x1,y1還是y1,x1,這個也是和正常登錄發送的坐標大概對比一下就能確定了,我這里只說大概原理,具體情況自己去嘗試一下。下面上代碼

驗證碼函數

def captcha(): # 請求數據是不變的,隨機數可以使用random.random() data = { "login_site": "E", "module": "login", "rand": "sjrand", "0.17231872703389062":"" } # 獲取驗證碼 param = parse.urlencode(data) url = "https://kyfw.12306.cn/passport/captcha/captcha-image?{}".format(param) response = session.get(url, headers=headers) if response.status_code == 200: # 獲取驗證碼並打開,然后...手動找一下坐標吧,我開始就說了不涉及自動識別驗證碼的 file = BytesIO(response.content) img = Image.open(file) img.show() positions = input("請輸入驗證碼: ") # 發送驗證碼 data = { "answer": positions, "login_site": "E", "rand": "sjrand" } url = "https://kyfw.12306.cn/passport/captcha/captcha-check" response = session.post(url, headers=headers, data=data) if response.status_code == 200: result = json.loads(response.text) print(result.get("result_message")) # 請求成功以后返回的code是4,這個看請求信息就知道了 return True if result.get("result_code") == "4" else False return False 

驗證碼通過以后,就要發送賬號登錄了,繼續看下面的請求https://kyfw.12306.cn/passport/web/login 這里寫圖片描述

賬號密碼登錄成功,看到沒?好了

 這里寫圖片描述 

還沒完呢,為啥捏?還沒看到initMy12306這個請求呢 繼續往下擼吧,https://kyfw.12306.cn/passport/web/auth/uamtk,這個請求熟悉不?不熟悉的去翻前面 這里寫圖片描述

對比一下,前后不一樣吧,驗證通過,開心吧,哈哈! 咦!好像還多了點東西哦,newapptk什么鬼呢?不明白,繼續看下面

敲黑板!!!是看后面的請求,你在看哪個下面,睡着了嗎?https://kyfw.12306.cn/otn/uamauthclient 這里寫圖片描述

驗證通過,不過重點我都圈起來了,還看不見嗎?tk,你再看看上面newapptk,明白了嗎?上面的請求服務器返回了newapptk數據,我們通過下面的請求把這個值賦給tk,再發送給服務器,然后服務器告訴我們驗證通過,

apptk和前面的tk一樣,有什么用呢?不知道,那就往下看,哎呀,到站了 https://kyfw.12306.cn/otn/index/initMy12306 這里寫圖片描述

已經成功了,有我們的賬號名了,就是紅點的地方,我當然不會給你看我的賬號名了,到這里就真的完了,apptk沒用到?沒用就沒用唄,終於結束了 這里寫圖片描述

我還要貼一下代碼,差點忘了

完整的代碼

# -*- coding: utf-8 -*- import json from urllib import parse from io import BytesIO from config import * import requests from PIL import Image from fake_useragent import UserAgent # 禁用安全請求警告 from urllib3 import disable_warnings from urllib3.exceptions import InsecureRequestWarning disable_warnings(InsecureRequestWarning) session = requests.session() session.verify = False ua = UserAgent(verify_ssl=False) headers = { "User-Agent": ua.random, "Host":"kyfw.12306.cn", "Referer":"https://kyfw.12306.cn/otn/passport?redirect=/otn/" } def login(): # 打開登錄頁面 url = "https://kyfw.12306.cn/otn/login/init" session.get(url, headers=headers) # 發送驗證碼 if not captcha(): return False # 發送登錄信息 data = { "username":USER_NAME, "password":PASSWORD, "appid":"otn" } url = "https://kyfw.12306.cn/passport/web/login" response = session.post(url, headers=headers, data=data) if response.status_code == 200: result = json.loads(response.text) print(result.get("result_message"), result.get("result_code")) if result.get("result_code") != 0: return False data = { "appid":"otn" } url = "https://kyfw.12306.cn/passport/web/auth/uamtk" response = session.post(url, headers=headers, data=data) if response.status_code == 200: result = json.loads(response.text) print(result.get("result_message")) newapptk = result.get("newapptk") data = { "tk":newapptk } url = "https://kyfw.12306.cn/otn/uamauthclient" response = session.post(url, headers=headers, data=data) if response.status_code == 200: print(response.text) url = "https://kyfw.12306.cn/otn/index/initMy12306" response = session.get(url, headers=headers) if response.status_code == 200 and response.text.find("用戶名") != -1: return True return False def captcha(): data = { "login_site": "E", "module": "login", "rand": "sjrand", "0.17231872703389062":"" } # 獲取驗證碼 param = parse.urlencode(data) url = "https://kyfw.12306.cn/passport/captcha/captcha-image?{}".format(param) response = session.get(url, headers=headers) if response.status_code == 200: file = BytesIO(response.content) img = Image.open(file) img.show() positions = input("請輸入驗證碼: ") # 發送驗證碼 data = { "answer": positions, "login_site": "E", "rand": "sjrand" } url = "https://kyfw.12306.cn/passport/captcha/captcha-check" response = session.post(url, headers=headers, data=data) if response.status_code == 200: result = json.loads(response.text) print(result.get("result_message")) return True if result.get("result_code") == "4" else False return False if __name__ == "__main__": if login(): print("Success") else: print("Failed") 

執行結果添一下,好累,我要去吃飯了

 這里寫圖片描述

我討厭寫文章,真的,這是我的第一篇爬蟲文章了,寫代碼過程用了兩個小時,寫文檔用了整整一個下午,是的,一個下午。哎,我果然還是不擅長寫文章,之前想要寫python微信公眾號二次開發,結果寫了好幾次都是開頭就刪掉了,文才真的很爛,而且通篇邏輯混亂,我自己都看不下去。大家湊合看吧,希望多提意見,我一定虛心接受,慢慢改正。

 這里寫圖片描述

 

=======================================長長的分割線 2018-2-1 23:20:13=======================================

針對大家一直在問的登錄問題,我上周查看過,發現12306有一些改版,多出了兩個請求,主要是增加了兩個cookies,RAIL_DEVICEID和RAIL_EXPIRATION,

https://kyfw.12306.cn/otn/HttpZF/GetJS和https://kyfw.12306.cn/otn/HttpZF/logdevice?,通過一個js生成請求參數,然后獲取上面兩個cookie數據,之前看的時候發現GetJS這個請求返回的js腳本看不懂,分析不出來,就擱置了,這兩天想再回過頭來看一看,結果發現12306好像又改回去了,之前的又能正常登陸了,而且這個GetJS請求也沒有js腳本返回了,logdevice請求也沒了,估計是因為之前搞的不穩定,有時候正常登陸都有問題,所以暫時回退版本了吧。現在的這個教程應該是正常可用了。不過估計等12306調試穩定后,就又不行了,樓主還不會前端編程,所以js是薄弱點,如果12306再更新的話也搞不定這塊。好在之前的那個GetJS請求腳本我保存了一份,上傳上來希望熟悉js的同學能幫忙解密一下https://files.cnblogs.com/files/small-bud/test.js,大概就是后面的這些參數搞不定,可惜請求的參數信息我沒有保存下來

===================================================================================================================== 

 


如果你覺得我的文章還可以,可以關注我的微信公眾號:Python爬蟲實戰之路
也可以掃描下面二維碼,添加我的微信號

 
公眾號

 

 
微信號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM