忘記12306!用 Python3 實現(xiàn)自己的火車票查看器!

      網(wǎng)友投稿 650 2025-04-02

      課程簡介:使用 Python3 抓取 12306 網(wǎng)站信息提供一個命令行的火車票查詢工具。通過該項目的實現(xiàn),可以熟悉 Python3 基礎及網(wǎng)絡編程,以及 docopt,requests,prettytable 等庫的使用。


      項目由小蝸牛發(fā)布在實驗樓,項目在線練習地址:Python3 實現(xiàn)火車票查詢工具,可以直接在教程中下載代碼使用demo。

      一、實驗簡介

      當你想查詢一下火車票信息的時候,你還在上 12306 官網(wǎng)嗎?或是打開你手機里的 APP?

      下面讓我們來用 Python 寫一個命令行版的火車票查看器, 只要在命令行敲一行命令就能獲得你想要的火車票信息!如果你剛掌握了Python基礎,這將是個不錯的小練習。

      1.1 知識點

      Python3 基礎知識的綜合運用

      docopt、requests?及?prettytable?庫的使用

      1.2 效果截圖

      二、接口設計

      一個應用寫出來最終是要給人使用的,哪怕只是給你自己使用。

      所以,首先應該想想你希望怎么使用它?讓我們先給這個小應用起個名字吧,既然及查詢票務信息,那就叫它?tickets?好了。

      我們希望用戶只要輸入出發(fā)站,到達站以及日期就讓就能獲得想要的信息,比如要查看8月25號上海-北京的火車余票, 我們只需輸入:

      $?tickets?shanghai?beijing?2016-08-25

      注意:?由于實驗樓環(huán)境中無法輸入中文,所以我們的參數(shù)設計為拼音的形式,在這里思考下使用拼音是否有什么弊端?

      對這一接口進行抽象得到:

      $?tickets?from?to?date

      另外,火車有各種類型,高鐵、動車、特快、快速和直達,我們希望可以提供選項只查詢特定的一種或幾種的火車,所以,我們應該有下面這些選項:

      -g 高鐵

      忘記12306!用 Python3 實現(xiàn)自己的火車票查看器!

      -d 動車

      -t 特快

      -k 快速

      -z 直達

      這幾個選項應該能被組合使用,所以,最終我們的接口應該是這個樣子的:

      $?tickets?[-gdtkz]?from?to?date

      接口已經(jīng)確定好了,剩下的就是實現(xiàn)它了。

      三、代碼實現(xiàn)

      首先安裝一下實驗需要用到的庫:

      $?sodo?pip?install?requests?prettytable?docopt

      requests, 不用不多介紹了吧,使用 Python 訪問 HTTP 資源的必備庫。

      docopt, Python3 命令行參數(shù)解析工具。

      prettytable, 格式化信息打印工具,能讓你像 MySQL 那樣打印數(shù)據(jù)。

      3.1 解析參數(shù)

      Python有很多寫命令行參數(shù)解析工具,如?argparse,?docopt,?click,這里我們選用的是?docopt?這個簡單易用的工具。docopt?可以按我們在文檔字符串中定義的格式來解析參數(shù),比如我們在?tickets.py:

      注意:?實驗樓中無法輸入中文,參數(shù)后的中文可以使用拼音代替。

      #?coding:?utf-8"""Train?tickets?query?via?command-line.?Usage:?tickets?[-gdtkz]????Options:?-h,--help?顯示幫助菜單 ?-g?高鐵?-d?動車?-t?特快?-k?快速?-z?直達?Example:?tickets?beijing?shanghai?2016-08-25?"""from?docopt?import?docoptdef?cli(): ????"""command-line?interface""" ????arguments?=?docopt(__doc__) ????print(arguments)if?__name__?==?'__main__': ????cli()

      下面我們運行一下這個程序:

      $?python3?tickets.py?beijing?shanghai?2016-08-25

      我們得到下面的結(jié)果:

      3.2 獲取數(shù)據(jù)

      參數(shù)已經(jīng)解析好了,下面就是如何獲取數(shù)據(jù)了,這也是最主要的部分。首先我們打開?12306,進入余票查詢頁面,如果你使用 Chrome,那么按?F12?打開開發(fā)者工具,選中?Network?一欄,在查詢框鐘我們輸入?上海?到?北京,日期?2016-08-25, 點擊查詢,我們在調(diào)試工具發(fā)現(xiàn),查詢系統(tǒng)實際上請求了這個URL:

      https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=2016-07-01&from_station=SHH&to_station=BJP

      并且返回的是JSON格式的數(shù)據(jù)!

      接下來問題就簡單了,我們只需要構(gòu)建請求URL然后解析返回的Json數(shù)據(jù)就可以了。但是我們發(fā)現(xiàn),URL里面?from_station?和?to_station?并不是漢字或者拼音,而是一個代號,而我們想要輸入的是漢字或者拼音,我們要如何獲取代號呢?我們打開網(wǎng)頁源碼看看有沒有什么發(fā)現(xiàn)。

      果然,我們在網(wǎng)頁里面找到了這個鏈接:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8955?這里面貌似是包含了所有車站的中文名,拼音,簡寫和代號等信息。但是這些信息擠在一起,而我們只想要車站的拼音和大寫字母的代號信息,怎么辦呢?

      正則表達式就是答案,我們寫個小腳本來匹配提取出想要的信息吧, 在parse_station.py中:

      #?coding:?utf-8import?reimport?requestsfrom?pprint?import?pprint url?=?'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8955'text?=?requests.get(url,?verify=False) stations?=?re.findall(r'([A-Z]+)\|([a-z]+)',?text) stations?=?dict(stations) stations?=?dict(zip(stations.values(),?stations.keys())) pprint(stations,?indent=4)

      注意,上面的正則表達式匹配出的結(jié)果轉(zhuǎn)為字典后,字典的鍵是大寫字母大號,這顯然不是我們想要的結(jié)果,于是,我們通過一個變換將鍵值反過來。 我們運行這個腳本,它將以字典的形式返回所有車站和它的大寫字母代號, 我們將結(jié)果重定向到?stations.py中,

      $?python3?parse_station.py?>?stations.py

      我們?yōu)檫@個字典加名字,stations, 最終,stations.py文件是這樣的:

      現(xiàn)在,用戶輸入車站的中文名,我們就可以直接從這個字典中獲取它的字母代碼了:

      ...from?stations?import?stationsdef?cli(): ????arguments?=?docopt(__doc__) ????from_staion?=?stations.get(arguments['']) ????to_station?=?stations.get(arguments['']) ????date?=?arguments['']????#?構(gòu)建URL ????url?=?'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate={}&from_station={}&to_station={}'.format( ????????date,?from_staion,?to_station ????)

      萬事俱備,下面我們來請求這個URL獲取數(shù)據(jù)吧!這里我們使用?requests?這個庫, 它提供了非常簡單易用的接口,

      ...import?requestsdef?cli(): ????...????#?添加verify=False參數(shù)不驗證證書 ????r?=?requests.get(url,?verify=False)????print(r.json())

      從結(jié)果中,我們可以觀察到,與車票有關的信息需要進一步提取:

      def?cli(): ????... ????r?=?requsets.get(url); ????rows?=?r.json()['data']['datas']

      3.3 解析數(shù)據(jù)

      我們封裝一個簡單的類來解析數(shù)據(jù):

      from?prettytable?import?PrettyTableclass?TrainCollection(object): ????#?顯示車次、出發(fā)/到達站、?出發(fā)/到達時間、歷時、一等坐、二等坐、軟臥、硬臥、硬座 ????header?=?'train?station?time?duration?first?second?softsleep?hardsleep?hardsit'.split()????def?__init__(self,?rows): ????????self.rows?=?rows????def?_get_duration(self.row): ????????"""?獲取車次運行時間?""" ????????duration?=?row.get('lishi').replace(':',?'h')?+?'m' ????????if?duration.startswith('00'):????????????return?duration[4:]????????if?duration.startswith('0'):????????? ???????????return?duration[1:]????????return?duration????@property ????def?trains(self): ????????for?row?in?self.rows: ????????????train?=?[????????????????#?車次 ????????????????row['station_train_code'],????????????????#?出發(fā)、到達站 ????????????????'\n'.join([row['from_staion_name'],?row['to_station_name']]),????????????????#?出發(fā)、到達時間 ????????????????'\n'.join([row['start_time'],?row['arrive']]),????????????????#?歷時 ????????????????self._get_duration(row),????????????????#?一等坐 ????????????????row['zy_num'],????????????????#?二等坐 ????????????????row['ze_num'],????????????????#?軟臥 ????????????????row['rw_num'],????????????????#?軟坐 ????????????????row['yw_num'],????????????????#?硬坐 ????????????????row['yz_num'] ????????????]????????????yield?train????def?pretty_print(self): ????????"""?數(shù)據(jù)已經(jīng)獲取到了,剩下的就是提取我們要的信息并將它顯示出來。?`prettytable`這個庫可以讓我們它像MySQL數(shù)據(jù)庫那樣格式化顯示數(shù)據(jù)。?""" ????????pt?=?PrettyTable()????????#?設置每一列的標題 ????????pt._set_field_names(self.header)????????for?train?in?self.trains: ????????????pt.add_row(train) ????????print(pt)

      3.4 顯示結(jié)果

      最后,我們將上述過程進行匯總并將結(jié)果輸出到屏幕上:

      ...class?TrainCollection: ????... ????...def?cli(): ????arguments?=?docopt(__doc__) ????from_staion?=?stations.get(arguments['']) ????to_station?=?stations.get(arguments['']) ????date?=?arguments['']????#?構(gòu)建URL ????url?=?'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate={}&from_station={}&to_station={}'.format( ????????date,?from_staion,?to_station ????) ????r?=?requests.get(url,?verify=False) ????rows?=?r.json()['data']['datas'] ????trains?=?TrainCollection(rows) ????trains.pretty_print()if?__name__?==?'__main__': ????cli()

      3.5 最后一米

      至此, 程序的主體已經(jīng)完成了, 但是上面打印出的結(jié)果是黑白的,很是乏味,我們來給它添加顏色吧:

      def?colored(color,?text): ????table?=?{????????'red':?'\033[91m',????????'green':?'\033[92m',????????#?no?color ????????'nc':?'\033[0' ????} ????cv?=?table.get(color) ????nc?=?table.get('nv')????return?''.join([cv,?text,?nc])

      修改一下程序,將出發(fā)車站與出發(fā)時間顯示為紅色, 將到達車站與到達時間顯示為綠色:

      ...'\n'.join([colored('green',?row['from_staion_name']) ???????????colored('red',?row['to_station_name'])]),'\n'.join([colored('green',?row['start_time']) ???????????colored('red',?row['arrive_time'])]), ...

      四、總結(jié)

      本課程使用 Python3 抓取 12306 網(wǎng)站信息提供一個命令行的火車票查詢工具。通過該項目的實現(xiàn),可以學習并實踐 Python3 基礎及網(wǎng)絡編程,以及 docopt,requests,prettytable 等庫的使用。

      感興趣的同學可以實現(xiàn)更多擴展功能:

      顯示商務坐, 無坐

      添加參數(shù)支持,用戶可以指定火車類型

      支持更多的時間格式,如:20161010

      本項目的完整代碼及demo,可在實驗樓查看并在線完成,立即【開始實驗】

      更多Python經(jīng)典項目:Python全部 - 課程

      本文轉(zhuǎn)載自異步社區(qū)。

      網(wǎng)絡 高性能計算 Web應用防火墻 WAF

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:Excel表格復制到word去掉表格邊框的方法(表格復制到word沒有邊框)
      下一篇:wps2016表格中怎么設置費用報銷單?
      相關文章
      中文字幕中韩乱码亚洲大片| 亚洲国产一区明星换脸| 亚洲综合在线另类色区奇米| 亚洲国产av无码精品| 亚洲国产成人久久精品大牛影视| 亚洲制服丝袜第一页| 亚洲一区二区三区深夜天堂| 亚洲国产精品综合久久20| 亚洲一区二区免费视频| 亚洲fuli在线观看| 国产精品亚洲自在线播放页码| 亚洲一区二区三区播放在线| 国产成人精品亚洲2020| 久久久久亚洲国产| 亚洲人成色77777在线观看| 亚洲国产精品美女久久久久| 精品国产日韩亚洲一区91| 亚洲成av人片在线观看天堂无码 | 亚洲AV永久无码天堂影院| 亚洲精品理论电影在线观看| 亚洲AV一区二区三区四区| 国产精品久久久久久亚洲影视| 国产精品亚洲一区二区三区在线观看| 国产偷国产偷亚洲高清在线| 亚洲精品久久久www| 中文字幕久久亚洲一区| 国产av天堂亚洲国产av天堂| 亚洲AV人人澡人人爽人人夜夜| 久久丫精品国产亚洲av不卡| 亚洲成人一级电影| 精品丝袜国产自在线拍亚洲| 亚洲成在人线在线播放无码 | 麻豆亚洲AV永久无码精品久久| 久久久国产精品亚洲一区| 亚洲手机中文字幕| 亚洲日本VA中文字幕久久道具| 亚洲av日韩av欧v在线天堂| 亚洲中文字幕无码一区二区三区| 亚洲精品高清视频| 亚洲人成在线中文字幕| 亚洲人成网站18禁止|