12306:媽媽再也不用擔心我搶不到票了(2)
一、寫在前面
上一篇中,我們實現了 12306的模擬登入(破解驗證碼),這裡是傳輸門,本篇推文,我將帶大家進行進一步的操作車票預訂第一步—實現車票查詢功能。
二、一頓騷操作
注: 以下所有操作可以不用登入(即不需要進行我們上一步的操作)
1.車票預訂地址
https://kyfw.12306.cn/otn/leftTicket/init
- 頁面顯示
- 引數分析
四個引數: 必填資料:出發地、目的地、出發日期 選填資料:返回日期
2.頁面詳情分析
(1)谷歌瀏覽器下,F12,輸入出發地、目的地、出發日期後,點選查詢。 (2)通過上面圖片分析我們可以發現,點選查詢後,多載入了一個xhr頁面,get請求,有四個引數,返回內容為json格式資料。
- 請求
- 引數
# 出發日期
leftTicketDTO.train_date: 2018-10-12
# 出發站
leftTicketDTO.from_station: KSN
# 目的站
leftTicketDTO.to_station: BJP
# 車票型別:成人
purpose_codes: ADULT
通過引數我發現,出發站和目的站並不是我們直接輸入的漢字,而是對應站點的英文縮寫,接下來我們會講怎麼處理這個問題,大家也可以停在這裡思考一下。
- 返回資料
- 頁面實際資料
通過我們獲取到的資料與原頁面資料顯示比較,我們發現,我們想要的頁面車次資訊在
json
檔案的data
下的result
3.動手動腦敲程式碼
(1)解決站點名稱轉換問題 想了很久,也看了光城學長之前寫的方法,覺得還是太麻煩了,我想要是有全國所有站點的中文名和英文縮寫就好了,百度一下,幾經搜尋,果然有這樣的網站存在。
https://im0x.com/C/detail/1.55
那就簡單了,把資料爬取下來,然後儲存到csv檔案即可,查詢時直接訪問本地檔案,比傳送請求要簡單的多吧~
def csm():
url = 'https://im0x.com/C/detail/1.55'
date = requests.get(url)
html = date. text
# 正則提取站點中文名和英文縮寫
city_code =re.findall('[a-z]\|(.*?)\|(.*?)\|.*?\|.*?\|\d',html,re.S)
list_info = []
for i in range(len(city_code)):
list_0 = [city_code[i][0],city_code[i][1]]
print(list_0)
list_info.append(list_0)
# 檔案操作函式,和之前推文思想是一樣的
# 點選閱讀原文可檢視所有原始碼
file_do(list_info)
執行結果: 一共2766個站點和英文縮寫,等會來波騷操作。 (2)獲取查詢資料
def search_ticket(self):
train_date = input("請輸入查詢時間(2018-10-12):")
from_station = input("請輸入始發站點(中文全稱:黃石北):")
to_station = input("請輸入終點站(中文全稱:北京西):")
en_station = self.conversion(from_station,to_station)
search_ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/queryO?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes=ADULT'.format(train_date,en_station[0],en_station[1])
# print(search_ticket_url)
headers = {
"Host": "kyfw.12306.cn",
"Referer": "https://kyfw.12306.cn/otn/leftTicket/init"
}
search_response = requests.get(search_ticket_url,headers = headers)
search_result = search_response.json()
print(search_result)
站點名稱轉換:
def conversion(self,from_station,to_station):
import csv
city_station = []
with open(r'H:\city_station.csv', 'r', encoding='utf_8_sig', newline='') as city_file:
# 讀檔案
reader = csv.reader(city_file)
i = 0
for row in reader:
if i != 0:
city_station.append([row[0], row[1]])
# print(row[1])
i = i + 1
# 轉換為字典格式,方便查詢
city_station_dict = {city_station[i][0]: city_station[i][1] for i in range(len(city_station))}
# print(city_station_dict)
if from_station in city_station_dict:
from_station_en = city_station_dict[from_station]
else:
print("出發站輸入錯誤!")
if to_station in city_station_dict:
to_station_en = city_station_dict[to_station]
else:
print("目的站輸入錯誤!")
return [from_station_en,to_station_en]
執行結果: 資料拿到了,就只剩下解析資料,用好看易懂的方式表示出來了。 (3)解析資料 接下來操作參考自光城學長的推文。
原文連結:https://mp.weixin.qq.com/s/7ECRtXmsx-ioV_t_lXmo-Q
- 資料名稱分類
名稱 | 變數名 |
---|---|
車次 | checi |
始發站 | from_station |
終點站 | to_station |
出發時間 | from_time |
到達時間 | to_time |
歷時 | total_time |
出發日期 | from_datetime |
高階軟臥 | high_soft |
軟臥 | common_soft |
無座 | no_seat |
動臥 | move_down |
商務座(特等座) | special_seat |
一等座 | first_seat |
二等座 | second_seat |
硬臥 | hard_seat |
- 實操程式碼
# 解析資料
# 這個過程需要大家耐心的觀察我們獲得的資料與頁面顯示資料異同點
# 找到之間關係,然後取出乾淨的資料
for each in tick_res:
# print("-----")
need_data = re.split(r'\|預訂\|', each)[1]
need_data = need_data.split('|')
# print(need_data)
# print(len(need_data))
checi.append(need_data[1])
# print(checi)
conversion_ch_reslut = self.conversion_ch(need_data[2],need_data[3])
from_station.append(conversion_ch_reslut[0])
to_station.append(conversion_ch_reslut[1])
from_time.append(need_data[6])
to_time.append(need_data[7])
total_time.append(need_data[8])
from_datetime.append(need_data[11])
high_soft.append(need_data[-16])
common_soft.append(need_data[-14])
no_seat.append(need_data[-11])
move_down.append(need_data[-4])
special_seat.append(need_data[-5])
first_seat.append(need_data[-6])
second_seat.append(need_data[-7])
hard_seat.append(need_data[-9])
return search_res, from_station, to_station, checi, from_station, to_station, from_time, to_time, total_time, from_datetime, high_soft, common_soft, no_seat, move_down, special_seat, second_seat, first_seat, hard_seat
# 將提取到的資料用圖表的格式打印出來
# 這裡用到模組PrettyTable
# 安裝方法:cmd下 :
# pip install prettytable
def print_TicketInfo(self, search_result, raw_from_station, raw_to_station):
search_res,checi, from_station, to_station, from_time, to_time, total_time, from_datetime, high_soft, common_soft, no_seat, move_down, special_seat, second_seat, first_seat, hard_seat = self.jx(
search_result)
pt = PrettyTable(['車次','始發站','終點站','出發時間','到達時間','歷時','出發日期','軟臥','無座','動臥', '商務座', '一等座', '二等座', '硬臥'])
pt.align["車次"] = "l"
pt.padding_width = 1
print("---------從" + str(raw_from_station) + '到' + str(raw_to_station) + '共' + str(
search_res) + '個車次' + '---------')
for i in range(len(checi)):
pt.add_row([checi[i],from_station[i],to_station[i],from_time[i],to_time[i],total_time[i],from_datetime[i],common_soft[i],no_seat[i],move_down[i],special_seat[i],second_seat[i],first_seat[i],hard_seat[i]])
pt.reversesort = True
return pt
- 執行結果:
通過對比,沒有問題,到此,我們的車票查詢功能就做完了,檢視全部原始碼,點選閱讀原文。
三、後話連篇
通過本次實驗,我們實現了12306車票查詢與顯示,這個過程中比較麻煩的還是資料的清理上,需要多次對比資料才能從我們獲得的資料中拿到原始資料,另外,在顯示資料上,我們用到了prettytable這個模組,我也是第一次用,感覺還不錯,比自己print要簡單很多。 下一次在更新,也不知道是什麼時候,那時候我會帶大家實現將查詢資訊傳送到qq郵箱、手機上,看哪個簡單來哪個,(如果不難得話)順便實現購票,哈哈哈,加油。