看到網(wǎng)上有很多火車票查詢的小腳本漱受,參考一下,發(fā)現(xiàn)很多都已經(jīng)不能再運行了骡送,據(jù)說12306接口返回的數(shù)據(jù)格式更新比較快拜效,這里自己也寫了一個。
環(huán)境
- Mac osx
- python3.6
- pycharm
效果圖
編碼
- 安裝腳本用到的模塊
requests各谚, 用于請求12306網(wǎng)站網(wǎng)址
docopt紧憾, 解析命令行參數(shù)
prettytable, 數(shù)據(jù)用表格的形式打印在終端
colorama昌渤, 為打印在表格中的數(shù)據(jù)著色
安裝方式赴穗,直接用pip命令就好:
pip install requests prettytable docopt colorama
下面先來介紹一下prettytable docopt colorama這三個模塊
docopt
python命令行參數(shù)解析工具有很多,這里參考別的查票腳本用的docopt膀息,為了對這個模塊進行了解學習般眉,本篇文章也用了這個模塊,首先針對本文潜支,我們要查詢火車票信息甸赃,肯定要輸入出發(fā)地點,到達地點冗酿,出發(fā)日期埠对,以及要查詢的票的種類络断,于是我們需要的命令行模型如下:
python tickets.py [-gdtrkz] <from> <to> <date>
- <from> 出發(fā)地
- <to> 目的地
- <date> 日期
- [-gdtrkz] 車票類型(對應)
看一下下面的代碼
#coding=utf-8
"""
Usage:
python tickets.py [-gdtrkz] <from> <to> <date>
"""
from docopt import docopt
arguments = docopt(__doc__)
print(arguments)
終端運行上面的代碼結(jié)果如下:
由上面的測試可以看出,docopt能從注釋中的Usage下面的命令解析出一個字典项玛,“[ ]”中的是選項貌笨,一般不寫代表全部,寫了代表查詢某一選項對應的數(shù)據(jù)襟沮,
若是選項輸入錯誤锥惋,不會拋出異常,只會出現(xiàn)以下提示:
Usage:
python tickets.py [-gdtkz] <from> <to> <date>
“< >”中的是參數(shù)开伏,如本例中的<from>,<to>,<date>,這里的參數(shù)是不能少的膀跌,上面提到的選項少了,是可以查詢到數(shù)據(jù)的固灵,這里的參數(shù)少了捅伤,雖然不會報錯,但是不可能有數(shù)據(jù)怎虫。
為了代碼的可讀性暑认,一般注釋中除了Usage(用法)之外,還有參數(shù)說明大审,使用方式等蘸际,比如:
"""
Usage:
python tickets.py [-gdtrkz] <from> <to> <date>
Options:
-h,--help 幫助菜單
-g 高鐵
-d 動車
-t 特快
-r 高級軟臥
-k 快速
-z 直達
Example:
#查詢5月10日北京到上海所有車次
python tickets.py 北京 上海 2017-05-10
#查詢5月10日南京到上海的動車和高鐵
python tickets.py -dg 南京 上海 2017-05-10
"""
prettytable
這個就不多說了,這里有個對它的用法介紹的很詳盡的文章
http://www.cnblogs.com/Mr-Koala/p/6582299.html
colorama
這是個對終端輸出的文本進行著色的模塊徒扶,直接上代碼圖(官方示例):
知道這些用法就足夠本腳本使用了粮彤,模塊的介紹到此結(jié)束,下面正式進入正題姜骡。
網(wǎng)頁接口的獲取
通過chrome監(jiān)聽點擊查詢按鈕后發(fā)送的請求如下
從這個請求我們需要知道导坟,這個一個get請求(嗯,確實是get)圈澈,請求的地址是:https://kyfw.12306.cn/otn/leftTicket/query 惫周,需要的參數(shù)有四個:
- leftTicketDTO.train_date=2017-05-10 車票日期2017-05-10
- leftTicketDTO.from_station=HZH 起點站HZH
- leftTicketDTO.to_station=NJH 終點站NJH
- purpose_codes=ADULT 車票類型ADULT(成人票)
起點站和終點站使用站點的字母縮寫表示的
車站對應的字母縮寫在哪里却音?狈究??癞志?
在剛進入車票查詢頁面時啥么,在頁面加載的js文件中登舞,有下面的鏈接
https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9006
這里返回的是所有的車站和對應縮寫的數(shù)據(jù)
import re
import requests
from pprint import pprint
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9006'
response = requests.get(url,verify=False)
stations = re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)',response.text)
pprint(dict(stations),indent=4)
通過正則表達式,匹配所有的車站和對應的縮寫悬荣,并轉(zhuǎn)換成字典格式菠秒。
因為所有的地點和對應的縮寫,變化的可能性不大氯迂,將上面獲取的字典數(shù)據(jù)保存為一個文件践叠,備用言缤。
下面繼續(xù)看查詢車票時返回的數(shù)據(jù):
通過這個json數(shù)據(jù),可以獲取到所有車次的信息酵熙,關鍵在于如何對現(xiàn)有的json進行解析轧简,獲取到所有車次的信息驰坊?ps:網(wǎng)上看了其他人寫的一些查詢車票信息的腳本匾二,之前的車票信息每個字段對應一個值,標準的json形式:
現(xiàn)在的json數(shù)據(jù)每列車的信息都在一串字符串中拳芙,思來想去察藐,沒有找到什么好辦法,唯一發(fā)現(xiàn)的規(guī)律就是每個字段用“|”分割舟扎,現(xiàn)在的做法就是將該段字符串以“|”分割成一個列表分飞,從列表中去數(shù)需要的字段對應的位置。
主要代碼如下:
def trains(self):
for raw_train in self.available_trains:
raw_train_list = raw_train.split('|')
train_no = raw_train_list[3]
initial = train_no[0].lower()
duration = raw_train_list[10]
if initial in self.options:
train = [
train_no,
'\n'.join([Fore.LIGHTGREEN_EX + self.available_place[raw_train_list[6]] + Fore.RESET,
Fore.LIGHTRED_EX + self.available_place[raw_train_list[7]] + Fore.RESET]),
'\n'.join([Fore.LIGHTGREEN_EX + raw_train_list[8] + Fore.RESET,
Fore.LIGHTRED_EX + raw_train_list[9] + Fore.RESET]),
duration,
raw_train_list[-4] if raw_train_list[-4] else '--',
raw_train_list[-5] if raw_train_list[-5] else '--',
raw_train_list[-14] if raw_train_list[-14] else '--',
raw_train_list[-12] if raw_train_list[-12] else '--',
raw_train_list[-7] if raw_train_list[-7] else '--',
raw_train_list[-6] if raw_train_list[-6] else '--',
raw_train_list[-9] if raw_train_list[-9] else '--',
]
yield train
最后附上代碼地址https://github.com/lexyhp/12306
關于如何從json數(shù)據(jù)中取出需要的字段睹限,歡迎有更好方法的朋友留言譬猫,感謝!羡疗!