php實現查詢火車票


  閑來無事,做一個好玩的東西吧,也不是很難,也沒啥技術難度,做來玩玩。做一個查詢火車票的吧,再加上一個能循環查詢的功能,這樣就實現了簡單的刷票程序。那就開始吧!

  首先打開 12306 的網頁。他有一個輸入開始地址和到達地址的 選擇框,根據全國的火車站可以選擇的,那么這部分數據是怎么來的呢?打開火狐的 firebug 我們可以看到這么一個js請求

  沒錯了,所有的候車站就在這里了。我們可以看到這個 地址是 https的,那么用file_get_contents,我們是無法獲取數據的。但是不用怕,我們可以用curl來訪問這個接口。看一下他的請求頭:

  注意看,這里 他有一個 host 和 referer 以及 cookie,12306的網站我們不登錄也是可以進行車票查詢的,因此可以確定 cookie不是必要值,但是帶上了 referer和host 顯然要進行來源的驗證,那么

我們可以偽造一個 這樣的頭去請求這個接口就可以得到數據了。看一下獲取的函數,這個函數在后面的查詢火車票的地方也可以用到:

 1 /**
 2      * 采集數據
 3      * @param $url
 4      * @param $decode
 5      */
 6     private function curlGet($url, $decode = true)
 7     {
 8         $ch = curl_init();
 9         $timeout = 5;
10         $header = [
11             'Accept:*/*',
12             'Accept-Charset:GBK,utf-8;q=0.7,*;q=0.3',
13             'Accept-Encoding:gzip,deflate,sdch',
14             'Accept-Language:zh-CN,zh;q=0.8,ja;q=0.6,en;q=0.4',
15             'Connection:keep-alive',
16             'Host:kyfw.12306.cn',
17             'Referer:https://kyfw.12306.cn/otn/lcxxcx/init',
18         ];
19         curl_setopt($ch, CURLOPT_URL, $url);
20         curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
21         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
22         curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip壓縮
23         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳過證書檢查
24         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);  // 從證書中檢查SSL加密算法是否存在
25         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
26         $result = curl_exec($ch);
27         curl_close($ch);
28 
29         $decode && $result = json_decode($result, true);
30 
31         return $result;
32     }

  還需要注意一點的 是,從請求頭可以看到,數據進行了gzip壓縮,這樣我們如果不做處理的話,返回的數據會成為亂碼,很麻煩,不過用curl很容易解決這個問題。

  數據得到了,那么根據規律很好拆分,首先根據 @ 查分一次,然后按照 | 查分一次即可。本項目中采用的是 boostrap-suggest, 這是一個非常強大且易用的輸入聯想

插件,我就降數據整理成 這個插件可以用的格式,代碼如下:

 1  /**
 2      * 解析火車站信息
 3      */
 4     private function parseStation()
 5     {
 6         $url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8992';
 7         $station = $this->curlGet($url, false);
 8 
 9         if (empty($station)) {
10             throw new Exception('獲取站點信息失敗!');
11         }
12 
13         $delStr = "var station_names ='"; //需要截斷的字符
14         $station = substr($station, strlen($delStr), strlen($station));
15 
16         $station = explode('@', $station);
17         $json = [
18             'message' => ''
19         ];
20 
21         foreach ($station as $key => $vo) {
22             if (empty($vo)) continue;
23 
24             $st = explode('|', $vo);
25             $json['value'][] = [
26                 'stationName' => $st['1'],
27                 'shortName' => $st['3'],
28                 'stationFlag' => $st['2']
29             ];
30         }
31         unset($station);
32 
33         file_put_contents(ROOT_PATH . '/data/station.json', json_encode($json));
34     }

  

  好了,車站信息我們有了,那么怎慢查詢具體的火車票呢?我們再在 firebug中看看,12306用的是哪個接口。很快看到了這個接口:

https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=2016-12-26&from_station=NJH&to_station=XCH,沒錯就是這個了。

頭信息基本上跟獲取站點的一樣。purpose_codes表示你要查詢的票的類型,默認為成人票。queryDate 為出發日期,from_station 開始站點編號,to_station:到達站點

編號。只要我們拼裝成這個樣子,很容易獲取到數據:

 1 /**
 2      * 入口函數
 3      */
 4     public function run()
 5     {
 6         if (is_null($this->fromStation) || is_null($this->toStation))
 7             throw new Exception('起始站不能為空!');
 8         is_null($this->date) && $date = date('Y-m-d');
 9 
10         $url = 'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=' . $this->date . '&from_station=';
11         $url .= $this->fromStation . '&to_station=' . $this->toStation;
12 
13         $ticketInfo = $this->curlGet($url);
14         return $ticketInfo;
15     }

  

  每步的代碼看到了,那么整個類如下:

  1 <?php
  2 
  3 /**
  4  * author: NickBai
  5  * createTime: 2016/12/26 0026 上午 9:11
  6  *
  7  */
  8 class Tickets
  9 {
 10     public $fromStation = null;
 11     public $toStation = null;
 12     public $date = null;
 13 
 14     public function __construct($fromStation = null, $toStation = null, $date = null)
 15     {
 16         if (!file_exists(ROOT_PATH . '/data/station.json')) {
 17             $this->parseStation();
 18         }
 19 
 20         $this->fromStation = $fromStation;
 21         $this->toStation = $toStation;
 22         $this->date = $date;
 23     }
 24 
 25     /**
 26      * 入口函數
 27      */
 28     public function run()
 29     {
 30         if (is_null($this->fromStation) || is_null($this->toStation))
 31             throw new Exception('起始站不能為空!');
 32         is_null($this->date) && $date = date('Y-m-d');
 33 
 34         $url = 'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=' . $this->date . '&from_station=';
 35         $url .= $this->fromStation . '&to_station=' . $this->toStation;
 36 
 37         $ticketInfo = $this->curlGet($url);
 38         return $ticketInfo;
 39     }
 40 
 41     /**
 42      * 解析火車站信息
 43      */
 44     private function parseStation()
 45     {
 46         $url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8992';
 47         $station = $this->curlGet($url, false);
 48 
 49         if (empty($station)) {
 50             throw new Exception('獲取站點信息失敗!');
 51         }
 52 
 53         $delStr = "var station_names ='"; //需要截斷的字符
 54         $station = substr($station, strlen($delStr), strlen($station));
 55 
 56         $station = explode('@', $station);
 57         $json = [
 58             'message' => ''
 59         ];
 60 
 61         foreach ($station as $key => $vo) {
 62             if (empty($vo)) continue;
 63 
 64             $st = explode('|', $vo);
 65             $json['value'][] = [
 66                 'stationName' => $st['1'],
 67                 'shortName' => $st['3'],
 68                 'stationFlag' => $st['2']
 69             ];
 70         }
 71         unset($station);
 72 
 73         file_put_contents(ROOT_PATH . '/data/station.json', json_encode($json));
 74     }
 75 
 76     /**
 77      * 采集數據
 78      * @param $url
 79      * @param $decode
 80      */
 81     private function curlGet($url, $decode = true)
 82     {
 83         $ch = curl_init();
 84         $timeout = 5;
 85         $header = [
 86             'Accept:*/*',
 87             'Accept-Charset:GBK,utf-8;q=0.7,*;q=0.3',
 88             'Accept-Encoding:gzip,deflate,sdch',
 89             'Accept-Language:zh-CN,zh;q=0.8,ja;q=0.6,en;q=0.4',
 90             'Connection:keep-alive',
 91             'Host:kyfw.12306.cn',
 92             'Referer:https://kyfw.12306.cn/otn/lcxxcx/init',
 93         ];
 94         curl_setopt($ch, CURLOPT_URL, $url);
 95         curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
 96         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 97         curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip壓縮
 98         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳過證書檢查
 99         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);  // 從證書中檢查SSL加密算法是否存在
100         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
101         $result = curl_exec($ch);
102         curl_close($ch);
103 
104         $decode && $result = json_decode($result, true);
105 
106         return $result;
107     }
108 
109 }

  代碼組織結構如下:

 

  前台,依舊是采用 H+和layui 進行修改的頁面,效果如下:

 

設置好 刷新頻率,就可以愉快的刷票了。

代碼分享地址:http://pan.baidu.com/s/1o8Apa5c

線上體驗地址:http://www.baiyf.com/

 


免責聲明!

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



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