├── .gitignore ├── README.md ├── composer.json ├── composer.lock ├── search └── src └── SearchCommand.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea/ 3 | .idea 4 | 5 | # Created by .ignore support plugin (hsz.mobi) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A Command Line App For Searching Tickets From 12306 2 | 3 | **命令行查询12306的火车票** 4 | 5 | ![](https://o0dpls1ru.qnssl.com/2015-12-15_20-58-16.png) 6 | 7 | ## 使用方法: 8 | 9 | ``` 10 | git clone https://github.com/JellyBool/12306.git 11 | 12 | cd 12306 13 | 14 | composer install 15 | 16 | ./search 12306 成都 南宁 2016-01-28 17 | ``` 18 | 19 | > 如果你没有安装composer,可以`curl -sS https://getcomposer.org/installer | php` , 20 | > 或者直接到composer官网 [https://getcomposer.org/download/](https://getcomposer.org/download/) 21 | 22 | ## 说明: 23 | 1.日期为可选参数,如果不传入,默认为查询当天 24 | ``` 25 | ./search 12306 成都 南宁 26 | ``` 27 | 28 | **2. 目前为第一个测试版,后续后慢慢改善** 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravist/search", 3 | "description": "A Command Line App For Search Tickets From 12306", 4 | "type": "php", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "JellyBool", 9 | "email": "jellybool@outlook.com" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "Laravist\\Search\\Console\\": "src/" 15 | } 16 | }, 17 | "require": { 18 | "symfony/console": "~3.0", 19 | "guzzlehttp/guzzle": "~6.0" 20 | }, 21 | "bin": [ 22 | "search" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "8358dd0826ca839bda9269a3d0091807", 8 | "content-hash": "83927bc45bf8af9d206b0703cac30891", 9 | "packages": [ 10 | { 11 | "name": "guzzlehttp/guzzle", 12 | "version": "6.1.1", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/guzzle/guzzle.git", 16 | "reference": "c6851d6e48f63b69357cbfa55bca116448140e0c" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c6851d6e48f63b69357cbfa55bca116448140e0c", 21 | "reference": "c6851d6e48f63b69357cbfa55bca116448140e0c", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "guzzlehttp/promises": "~1.0", 26 | "guzzlehttp/psr7": "~1.1", 27 | "php": ">=5.5.0" 28 | }, 29 | "require-dev": { 30 | "ext-curl": "*", 31 | "phpunit/phpunit": "~4.0", 32 | "psr/log": "~1.0" 33 | }, 34 | "type": "library", 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "6.1-dev" 38 | } 39 | }, 40 | "autoload": { 41 | "files": [ 42 | "src/functions_include.php" 43 | ], 44 | "psr-4": { 45 | "GuzzleHttp\\": "src/" 46 | } 47 | }, 48 | "notification-url": "https://packagist.org/downloads/", 49 | "license": [ 50 | "MIT" 51 | ], 52 | "authors": [ 53 | { 54 | "name": "Michael Dowling", 55 | "email": "mtdowling@gmail.com", 56 | "homepage": "https://github.com/mtdowling" 57 | } 58 | ], 59 | "description": "Guzzle is a PHP HTTP client library", 60 | "homepage": "http://guzzlephp.org/", 61 | "keywords": [ 62 | "client", 63 | "curl", 64 | "framework", 65 | "http", 66 | "http client", 67 | "rest", 68 | "web service" 69 | ], 70 | "time": "2015-11-23 00:47:50" 71 | }, 72 | { 73 | "name": "guzzlehttp/promises", 74 | "version": "1.0.3", 75 | "source": { 76 | "type": "git", 77 | "url": "https://github.com/guzzle/promises.git", 78 | "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea" 79 | }, 80 | "dist": { 81 | "type": "zip", 82 | "url": "https://api.github.com/repos/guzzle/promises/zipball/b1e1c0d55f8083c71eda2c28c12a228d708294ea", 83 | "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea", 84 | "shasum": "" 85 | }, 86 | "require": { 87 | "php": ">=5.5.0" 88 | }, 89 | "require-dev": { 90 | "phpunit/phpunit": "~4.0" 91 | }, 92 | "type": "library", 93 | "extra": { 94 | "branch-alias": { 95 | "dev-master": "1.0-dev" 96 | } 97 | }, 98 | "autoload": { 99 | "psr-4": { 100 | "GuzzleHttp\\Promise\\": "src/" 101 | }, 102 | "files": [ 103 | "src/functions_include.php" 104 | ] 105 | }, 106 | "notification-url": "https://packagist.org/downloads/", 107 | "license": [ 108 | "MIT" 109 | ], 110 | "authors": [ 111 | { 112 | "name": "Michael Dowling", 113 | "email": "mtdowling@gmail.com", 114 | "homepage": "https://github.com/mtdowling" 115 | } 116 | ], 117 | "description": "Guzzle promises library", 118 | "keywords": [ 119 | "promise" 120 | ], 121 | "time": "2015-10-15 22:28:00" 122 | }, 123 | { 124 | "name": "guzzlehttp/psr7", 125 | "version": "1.2.1", 126 | "source": { 127 | "type": "git", 128 | "url": "https://github.com/guzzle/psr7.git", 129 | "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982" 130 | }, 131 | "dist": { 132 | "type": "zip", 133 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982", 134 | "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982", 135 | "shasum": "" 136 | }, 137 | "require": { 138 | "php": ">=5.4.0", 139 | "psr/http-message": "~1.0" 140 | }, 141 | "provide": { 142 | "psr/http-message-implementation": "1.0" 143 | }, 144 | "require-dev": { 145 | "phpunit/phpunit": "~4.0" 146 | }, 147 | "type": "library", 148 | "extra": { 149 | "branch-alias": { 150 | "dev-master": "1.0-dev" 151 | } 152 | }, 153 | "autoload": { 154 | "psr-4": { 155 | "GuzzleHttp\\Psr7\\": "src/" 156 | }, 157 | "files": [ 158 | "src/functions_include.php" 159 | ] 160 | }, 161 | "notification-url": "https://packagist.org/downloads/", 162 | "license": [ 163 | "MIT" 164 | ], 165 | "authors": [ 166 | { 167 | "name": "Michael Dowling", 168 | "email": "mtdowling@gmail.com", 169 | "homepage": "https://github.com/mtdowling" 170 | } 171 | ], 172 | "description": "PSR-7 message implementation", 173 | "keywords": [ 174 | "http", 175 | "message", 176 | "stream", 177 | "uri" 178 | ], 179 | "time": "2015-11-03 01:34:55" 180 | }, 181 | { 182 | "name": "psr/http-message", 183 | "version": "1.0", 184 | "source": { 185 | "type": "git", 186 | "url": "https://github.com/php-fig/http-message.git", 187 | "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" 188 | }, 189 | "dist": { 190 | "type": "zip", 191 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", 192 | "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", 193 | "shasum": "" 194 | }, 195 | "require": { 196 | "php": ">=5.3.0" 197 | }, 198 | "type": "library", 199 | "extra": { 200 | "branch-alias": { 201 | "dev-master": "1.0.x-dev" 202 | } 203 | }, 204 | "autoload": { 205 | "psr-4": { 206 | "Psr\\Http\\Message\\": "src/" 207 | } 208 | }, 209 | "notification-url": "https://packagist.org/downloads/", 210 | "license": [ 211 | "MIT" 212 | ], 213 | "authors": [ 214 | { 215 | "name": "PHP-FIG", 216 | "homepage": "http://www.php-fig.org/" 217 | } 218 | ], 219 | "description": "Common interface for HTTP messages", 220 | "keywords": [ 221 | "http", 222 | "http-message", 223 | "psr", 224 | "psr-7", 225 | "request", 226 | "response" 227 | ], 228 | "time": "2015-05-04 20:22:00" 229 | }, 230 | { 231 | "name": "symfony/console", 232 | "version": "v3.0.0", 233 | "source": { 234 | "type": "git", 235 | "url": "https://github.com/symfony/console.git", 236 | "reference": "175871ca8d1ef16ff8d8cac395a1c73afa8d0e63" 237 | }, 238 | "dist": { 239 | "type": "zip", 240 | "url": "https://api.github.com/repos/symfony/console/zipball/175871ca8d1ef16ff8d8cac395a1c73afa8d0e63", 241 | "reference": "175871ca8d1ef16ff8d8cac395a1c73afa8d0e63", 242 | "shasum": "" 243 | }, 244 | "require": { 245 | "php": ">=5.5.9", 246 | "symfony/polyfill-mbstring": "~1.0" 247 | }, 248 | "require-dev": { 249 | "psr/log": "~1.0", 250 | "symfony/event-dispatcher": "~2.8|~3.0", 251 | "symfony/process": "~2.8|~3.0" 252 | }, 253 | "suggest": { 254 | "psr/log": "For using the console logger", 255 | "symfony/event-dispatcher": "", 256 | "symfony/process": "" 257 | }, 258 | "type": "library", 259 | "extra": { 260 | "branch-alias": { 261 | "dev-master": "3.0-dev" 262 | } 263 | }, 264 | "autoload": { 265 | "psr-4": { 266 | "Symfony\\Component\\Console\\": "" 267 | }, 268 | "exclude-from-classmap": [ 269 | "/Tests/" 270 | ] 271 | }, 272 | "notification-url": "https://packagist.org/downloads/", 273 | "license": [ 274 | "MIT" 275 | ], 276 | "authors": [ 277 | { 278 | "name": "Fabien Potencier", 279 | "email": "fabien@symfony.com" 280 | }, 281 | { 282 | "name": "Symfony Community", 283 | "homepage": "https://symfony.com/contributors" 284 | } 285 | ], 286 | "description": "Symfony Console Component", 287 | "homepage": "https://symfony.com", 288 | "time": "2015-11-30 12:36:17" 289 | }, 290 | { 291 | "name": "symfony/polyfill-mbstring", 292 | "version": "v1.0.0", 293 | "source": { 294 | "type": "git", 295 | "url": "https://github.com/symfony/polyfill-mbstring.git", 296 | "reference": "0b6a8940385311a24e060ec1fe35680e17c74497" 297 | }, 298 | "dist": { 299 | "type": "zip", 300 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0b6a8940385311a24e060ec1fe35680e17c74497", 301 | "reference": "0b6a8940385311a24e060ec1fe35680e17c74497", 302 | "shasum": "" 303 | }, 304 | "require": { 305 | "php": ">=5.3.3" 306 | }, 307 | "type": "library", 308 | "extra": { 309 | "branch-alias": { 310 | "dev-master": "1.0-dev" 311 | } 312 | }, 313 | "autoload": { 314 | "psr-4": { 315 | "Symfony\\Polyfill\\Mbstring\\": "" 316 | }, 317 | "files": [ 318 | "bootstrap.php" 319 | ] 320 | }, 321 | "notification-url": "https://packagist.org/downloads/", 322 | "license": [ 323 | "MIT" 324 | ], 325 | "authors": [ 326 | { 327 | "name": "Nicolas Grekas", 328 | "email": "p@tchwork.com" 329 | }, 330 | { 331 | "name": "Symfony Community", 332 | "homepage": "https://symfony.com/contributors" 333 | } 334 | ], 335 | "description": "Symfony polyfill for the Mbstring extension", 336 | "homepage": "https://symfony.com", 337 | "keywords": [ 338 | "compatibility", 339 | "mbstring", 340 | "polyfill", 341 | "portable", 342 | "shim" 343 | ], 344 | "time": "2015-11-04 20:28:58" 345 | } 346 | ], 347 | "packages-dev": [], 348 | "aliases": [], 349 | "minimum-stability": "stable", 350 | "stability-flags": [], 351 | "prefer-stable": false, 352 | "prefer-lowest": false, 353 | "platform": [], 354 | "platform-dev": [] 355 | } 356 | -------------------------------------------------------------------------------- /search: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(new Laravist\Search\Console\SearchCommand); 11 | $app->run(); -------------------------------------------------------------------------------- /src/SearchCommand.php: -------------------------------------------------------------------------------- 1 | setName('12306') 31 | ->setDescription('Search tickets from 12306.') 32 | ->addArgument('from', InputArgument::REQUIRED) 33 | ->addArgument('to', InputArgument::REQUIRED) 34 | ->addArgument('date', InputArgument::OPTIONAL); 35 | } 36 | 37 | /** 38 | * @param InputInterface $input 39 | * @param OutputInterface $output 40 | */ 41 | protected function execute(InputInterface $input, OutputInterface $output) 42 | { 43 | $date = $input->getArgument('date') ?: $this->today(); 44 | $results = $this->search( 45 | $input->getArgument('from'), 46 | $input->getArgument('to'), 47 | $date 48 | ); 49 | $data = json_decode($results); 50 | if (isset($data->status) && $data->status) { 51 | $rows = $this->generateTableRows($data->data->datas); 52 | $table = new Table($output); 53 | $table->setHeaders(['车次', '出发', '到达', '历时', '商务座', '特等座', '一等座', '二等座', '软卧', '硬卧', '软座', '硬座', '无座']) 54 | ->setRows($rows); 55 | $table->render(); 56 | 57 | return true; 58 | } 59 | $output->writeln('请填写正确地地址'); 60 | 61 | return false; 62 | } 63 | 64 | /** 65 | * @param $from 66 | * @param $destination 67 | * @param $date 68 | * 69 | * @return string 70 | */ 71 | protected function search($from, $destination, $date) 72 | { 73 | $from = $this->getStationCode($from); 74 | $destination = $this->getStationCode($destination); 75 | $client = new Client(); 76 | $response = $client->request('GET', $this->url, [ 77 | 'verify' => false, 78 | 'query' => [ 79 | 'purpose_codes' => 'ADULT', 80 | 'queryDate' => $date, 81 | 'from_station' => $from, 82 | 'to_station' => $destination, 83 | ], 84 | ]); 85 | 86 | return $response->getBody()->getContents(); 87 | } 88 | 89 | /** 90 | * @param $text 91 | * 92 | * @return bool 93 | */ 94 | protected function getStationCode($text) 95 | { 96 | //todo: make it more smart 97 | $stations = explode('|', self::$stationNames); 98 | if (in_array($text, $stations)) { 99 | $code = $stations[array_search($text, $stations) + 1]; 100 | 101 | return $code; 102 | } 103 | 104 | return false; 105 | } 106 | 107 | /** 108 | * @param array $data 109 | * 110 | * @return array 111 | */ 112 | protected function generateTableRows(array $data) 113 | { 114 | $rows = []; 115 | foreach ($data as $row) { 116 | $format = [ 117 | $row->station_train_code, 118 | $row->start_time, 119 | $row->arrive_time, 120 | $row->lishi, 121 | $row->swz_num, 122 | $row->tz_num, 123 | $row->zy_num, 124 | $row->ze_num, 125 | $row->rw_num, 126 | $row->yw_num, 127 | $row->rz_num, 128 | $row->yz_num, 129 | $row->wz_num, 130 | ]; 131 | $rows[] = $format; 132 | } 133 | 134 | return $rows; 135 | } 136 | 137 | /** 138 | * @return bool|string 139 | */ 140 | protected function today() 141 | { 142 | date_default_timezone_set('Asia/Chongqing'); 143 | 144 | return date('Y-m-d'); 145 | } 146 | } 147 | --------------------------------------------------------------------------------