Python爬取12306实现火车票查询

介绍:

除了官方的12306网站,其他的很多网站都提供了购买查询的功能,像携程的铁友,途牛等等。这些网站他们盈利大都是通过广告的收入,以及通过购票带动的网站内酒店、景点额外收入,他们的网站界面友好型优于12306,使得在购票入口中占据了一定份额。但是归根到底,所有购票的APP、网站都是拿的12306的基础数据,或者说是12306的接口,只是做了前端界面的设计。在程序员的眼中,有接口就能创造世界(然而这只是我的一种臆想)。本文就是通过查询接口,在命令行窗口中实现火车票查询。  

完整思路和处理过程:

首先要拿到查询的接口就要采用抓包的方式,常用的抓包工具:浏览器自带的检查功能,FIddler抓包工具(功能更强大)


但是在URL中的出发地和目的地都是字母,那我们必须要拿到所有的车站信息列表才能构造URL请求,在网页Sources和网页源代码中分别寻找station的文件



对station进行解析:parse_station.py 

通过“>”,来重定向输出的内容,通过“>>”将输出的内容追加到文件中,这里我们通过 $ python parse_station.py > station.py

# coding: utf-8
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.8971'
    # 发送get请求,不判断证书
    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()

在station.py 中建立键值之间的双向关系

def get_name(telecode):

    return names[telecodes.index(telecode)]

def get_telecode(name):
    return telecodes[names.index(name)]

这里先对提取数据的代码中涉及到的点进行总结:

1.两种格式化输出的方式:%和format
In [7]: "%s,%d"%("kzc",18)
Out[7]: 'kzc,18'
In [8]: '{0},{1}'.format('kzc',18)
Out[8]: 'kzc,18'
2.用到的库

requests:爬虫必用的发送请求,获取HTML网页内容的库
docopt:命令行解析工具,可以根据自定义的文档描述,自动生成解析器

prettytable:能让你的数据像MySQL的命令行显示数据的格式一样

colorama:命令行着色库

# coding: utf-8

"""命令行火车票查看器:Usage Options为docopt库固定格式

Usage:
    tickets [-dgktz] <from> <to> <date>

Options:
    -h, --help 查看帮助
    -d         动车
    -g         高铁
    -k         快速
    -t         特快
    -z         直达

Examples:
    tickets 上海 北京 2017-10-10
    tickets -dg 成都 南京 2017-10-10
"""

from docopt import docopt
import requests
from prettytable import PrettyTable
from colorama import Fore
import stations

def cli():
    arguments = docopt(__doc__,version='ticket 1.0')
    from_station = stations.get_telecode(arguments.get('<from>'))
    to_station = stations.get_telecode(arguments.get('<to>'))
    date = arguments.get('<date>')
    # 列表推导式,得到的是查询车次类型的集合
    options = ''.join([key for key,value in arguments.items() if value is True])
    print(options)

    url = ('https://kyfw.12306.cn/otn/leftTicket/query?'
            'leftTicketDTO.train_date={}&'
            'leftTicketDTO.from_station={}&'
            'leftTicketDTO.to_station={}&'
            'purpose_codes=ADULT').format(date,from_station,to_station)

    r = requests.get(url, verify=False)
    # print(r.json())
  #   requests得到的是一个json格式的对象,r.json()转化成python字典格式数据来提取,所有的车次结果result
    raw_trains = r.json()['data']['result']
    pt = PrettyTable()
    pt._set_field_names("车次 车站 时间 经历时 一等座 二等座 软卧 硬卧 硬座 无座".split())
    for raw_train in raw_trains:
        # split切割之后得到的是一个列表
        data_list = raw_train.split("|")
        train_no = data_list[3]
        initial = train_no[0].lower()
        # print(train_no[0])
        # 判断是否是查询特定车次的信息
        if not options or initial in options:
            from_station_code = data_list[6]
            to_station_code = data_list[7]
            from_station_name = ''
            to_station_name = ''
            start_time = data_list[8]
            arrive_time = data_list[9]
            time_duration = data_list[10]
            first_class_seat = data_list[31] or "--"
            second_class_seat = data_list[30] or "--"
            soft_sleep = data_list[23]  or "--"
            hard_sleep = data_list[28] or "--"
            hard_seat = data_list[29] or "--"
            no_seat = data_list[33] or "--"

        pt.add_row([
                   # 对特定文字添加颜色
                   train_no,
                   '\n'.join([Fore.GREEN + stations.get_name(from_station_code) + Fore.RESET, Fore.RED + stations.get_name(to_station_code) +  Fore.RESET]),
                   '\n'.join([Fore.GREEN + start_time + Fore.RESET,Fore.RED + arrive_time +  Fore.RESET]),
                   time_duration,
                   first_class_seat,
                   second_class_seat,
                   soft_sleep,
                   hard_sleep,
                   hard_seat,
                   no_seat
        ])

    print(pt)


if __name__ == '__main__':
    cli()

参考来源: https://www.shiyanlou.com/courses/623/labs/2072/document
最后效果图:很像火车站电子屏幕的效果








猜你喜欢

转载自blog.csdn.net/tigaoban/article/details/78558417
今日推荐