Python查询12306车次信息

代码采用python3编写,本来用PyQt5做了一个方便的查询界面,不过这篇文章主要来介绍爬取功能,就不把Qt代码贴出来了,可以先看一下效果。运行后只需输入起始站、终点站和日期就可以看到如下效果,和火车站的显示屏有点相似。
在这里插入图片描述
查询车次的脚本还是比较简单的,只是访问一下车次信息而已,不需要用到Cookie和Session这些信息。
首先把过程写出来:
1.分析查询需要的信息参数
2.找到查询的链接
3.写代码
4.测试


首先分析网站

通过抓包可以知道查询请求的网址链接,比如:https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-01-24&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT
在这里插入图片描述
因为12306的网站信息是经常变化的,所以需要实时地在代码中修改它,到时候再查看即可。其中有三个参数:
train_date
from_station
to_station
我们可以看到其中的车站信息都不是以汉字的形式给出的,比如上面的BJP和SHH。所以需要把车站名和这些代号的联系找出来。

获取车站信息

这些信息储存再一个js里面,链接:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9091

再查询的界面抓包也可以找到,打开这个链接可以看到这样的界面:在这里插入图片描述
把这些信息截取下来:

import re
import requests
from pprint import pprint

def main():
    url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9091'
    # 获取信息,且不判断证书
    response = requests.get(url, verify=False)
    # 使用正则表达式提取所有的站点
    stations = dict(re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text))
    # 分别把车站名和代号提取出来
    pprint(stations.keys())
    pprint(stations.values())
 
if __name__ == '__main__':
    main()

新建一个stations.py来储存这些信息并且建立双向关系:

names = ['北京北', '北京东', '北京', '北京南', '北京西'......]
codes = ['VAP', 'BOP', 'BJP', 'VNP', 'BXP'......]

#关系转换
def get_name(code):
    return names[telecodes.index(code)]
    
def get_code(name):
    return codes[names.index(name)]

写代码进行提取

爬取需要用到的库:
requests:爬虫必用的发送请求,获取HTML网页内容的库。
发挥爬取功能只需要这个库就绰绰有余,为了显示方便我们加上一个显示库:
prettytable:能让你的数据像MySQL的命令行显示数据的格式一样。
本次爬取不需要请求头也能正常进行,当然加上也可以。

以下是主要代码,可以根据需求自己更改,如果是直接复制下来可能无法成功运行,毕竟写了有一段时间了,12306每隔一段时间就会更改网站信息,所以看情况更改。当然,重在学习知识。
这是在win10上面编写的,要在linux上运行就加上必要的头信息。

import requests
from prettytable import PrettyTable
import stations
from colorama import init, Fore

#请求信息
f_station = stations.get_code(input('起始站:'))
t_station = stations.get_code(input('终点站:'))
date = input('出行时间:')

# 构造请求地址
url = ('https://kyfw.12306.cn/otn/leftTicket/queryZ?'
        'leftTicketDTO.train_date={}&'
        'leftTicketDTO.from_station={}&'
        'leftTicketDTO.to_station={}&'  
        'purpose_codes=ADULT').format(date, f_station, t_station)
        
# 不判断证书
r = requests.get(url, verify=False)
r.encoding = r.apparent_encoding

# requests得到的是一个json格式的对象
# 原始火车数据
raw_trains = r.json()['data']['result']
header = ('车次 车站 时间 历时 特等座 一等 二等 高级软卧 软卧 硬卧'
               + '软座 硬座 无座 备注').split()

# 历时转换
def get_duration(raw_trains):
    duration = raw_train[10].replace(':', '小时') + '分'
    if duration.startswith('00'):
        return duration[4:]
    if duration.startswith('0'):
        return duration[1:]
    return duration
    
# 返回每个车次的基本信息
def trains(raw_train):
    for raw_train in raw_trains:
        # 列车号
        train_no = raw_train[3]
        # 得到什么列车并小写
        initial = train_no[0].lower()
        # 反转station所对应的字典
        stations_re = dict(zip(stations.values(), stations.keys()))
        # 将车次的信息保存到列表中
        # train 出发地
        begin_station = stations_re.get(raw_train[4])
        # train 目的地
        end_station = stations_re.get(raw_train[5])
        # your 出发地
        from_station = stations_re.get(raw_train[6])
        # your 目的地
        to_station = station_re.get(raw_train[7])
        # 判断是起始还是经过
        begin_flag = __check_equals(begin_station, from_station)
        end_flag = __check_equals(end_station, to_station)
        train = [
            train_no,
            # 时间
            get_duration(raw_train),
            # 历时
            raw_train[9],
            # 特等座
            __show_color(raw_train[31]),
            # 一等
            __show_color(raw_train[30]),
            # 二等
            __show_color(raw_train[22]),
            # 高级软卧
            __show_color(raw_train[23]),
            # 软卧
            __show_color(raw_train[25]),
            # 软座
            __show_color(raw_train[24]),
            # 硬座
            __show_color(raw_train[29]),
            # 无座
            __show_color(raw_train[26]),
            # 备注
            __show_color(raw_train[1]),
            ]
        # 更改不运行车次的时间和历时
        if raw_train[14] == 'null':
            train[2] = '--\n--'
        train[3] = '--'

        for i, item in enumerate(train):
            if not item:
                train[i] = '--'
        yield train
        
def __check_equals(from_station, to_station):
    if from_station == to_station:
        return '始'
    else:
        return '过'

def __get_color(color, content):
    return color + content + Fore.RESET

def __show_color(content):
    if content == '有':
        return Fore.GREEN + content + Fore.RESET
    else:
        return content
        
#打印信息
def pretty_print():
    pt = PrettyTable()
    pt._set_field_names(self.header)
    for train in self.trains():
        pt.add_row(train)
    print(pt)

if __name__ == '__main':
    time = get_duration(raw_trains)

部分参考来源:https://blog.csdn.net/tigaoban/article/details/78558417

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值