程序运行效果图:
首先简单介绍下用到的2个重要的库:requests和docopt,可使用命令pip install requests docopt进行安装。
- requests是一个很实用的Python HTTP客户端库,编写爬虫和测试服务器响应数据时经常会用到。
- docopt 命令行参数解析库,docopt 本质上是在 Python 中引入了一种针对命令行参数的形式语言,需实现两个部分:1、在代码的最开头使用 """ """文档注释的形式写出符合要求的文档,就会自动生成对应的parse;2、在代码执行中加入
arguments
=
docopt(__doc__
)。
通过分析12306的网站发现请求中的站点都是站点代码,所以先通过下列代码parse_stations.py得到站点名称:站点代码的列表,
执行:python parse_stations.py > stations.py 将打印结果重定向到stations.py文件中,方便后续调用
# -*- coding: utf-8 -*-
import re
import requests
from pprint import pprint
# 此url地址可以通过分析12306页面源码得到
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9044'
response = requests.get(url, verify=False) #利用requests发送get请求
# 此句得到的数据格式是[('北京北', 'VAP'), ('北京东', 'BOP'), ('北京', 'BJP'), ...]
# 使用正则表达式,([\u4e00-\u9fa5]+)至少一个汉字,([A-Z]+)至少一个大写字母
stations = re.findall('([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
# 调用dict()后将格式转为字典{'北京北': 'VAP', '北京东': 'BOP', '北京': 'BJP', ...]
print(dict(stations))
完整代码如下所示:
1、由于stations中字典的key是站点名称,value是站点代码,而页面中查询结果使用的是站点代码,所以需要将此字典的key、value互换
new_stations = {v : k for k, v in stations.items()}
2、12306余票查询的响应内容的格式跟
实验楼文档中的已经不一样了,可明显看出不同字段由“|”分隔,可分析出不同字段的含义。
# -*- coding: utf-8 -*-
""" 命令行火车票查看器
Usage:
tickets [-gdtkz] <from> <to> <date>
Options:
-h,--help 显示帮助菜单
-g 高铁
-d 动车
-t 特快
-k 快速
-z 直达
Example:
tickets 北京 上海 2018-01-13
tickets -dg 成都 南京 2018-01-13
"""
import requests
import json
from docopt import docopt
# key:站点名称 value: 站点代码
from stations import stations
# 反转k,v形成新的字典,站点代码:站点名称
new_stations = {v : k for k, v in stations.items()}
def cli():
"""command-line interface"""
arguments = docopt(__doc__)
# python tickets.py -dg 合肥 天柱山 2018-01-13
# {'-d': True,
#'-g': True,
# '-k': False,
# '-t': False,
# '-z': False,
# '<date>': '2018-01-13',
# '<from>': '合肥',
# '<to>': '天柱山'}打印arguments得到一个字典
# print(arguments)
from_station = stations.get(arguments['<from>'])
to_station = stations.get(arguments['<to>'])
date = arguments['<date>']
url = 'https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(date, from_station, to_station)
info_list = get_train_info(url)
print(','.join(info_list))
# resp = requests.get(url, verify=False)
# print(json.loads(resp.text)['data']['result'])
# print(resp.json()['data']['result'])
def get_train_info(url):
info_list = []
try:
resp = requests.get(url, verify=False)
raw_trains = resp.json()['data']['result']
for raw_train in raw_trains:
data_list = raw_train.split('|')
train_no = data_list[3]
# 起点站
from_station_code = data_list[6]
from_station_name = new_stations.get(from_station_code)
# 终点站
to_station_code = data_list[7]
to_station_name = new_stations.get(to_station_code)
start_time = data_list[8]
arrive_time = data_list[9]
time_fucked_up = data_list[10]
first_class_seat = data_list[31] or '--'
second_class_seat = data_list[30] or '--'
soft_sleeper = data_list[23] or '--'
hard_sleeper = data_list[28] or '--'
hard_seat = data_list[29] or '--'
no_seat = data_list[26] or '--'
info = '车次:{},出发站:{},目的地:{},出发时间:{},到达时间:{},历时:{},一等座:{},二等座:{},软卧:{},硬卧:{},硬座:{},无座:{}\n\n'.format(
train_no, from_station_name, to_station_name, start_time, arrive_time, time_fucked_up, first_class_seat,
second_class_seat, soft_sleeper, hard_sleeper, hard_seat, no_seat)
info_list.append(info)
return info_list
except:
return "输入信息有误,请重新输入!"
if __name__ == '__main__':
cli()
本文介绍比较简单,可参看
实验楼文档,目前打印的形式也比较丑,还需完善。