在第一版的返乡之路不容易之12306余票查询并给出备选方案中,给出了余票查询和备选方案推荐,但当时有两个问题:

  • 没有备选排名:虽然给出了备选,但哪个备选好没有给出排序
  • 没有座位信息(商务/一等/二等/硬座/硬卧/无座):虽然能买,但是不一定能买到适合自己的(便宜的),有点奢侈了

因此这几天对代码进行了更新。

首先说一下备选方案排序的原理,如果我们直接买不到出发地和目的地的车票,那对于这趟车来说,只要出发站买在首发站和出发地之间,到达站买在出发地和终点站之间,就可以保证我们能顺利踏上这趟车,大不了多买几站或者上车补票。那么我们的排序原理就是花最少的钱回家,怎么办呢?

出发站尽量买在离出发地近的站,到达站尽量买在离目的地近的站,而且尽量补票,不要多买站,毕竟我们秉着花钱最少的原则。


实现的效果如下

趁着这次做2.0,对代码做了优化:

  • 城市缩写使用保存的文件进行读入,不使用12306的接口进行获取,加快速度;(城市缩写文件可以由第一版的Citys()函数进行获取保存,形式如下:)

    {"北京北": "VAP","北京东": "BOP","北京": "BJP","北京南": "VNP","北京西": "BXP","广州南": "IZQ","重庆北": "CUW","重庆": "CQW","重庆南": "CRW",...
    }
    
  • 增加爬虫自省机制,如果接口调用失败则延时后再次调用

  • 增加了座位信息

  • 对备选方案做出排序

代码如下:

# coding=utf-8
import requests
import urllib.parse as parse
import time
import json
import pretty_errors
import re
from fake_useragent import UserAgentTRAIN_NO = 2
TRAIN = 3
DEPARTURE_STATION = 6
TERMINUS = 7
DEPARTURE_TIME = 8
ARRIVAL_TIME = 9
DURATION = 10
IF_BOOK = 11
DATE = 13
FROM_STATION_NO = 16
TO_STATION_NO = 17
SEAT_TYPES = 35
#
OTHER = 22
NO_SEAT = 26  # WZ
HARD_SEAT = 29  # A1
SECOND_SEAT = 30  # O
FIRST_SEAT = 31  # M
BUSINESS_SEAT = 32  # A9
HARD_SLEEPER = 28  # A3
SOFT_SLEEPER = 23  # A4# 城市缩写
with open('citys.json', 'r') as f:Citys = json.load(f)def Time():"""获取当前时间:return:"""list_time = list(time.localtime())year = str(list_time[0])month = str(list_time[1])day = str(list_time[2])if len(month) == 1:month = '0' + monthif len(day) == 1:day = '0' + dayreturn year, month, dayproxy = {'http': '113.125.128.4:8888'}class Train:def __init__(self,from_station,to_station,train_date=Time()[0] + '-' + Time()[1] + '-' + Time()[2]):self.from_station = from_stationself.to_station = to_stationself.train_date = train_dateself.url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?leftTicketDTO.%s&leftTicketDTO.%s&leftTicketDTO.%s&purpose_codes=ADULT'self.headers = {'User-Agent': str(UserAgent().random)}self.session = requests.session()self.session.get('https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E6%9D%AD%E5%B7%9E%E4%B8%9C,HGH&ts=%E5%A4%AA%E5%8E%9F%E5%8D%97,TNV&date=2022-01-19&flag=N,N,Y',headers=self.headers, proxies=proxy, timeout=5)def station(self, train_number):"""查找列车起点可买和终点可买:return:"""url = f'https://kyfw.12306.cn/otn/czxx/queryByTrainNo?' \f'{parse.urlencode({"train_no": train_number})}&' \f'{parse.urlencode({"from_station_telecode": Citys[self.from_station]})}&' \f'{parse.urlencode({"to_station_telecode": Citys[self.to_station]})}&' \f'{parse.urlencode({"depart_date": self.train_date})}'self.headers['User-Agent'] = str(UserAgent().random)content = self.session.get(url, headers=self.headers, proxies=proxy, timeout=5)content = content.content.decode('utf-8')data = json.loads(content)stations_data = data['data']['data']stations_data.sort(key=lambda x: x['station_no'])from_station_idx = int(list(filter(lambda x: self.from_station in x['station_name'], stations_data))[0]['station_no'])to_station_idx = int(list(filter(lambda x: self.to_station in x['station_name'], stations_data))[0]['station_no'])from_station_buy = [station['station_name'] for station in stations_data[:from_station_idx]][::-1]to_station_buy_1 = [station['station_name'] for station in stations_data[from_station_idx:to_station_idx]][::-1]to_station_buy_2 = [station['station_name'] for station in stations_data[to_station_idx:]]to_station_buy = to_station_buy_1 + to_station_buy_2return from_station_buy, to_station_buydef train(self):"""爬取信息:return:"""url = self.url % (parse.urlencode({"train_date": self.train_date}),parse.urlencode({"from_station": Citys[self.from_station]}),parse.urlencode({"to_station": Citys[self.to_station]}))self.headers['User-Agent'] = str(UserAgent().random)content = self.session.get(url, headers=self.headers, proxies=proxy, timeout=5)content = content.content.decode('utf-8')data = json.loads(content)dict_train = data['data']['result']dict_map = data['data']['map']res = []TRAINs = []for train in dict_train:train_split = train.split('|')if train_split[TRAIN] in TRAINs:continueprint(train_split[TRAIN])prices = self.price(train_split[FROM_STATION_NO], train_split[TO_STATION_NO],train_split[SEAT_TYPES], train_split[TRAIN_NO])from_station_buy, to_station_buy = self.station(train_split[TRAIN_NO])buy = []for from_station, to_station in [[x, y] for x in from_station_buy for y in to_station_buy]:if train_split[IF_BOOK] == 'N' and self.book_if(from_station, to_station, train_split[TRAIN]):buy.append(f'{from_station}-{to_station}')train_str = [train_split[TRAIN], dict_map[train_split[DEPARTURE_STATION]],dict_map[train_split[TERMINUS]], train_split[DEPARTURE_TIME],train_split[ARRIVAL_TIME], train_split[DURATION],f'{train_split[BUSINESS_SEAT]} / {prices.get("A9")}' if train_split[BUSINESS_SEAT] and  train_split[BUSINESS_SEAT] != '无' else '无',f'{train_split[FIRST_SEAT]} / {prices.get("M")}' if train_split[FIRST_SEAT] and  train_split[FIRST_SEAT] != '无' else '无',f'{train_split[SECOND_SEAT]} / {prices.get("O")}' if train_split[SECOND_SEAT] and  train_split[SECOND_SEAT] != '无' else '无',f'{train_split[SOFT_SLEEPER]} / {prices.get("A4")}' if train_split[SOFT_SLEEPER] and  train_split[SOFT_SLEEPER] != '无' else '无',f'{train_split[HARD_SLEEPER]} / {prices.get("A3")}' if train_split[HARD_SLEEPER] and  train_split[HARD_SLEEPER] != '无' else '无',f'{train_split[HARD_SEAT]} / {prices.get("A1")}' if train_split[HARD_SEAT] and  train_split[HARD_SEAT] != '无' else '无',f'{train_split[NO_SEAT]} / {prices.get("WZ")}' if train_split[NO_SEAT] and  train_split[NO_SEAT] != '无' else '无','可以' if train_split[IF_BOOK] == 'Y' else '不可以', ', '.join(buy)]res.append('| ' + ' | '.join(train_str) + ' |')TRAINs.append(train_split[TRAIN])return resdef book_if(self, from_station, to_station, train_number):"""查询是否有票:param from_station::param to_station::param train_number::return:"""url = self.url % (parse.urlencode({"train_date": self.train_date}),parse.urlencode({"from_station": Citys[from_station]}),parse.urlencode({"to_station": Citys[to_station]}))train = ['']for _ in range(20):try:self.headers['User-Agent'] = str(UserAgent().random)content = self.session.get(url, headers=self.headers, proxies=proxy, timeout=5)content = content.content.decode('utf-8')data = json.loads(content)dict_train = data['data']['result']train = list(filter(lambda x: x.split('|')[TRAIN] == train_number, dict_train))if not train:returnbreakexcept:print('查询余票请求失败,重新请求')time.sleep(2)return True if train[0].split('|')[IF_BOOK] == 'Y' else Falsedef price(self, from_station_no, to_station_no, seat_types, train_no):"""查询车票价格:return:"""url = f'https://kyfw.12306.cn/otn/leftTicket/queryTicketPrice?' \f'{parse.urlencode({"train_no": train_no})}&' \f'{parse.urlencode({"from_station_no": from_station_no})}&' \f'{parse.urlencode({"to_station_no": to_station_no})}&' \f'{parse.urlencode({"seat_types": seat_types})}&' \f'{parse.urlencode({"train_date": self.train_date})}'data = {}for _ in range(20):try:self.headers['User-Agent'] = str(UserAgent().random)content = self.session.get(url, headers=self.headers, proxies=proxy, timeout=5)content = content.content.decode('utf-8')data = json.loads(content)breakexcept:print('查询价格请求失败,重新请求')time.sleep(2)return data.get('data')if __name__ == '__main__':print('--------------------12306信息查询-----------------------')while True:from_station = input('请输入出发地:') or '杭州'if from_station in Citys:breakwhile True:to_station = input('请输入目的地:') or '太原'if to_station in Citys:breakpattern = re.compile('\d{4}-\d{2}-\d{2}')while True:date = input('请输入出发时间(注意格式:2022-02-01, 默认情况下为购票当日):')if not date or re.match(pattern, date):breakif not date:date = Time()[0] + '-' + Time()[1] + '-' + Time()[2]train = Train(from_station, to_station, date)#information = train.train()print('-------------------------------------------------------')print('-------------------12306查询结果如下---------------------')print('-------------------------------------------------------')print('| 车次 | 出发站 | 到达站 | 出发时间 | 到达时间 | 历时 | 商务座 | 一等座 | 二等座 | 软卧 | 硬卧 | 硬座 | 无座 |直接买 | 备选 |')for info in information: print(info)print('-------------------------------------------------------')

当然目前的版本还是存在着问题:就是这个排序太过死板,只考虑了出发站和到达站的距离,没有考虑座位信息,比如本来可以多买几站坐着回去,但非要为了少花点钱补票站着,也不太合适。

因此为了能舒舒服服的回家,着手下一版…

返乡之路不容易之12306余票查询并给出备选方案v2相关推荐

  1. 返乡之路不容易之12306余票查询并给出备选方案

    马上过年了,在外打工的游子终于要踏上返乡的旅程,尽管疫情严重,但家还是要回的,咱这离家远的一年到头只能回去一两次,想家呀!不说了,打开12306买票吧,我一看,果然没票了,这商务一等座咱也坐不起呀,这 ...

  2. 火车票查询系统(一)——利用python实现监控12306余票查询

    最近春运高峰,本人从12月开始买过年回家的火车票,然鹅~现实总是那么残酷,放票时间记不住,要么就是不能实时盯着刷票,外面的软件也不知道靠不靠谱,反正是没买到票,所以一怒之下,自己做一个12306余票查 ...

  3. 12306 余票查询API浅探索

    12306 余票查询API浅探索 余票查询请求分析 响应数据解密代码查找调试 数据说明 余票查询请求分析 点击查询,F12进入控制台,进入NetWork面板,查看发送的请求: 点入请求查看详情,很明显 ...

  4. 最新Python实现12306余票查询系统

    首先感谢这篇博客的引导,但是这篇文章已经比较旧了,12306进行了改版,源码编排方式有了变化.12306源码获取的方法可以从这篇博客中学习,我在这里主要贴出对于目前最新的12306的Python余票查 ...

  5. Python之12306余票查询

    简单的Python学习,用Python完成一个12306余票查询- Python之12306余票查询 参考资料来自Python 实现火车票查询工具 需要用到的第三方库 requests,使用 Pyth ...

  6. 铁路局12306余票查询的实现

    离上次写的类似于铁道部12306城市选择框的实现过了都快一个月了,最近一直在忙,在学习sharepoint2013,虽然早就实现了12306的余票查询,但是一直没抽出时间来写,刚好这几天也快放假了,抽 ...

  7. 12306余票查询(爬虫)

    经常因为忙而忘记给妹子订票. 每次结果不言而喻. 登个12306查下余票其实很简单,但不知道为什么就是不想登. 所以弄了个爬虫脚本直接查询余票.希望下个女票不会这样尴尬,哈哈哈哈哈哈 12306首页为 ...

  8. 使用c#中的HttpWebRequest实现12306余票查询

    准备工作 下载用于解析JSON的类库(Newtonsoft.Json.dll)(下载地址:Newtonsoft.Json.dll): 在C#项目中添加对其的引用. 在程序中导入命名空间: 1 2 3 ...

  9. php 12306余票查询,PHP火车余票查询的API,12306官方的API

    我在做自己微信的时候需要的火车票查询的东西,注册的东西等等太多了,今天就简单的做了下测试看能获取到不,数据怎么样处理速度怎么样,因为是个人做所以就没有去其他网站上找接口,就自己做了下!下边把方法放出来 ...

最新文章

  1. python非贪婪、多行匹配正则表达式例子[转载]
  2. gcc c语言标准,GCC 7.1发布 支持当前所有的C ++ 17标准
  3. linux c多进程多线程,linux下的C\C++多进程多线程编程实例详解
  4. OpenJudge——0003:jubeeeeeat
  5. 七夕赶上服务器架构升级,女朋友的约会怎么办
  6. DOM获取元素、事件基础、操作元素、节点操作
  7. springmvc中@PathVariable和@RequestParam的区别(百度收集)
  8. python入门教程傻瓜版_大数据学习资料集--2014-12-23
  9. 文档管理系统 Mayan EDMS安装
  10. java ip138_判读ip所在地(通过百度ip138的提供的服务) java实现
  11. 抽象工厂模式(优缺点、使用场景、具体实现)
  12. tp5.1 系统常量
  13. IOS APP 发布App Store (同一开发帐号,发布N个APP)
  14. IMX6UL系列小屏驱动之像素时钟无法修改
  15. 踩坑记录丨记Jekyll + Github Pages搭建个人博客时遇到的各种问题
  16. java过滤ios表情,JS前端去掉emoji表情和Java后台处理emoji表情方法
  17. 大前端CPU优化技术--NEON intrinsics进阶
  18. 从零学Java(17)之字符串连接,你要成为小学生之友吗?
  19. b500k带开关电位器内部构造_b500k开关摇杆电位器,直滑电位器103
  20. 第十二届蓝桥杯D题 货物摆放

热门文章

  1. 设计模式---装饰器模式(C++实现)
  2. 如何在Win11调出IE11浏览器?
  3. 创建本地的https证书
  4. 唯美烟花特效登录页面,我感觉自己又行了
  5. c盘满了怎么清理垃圾而不误删?C盘清理,4个方法!
  6. 学计算机大学报什么专业好就业前景,学哪些专业就业前景好?附未来最吃香的十大专业...
  7. Java计算平均成绩
  8. BZOJ1066 蜥蜴
  9. java主机哪儿好_java虚拟主机哪个好,香港java虚拟主机哪里有!
  10. OpenCV路在何方