返乡之路不容易之12306余票查询并给出备选方案
马上过年了,在外打工的游子终于要踏上返乡的旅程,尽管疫情严重,但家还是要回的,咱这离家远的一年到头只能回去一两次,想家呀!不说了,打开12306买票吧,我一看,果然没票了,这商务一等座咱也坐不起呀,这可咋整?
买不到直达的,咱要不多花点钱多买几站,或者先买票上车然后补票?
这个想法不错,那我首先得看每一趟车次的所有停靠站,然后再去搜有没有票。拿G3136这趟车来说,首发站是宁波,终点是太原南,那我只要出发站买在宁波到杭州东之间的任一站,终点站买在杭州东和太原南之间的任一站我就可以上车,一想到有办法回家了,心里还有点小激动。
一搜我就懵了,这么多的停靠站,出发站和到达站的排列组合那还不爆炸了?这得把我累死,回个家容易吗?
不对,咱是干啥的呀,鼓捣代码的,这种大量重复的工作交给它来干岂不是快哉,说干就干
此处省略一万字。。。
终于实现了!!!
只要输入出发地、目的地和出发时间,便能输出所有车次信息,有的车次能直接买,有的没法直接买,但是给出了备选,只要在备选中选一个就可以买到票回家喽。
代码如下:
# coding=utf-8import requestsimport urllib.parse as parseimport timeimport jsonimport pretty_errorsimport refrom fake_useragent import UserAgentTRAIN_NUMBER = 2TRAIN = 3DEPARTURE_STATION = 6TERMINUS = 7DEPARTURE_TIME = 8ARRIVAL_TIME = 9DURATION = 10IF_BOOK = 11DATE = 13NO_SEAT = 29HARD_SEAT = 28SOFT_SEAT = 27def Citys(): """ 城市缩写 :return: """ headers = {'User-Agent': str(UserAgent().random)} url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9141' content = requests.get(url=url, headers=headers) content = content.content.decode('utf-8') content = content content_list = content.split('@') dict_city = {} for city in content_list: str_1 = city city_name = str_1 # 城市简称名 city_name_1 = str_1<:str_1.find('|')> # 城市名 dict_city = city_name return dict_citydef 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' + month if len(day) == 1: day = '0' + day return year, month, dayproxy = {'http': '122.226.57.70:8888'}class Train: def __init__(self, from_station, to_station, train_date=Time()<0> + '-' + Time()<1> + '-' + Time()<2> ): self.from_station = from_station self.to_station = to_station self.train_date = train_date self.url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?leftTicketDTO.%s&leftTicketDTO.%s&leftTicketDTO.%s&purpose_codes=ADULT' # self.headers = { # 'Cookie': 'JSESSIONID=073CAA21150F40AA7F05551F7E1B5F5C; RAIL_DEVICEID=STuBhcdGH45k8StSkJVyk_v6qFnIUDpyzg1O9l7IyMoOPPIEEuBEqcBRuuv0WOKJrV4MdxGi08T0AzwlQ3d4guOQ6LTNlh7emO8TdgWZe2Wp3OuA9WIKYP5Ly-a3o-f5uHGmyX8yleCV0nzQDSL9grkJHRjA4syw; RAIL_EXPIRATION=1642644949013; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; BIGipServerpool_index=804258314.43286.0000; route=6f50b51faa11b987e576cdb301e545c4; BIGipServerotn=384827914.24610.0000'} 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()})}&' \ f'{parse.urlencode({"to_station_telecode": Citys()})}&' \ 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 = requests.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'>) from_station_buy = for station in stations_data<:from_station_idx>> to_station_buy = for station in stations_data> return from_station_buy, to_station_buy def train(self): """ 爬取信息 :return: """ url = self.url % (parse.urlencode({"train_date": self.train_date}), parse.urlencode({"from_station": Citys()}), parse.urlencode({"to_station": Citys()})) self.headers<'User-Agent'> = str(UserAgent().random) content = self.session.get(url, headers=self.headers, proxies=proxy, timeout=5) # content = requests.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 = <> for train in dict_train: train_split = train.split('|') from_station_buy, to_station_buy = self.station(train_split) buy = <> for from_station, to_station in < for x in from_station_buy for y in to_station_buy>: if train_split == 'N' and self.book_if(from_station, to_station, train_split): buy.append(f'{from_station}-{to_station}') train_str = , dict_map>, dict_map>, train_split, train_split, train_split, '可以' if train_split == 'Y' else '不可以', ', '.join(buy)> res.append('| ' + ' | '.join(train_str) + ' |') return res def 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()}), parse.urlencode({"to_station": Citys()})) self.headers<'User-Agent'> = str(UserAgent().random) content = self.session.get(url, headers=self.headers, proxies=proxy, timeout=5) # content = requests.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_number, dict_train)) if not train: return return True if train<0>.split('|') == 'Y' else Falseif __name__ == '__main__': print('--------------------12306信息查询-----------------------') while True: from_station = input('请输入出发地:') or '杭州' if from_station in Citys(): break while True: to_station = input('请输入目的地:') or '太原' if to_station in Citys(): break pattern = re.compile('\d{4}-\d{2}-\d{2}') while True: date = input('请输入出发时间(注意格式:2022-02-01, 默认情况下为购票当日):') if not date or re.match(pattern, date): break if 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('-------------------------------------------------------')
这是第一版的实现,目前还只是粗略地给出备选,省去大家手动搜索的时间,还有好多功能没有实现
- 没有备选排名:虽然给出了备选,但哪个备选好一点没有给个排序
- 没有座位信息(商务/一等/二等/硬座/无座):虽然能买,但是不一定能买到适合自己的(便宜的),有点奢侈了
能回家已经很开心了,准备实现第二版...