最近我看到看到使用python實現火車票查詢,我自己也實現了,感覺收穫蠻多的,下面我就把每一步驟都詳細給分享出來。 (注意使用的是python3)
首先我將最終結果給展示出來:
在cmd命令列執行:python tickets.py -dk shanghai chengdu 20161007 > result. txt
意思是:查詢上海--成都2016.10.07 的D和K開頭的列車訊息,並儲存到result.txt檔案中;下面就是result.txt文件中的結果:
下面的將是實作步驟:
1、安裝第三方函式庫pip install 安裝:requests,docopt,prettytable
#2、docopt可以用來解析從命令列輸入的參數:
#""" Usage: test [-gdtkz] <from> <to> <date> Options: -h,--help 显示帮助菜单 -g 高铁 -d 动车 -t 特快 -k 快速 -z 直达 Example: tickets -gdt beijing shanghai 2016-08-25 """ import docopt args = docopt.docopt(__doc__) print(args) # 上面 """ """ 包含中的: #Usage: # test [-gdtkz] <from> <to> <date> #是必须要的 test 是可以随便写的,不影响解析
最終列印的結果是一個字典,方便後面使用:
3、取得列車的資訊
我們在12306的餘票查詢的介面:
url: 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version= 1.8968'
方法為:get
傳輸的參數:queryDate:2016-10-05、from_station:CDW、to_station:SHH
其中城市對應簡稱為需要另外的介面查詢得出
3.1 查詢城市對應的簡稱:
這個介面的url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name. js?station_version=1.8968'
方法是get,對返回結果利用正則表達式,取出城市名和簡稱的值(返回的值類似:7@cqn|重慶南|CRW|chongqingnan|cqn|,我們需要的就是:CRW、chongqingnan),程式碼如下
parse_stations.py:
#coding=utf-8 from prettytable import PrettyTable class TrainCollection(object): """ 解析列车信息 """ # 显示车次、出发/到达站、 出发/到达时间、历时、一等坐、二等坐、软卧、硬卧、硬座 header = '序号 车次 出发站/到达站 出发时间/到达时间 历时 商务座 一等座 二等座 软卧 硬卧 硬座 无座'.split() def __init__(self,rows,traintypes): self.rows = rows self.traintypes = traintypes def _get_duration(self,row): """ 获取车次运行的时间 """ duration = row.get('lishi').replace(':','小时') + '分' if duration.startswith('00'): return duration[4:] elif duration.startswith('0'): return duration[1:] return duration @property def trains(self): result = [] flag = 0 for row in self.rows: if row['station_train_code'][0] in self.traintypes: flag += 1 train = [ # 序号 flag, # 车次 row['station_train_code'], # 出发、到达站点 '/'.join([row['from_station_name'],row['to_station_name']]), # 成功、到达时间 '/'.join([row['start_time'],row['arrive_time']]), # duration 时间 self._get_duration(row), # 商务座 row['swz_num'], # 一等座 row['zy_num'], # 二等座 row['ze_num'], # 软卧 row['rw_num'], # 硬卧 row['yw_num'], # 硬座 row['yz_num'], # 无座 row['wz_num'] ] result.append(train) return result def print_pretty(self): """打印列车信息""" pt = PrettyTable() pt._set_field_names(self.header) for train in self.trains: pt.add_row(train) print(pt) if __name__ == '__main__': t = TrainCollection()
其中pprint這個模組能是印出來的信息,更方便閱讀:
在cmd中運行:python parse_stations.py > stations.py
就會在當前目錄下得到stations.py文件,文件中就是站點名字和簡稱,在stations.py檔案中加入"stations = "這樣就是一個字典,方便後面的取值,下面就是stations.py檔案的內容:
##3.2 現在取得列車資訊的參數已經準備齊了,接下來就是拿到列車的回傳值,解析出自己需要的資訊,例如:車次號,一等座的票數等等。 。 ,myprettytable.py#coding=utf-8 from prettytable import PrettyTable class TrainCollection(object): """ 解析列车信息 """ # 显示车次、出发/到达站、 出发/到达时间、历时、一等坐、二等坐、软卧、硬卧、硬座 header = '序号 车次 出发站/到达站 出发时间/到达时间 历时 商务座 一等座 二等座 软卧 硬卧 硬座 无座'.split() def __init__(self,rows,traintypes): self.rows = rows self.traintypes = traintypes def _get_duration(self,row): """ 获取车次运行的时间 """ duration = row.get('lishi').replace(':','小时') + '分' if duration.startswith('00'): return duration[4:] elif duration.startswith('0'): return duration[1:] return duration @property def trains(self): result = [] flag = 0 for row in self.rows: if row['station_train_code'][0] in self.traintypes: flag += 1 train = [ # 序号 flag, # 车次 row['station_train_code'], # 出发、到达站点 '/'.join([row['from_station_name'],row['to_station_name']]), # 成功、到达时间 '/'.join([row['start_time'],row['arrive_time']]), # duration 时间 self._get_duration(row), # 商务座 row['swz_num'], # 一等座 row['zy_num'], # 二等座 row['ze_num'], # 软卧 row['rw_num'], # 硬卧 row['yw_num'], # 硬座 row['yz_num'], # 无座 row['wz_num'] ] result.append(train) return result def print_pretty(self): """打印列车信息""" pt = PrettyTable() pt._set_field_names(self.header) for train in self.trains: pt.add_row(train) print(pt) if __name__ == '__main__': t = TrainCollection()
4、接下來就是整合各個模組:tickets.py
"""Train tickets query via command-line. Usage: tickets [-gdtkz] <from> <to> <date> Options: -h,--help 显示帮助菜单 -g 高铁 -d 动车 -t 特快 -k 快速 -z 直达 Example: tickets -gdt beijing shanghai 2016-08-25 """ import requests from docopt import docopt from stations import stations # from pprint import pprint from myprettytable import TrainCollection class SelectTrain(object): def __init__(self): """ 获取命令行输入的参数 """ self.args = docopt(__doc__)#这个是获取命令行的所有参数,返回的是一个字典 def cli(self): """command-line interface""" # 获取 出发站点和目标站点 from_station = stations.get(self.args['<from>']) #出发站点 to_station = stations.get(self.args['<to>']) # 目的站点 leave_time = self._get_leave_time()# 出发时间 url = 'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate={0}&from_station={1}&to_station={2}'.format( leave_time,from_station,to_station)# 拼接请求列车信息的Url # 获取列车查询结果 r = requests.get(url,verify=False) traindatas = r.json()['data']['datas'] # 返回的结果,转化成json格式,取出datas,方便后面解析列车信息用 # 解析列车信息 traintypes = self._get_traintype() views = TrainCollection(traindatas,traintypes) views.print_pretty() def _get_traintype(self): """ 获取列车型号,这个函数的作用是的目的是:当你输入 -g 是只是返回 高铁,输入 -gd 返回动车和高铁,当不输参数时,返回所有的列车信息 """ traintypes = ['-g','-d','-t','-k','-z'] # result = [] # for traintype in traintypes: # if self.args[traintype]: # result.append(traintype[-1].upper()) trains = [traintype[-1].upper() for traintype in traintypes if self.args[traintype]] if trains: return trains else: return ['G','D','T','K','Z'] def _get_leave_time(self): """ 获取出发时间,这个函数的作用是为了:时间可以输入两种格式:2016-10-05、20161005 """ leave_time = self.args['<date>'] if len(leave_time) == 8: return '{0}-{1}-{2}'.format(leave_time[:4],leave_time[4:6],leave_time[6:]) if '-' in leave_time: return leave_time if __name__ == '__main__': cli = SelectTrain() cli.cli()