利用Python爬虫,查询12306车次信息

2023-11-02

效果展示:

在这里插入图片描述
在这里插入图片描述

分析目标网站:

  1. 进入12306官网
  2. 商丘南汝州为例,在点击查询后会跳转到查询结果的网站

在这里插入图片描述

  1. 右键点检查审查元素,在弹出的控制台中点网络network,如果没有显示数据的话,刷新一下网页就有了;在点击Fetch/XHR后会发现有一个名为query...的请求,点开它后再点击预览会发现,车票的信息就在这个里面

在这里插入图片描述
4. 在找到存放的车票信息后,按常理直接对目标链接发送请求即可,但我们通过查看URL携带的参数时,不难发现:
- 第一个参数:查询的日期,固定格式(YYYY-MM-DD)
- 第二个和第三个参数:不同城市对应的英文代码
- 第四个参数:固定值

在这里插入图片描述

获取所有城市英文代码:

  1. 这里不在过多叙述,找到URL链接直接发送请求,获取响应的数据即可,代码如下:
url = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9053"
print("正在获取数据。")
# 发送请求,获取返回的数据
res = requests.get(url)
data = str(res.content, encoding="utf8")
print(data)

在这里插入图片描述

  1. 通过返回的数据可以发现,所有的数据都是以|符号隔开的,所以使用split("|")对数据进行处理,代码如下:
dict_data = dict()
# 根据'|'分隔数据
list_data = data.split('|')
# 从下标'1'开始, 每间隔5个为字典key
result_x = list_data[1:len(list_data):5]
# 从下标'2'开始, 每间隔5个为字典value
result_y = list_data[2:len(list_data):5]
# 循环将数据写入字典
for i in range(len(result_x)):
    dict_data[result_x[i].replace(" ", "")] = result_y[i]
print(dict_data)
  1. 将数据提取后保存到工作路径的data文件夹下,这样后期使用时,就无需再次对该网站发送请求了,代码如下:
json_data = json.dumps(dict_data, indent=1, ensure_ascii=False)
    with open("./data/city_data.json", 'w') as w:
        w.write(json_data)
        print("数据保存完成!")
  1. 预览city_data.json文件,所有的数据都已保存在了该文件里,共三千多个不同的城市。

完整版代码:

import requests
import json


def get_city_data():
    url = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9053"
    print("正在获取数据。")
    # 发送请求,获取返回的数据
    res = requests.get(url)
    data = str(res.content, encoding="utf8")
    # 格式化返回的数据
    response_format(data)


def response_format(data):
    dict_data = dict()
    # 根据'|'分隔数据
    list_data = data.split('|')
    # 从下标'1'开始, 每间隔5个为字典key
    result_x = list_data[1:len(list_data):5]
    # 从下标'2'开始, 每间隔5个为字典value
    result_y = list_data[2:len(list_data):5]
    # 循环将数据写入字典
    for i in range(len(result_x)):
        dict_data[result_x[i].replace(" ", "")] = result_y[i]
    # 保存数据
    save_data(dict_data)


def save_data(dict_data):
    json_data = json.dumps(dict_data, indent=1, ensure_ascii=False)
    with open("./data/city_data.json", 'w') as w:
        w.write(json_data)
        print("数据保存完成!")


get_city_data()

获取12306车次的信息:

  1. 在前面成功获取所有城市对应的英文代码后,先让用户输入需要查询的日期、出发地和目的地信息,从文件中提取城市对应的英文代码,代码如下:
date = input("请输入出发日期(YYYY-MM-DD):")
begin = input("请输入出发地:")
end = input("请输入目的地:")
# 读取生成的json文件
city_list = json.load(open('./data/city_data.json', 'r'))
# 获取城市对应的英文代码
begin_id = city_list[begin]
end_id = city_list[end]
  1. 再获取到城市对应的英文代码后,构建请求头和需要携带的参数,代码如下:
# 请求的目标链接
self.url = "https://kyfw.12306.cn/otn/leftTicket/query"
# 构建请求头
self.headers = {
    # 失效时,需要更新cookie
    "Cookie": "JSESSIONID=5BCD4997EB7387D6F2F26CF860144AE6; RAIL_EXPIRATION=1653658158853; RAIL_DEVICEID=OYdRuCkXuonxJIyWihWNwMa5x-JAFt30BYWuZd9lAzHOtXh1TezSjz0oQm9n0TYq3InM3pJKfGexQCQEFpOqkTJq5XqXQ_taNYf1hTlQ6YWdWKWrJosRmvmDdUmt9omgZ2sDBAmcohSg662SJ-55JM97DtJQ0sfA; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; BIGipServerotn=384827914.50210.0000; BIGipServerpool_passport=31719946.50215.0000; route=c5c62a339e7744272a54643b3be5bf64; _jc_save_toDate=2022-05-25; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u5546%u4E18%2CSQF; _jc_save_toStation=%u90D1%u5DDE%2CZZF; _jc_save_fromDate=2022-05-29",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53"
}
# 构建请求所需参数
self.params = {
    "leftTicketDTO.train_date": date,
    "leftTicketDTO.from_station": begin_id,
    "leftTicketDTO.to_station": end_id,
    "purpose_codes": "ADULT"
}

在这里插入图片描述

  1. 构建完成必须的参数后,发送请求观察返回的数据发现,每条数据间也是由|隔开的,用同样的方法对数据进行分隔,代码如下:
res = requests.get(self.url, headers=self.headers, params=self.params).json()
data_list = res['data']['result']
for data in data_list:
	all_data_list = data.split('|')
	# 此处使用枚举,方便后期查看列表下标
	for i,j in enumerate(all_data_list):
		print(f"[{i}__{j}]")
  1. 提取列表元素中的数据,获取车次的相关信息,这里直接分享我查找的,不同车次信息对应列表元素的各下标,代码如下:
trains_msg = [
	all_data_list[3],
	all_data_list[8],
	all_data_list[9],
	all_data_list[10],
	all_data_list[32] if all_data_list[32] != "" else "--",
	all_data_list[31] if all_data_list[31] != "" else "--",
	all_data_list[30] if all_data_list[30] != "" else "--",
	all_data_list[23] if all_data_list[23] != "" else "--",
	all_data_list[28] if all_data_list[28] != "" else "--",
	all_data_list[29] if all_data_list[29] != "" else "--",
	all_data_list[26] if all_data_list[26] != "" else "--",
	all_data_list[1] if all_data_list[1] != "" else "--"
]
print(trains_msg)
  1. 到此以可以获取到车次的信息了,但以这种方式显示显然并不友好,下面介绍美化方法!

以表格形式输出车次信息:

  1. 这里我们使用的是prettytable第三方库,这个库可以将我们的数据进行表格化显示,安装方法:pip install prettytable

  2. 导入该模块,并实例化对象:

from prettytable import PrettyTable
# 实例化美化表格对象
self.pt= PrettyTable()
  1. 构建表头,并将提取的数据以表格的形式显示出来:
# 创建表头,即表格的首行信息
header_list = [
	['车次', '出发时间', '到达时间', '历时', '商务座', '一等座', '二等座', '软卧', '硬卧', '硬座', '无座', '备注']
]
# 将表头信息添加进展示表格的表头
self.pt.field_names = header_list[0]
# 将提取到的车次信息添加到表格的内容信息
self.pt.add_row(trains_msg)
# 打印表格
print(self.pt)
  1. 此时的效果如下图所示,可以清晰的看出表格的对齐有点问题,所有此时为程序增加保存数据的功能!

在这里插入图片描述

将数据保存为Excel表格:

  1. 这里我们使用的是openpyxl第三方库,这个库可以将我们的数据进行表格化显示,安装方法:pip install openpyxl

  2. 导入该模块,并实例化对象:

from openpyxl import Workbook
wb = Workbook()
  1. 因为车次信息是不定的,所有车次少时可以无需保存,这时就需要用户自己选择是否要保存信息了,代码如下:
num = input("如果展示不清晰,需要保存时请扣1:")
if num == "1":
    wb = Workbook()
    sheet = wb.create_sheet("车次信息", -1)
    # 遍历表格索引,写入数据
    for x in range(len(trains_data_list)):
        for y in range(len(trains_data_list[x])):
            sheet.cell(x + 1, y + 1).value = trains_data_list[x][y]
    wb.save(f"./data/{date}_{begin}_{end}.xlsx")
    print("数据保存完成!")
  1. 为了更加人性化,这里通过用户输入的日期、出发地和目的地,再拼接出12306购票的直达链接,代码如下:
print(
	"12306直达链接(复制到浏览器打开):",
	"https://kyfw.12306.cn/otn/leftTicket/init?"
	"linktypeid=dc&"
	f"fs={begin},{begin_id}&"
	f"ts={end},{end_id}&"
	f"date={date}&"
	"flag=N,N,Y"
)
  1. 此时就完全实现了这次的查票查询,谢谢观看~

在这里插入图片描述

最终完整版代码:

  • 这个是我平时做爬虫练习时,汇总的案例的其中之一,代码比较适合新手学习,该Github仓库的爬虫案例也会在以后不断更新,有兴趣学习爬虫的可以来捧捧场哦QwQ。仓库链接:https://github.com/cjladmin/spider_cases
import requests
import json
from openpyxl import Workbook
from prettytable import PrettyTable
from save_city_list import get_city_data


class GetTrains:
    def __init__(self, date, begin_id, end_id):
        self.url = "https://kyfw.12306.cn/otn/leftTicket/query"
        # 构建请求头
        self.headers = {
            # 失效时,需要更新cookie
            "Cookie": "JSESSIONID=5BCD4997EB7387D6F2F26CF860144AE6; RAIL_EXPIRATION=1653658158853; RAIL_DEVICEID=OYdRuCkXuonxJIyWihWNwMa5x-JAFt30BYWuZd9lAzHOtXh1TezSjz0oQm9n0TYq3InM3pJKfGexQCQEFpOqkTJq5XqXQ_taNYf1hTlQ6YWdWKWrJosRmvmDdUmt9omgZ2sDBAmcohSg662SJ-55JM97DtJQ0sfA; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; BIGipServerotn=384827914.50210.0000; BIGipServerpool_passport=31719946.50215.0000; route=c5c62a339e7744272a54643b3be5bf64; _jc_save_toDate=2022-05-25; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u5546%u4E18%2CSQF; _jc_save_toStation=%u90D1%u5DDE%2CZZF; _jc_save_fromDate=2022-05-29",
            # "Referer": referer,
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53"
        }
        # 构建请求所需参数
        self.params = {
            "leftTicketDTO.train_date": date,
            "leftTicketDTO.from_station": begin_id,
            "leftTicketDTO.to_station": end_id,
            "purpose_codes": "ADULT"
        }
        # 实例化美化表格对象
        self.pt = PrettyTable()

    def run(self):
        # 对目标网址发送请求
        res = requests.get(self.url, headers=self.headers, params=self.params).json()
        data_list = res['data']['result']
        # 构造表格的表头,用于展示和保存
        header_list = [
            ['车次', '出发时间', '到达时间', '历时', '商务座', '一等座', '二等座', '软卧', '硬卧', '硬座', '无座', '备注']
        ]
        # 将表头信息添加进展示表格的表头
        self.pt.field_names = header_list[0]
        for data in data_list:
            # 格式化添加表数据
            trains_msg = self.format_data(data)
            # 将数据添加进列表,用于保存
            header_list.append(trains_msg)
        # 打印表格
        print(self.pt)
        # 返回车次信息列表
        return header_list

    def format_data(self, data):
        # 将返回的数据以'|'进行分隔
        all_data_list = data.split('|')
        # 提取车次的信息
        trains_msg = [
            all_data_list[3],
            all_data_list[8],
            all_data_list[9],
            all_data_list[10],
            all_data_list[32] if all_data_list[32] != "" else "--",
            all_data_list[31] if all_data_list[31] != "" else "--",
            all_data_list[30] if all_data_list[30] != "" else "--",
            all_data_list[23] if all_data_list[23] != "" else "--",
            all_data_list[28] if all_data_list[28] != "" else "--",
            all_data_list[29] if all_data_list[29] != "" else "--",
            all_data_list[26] if all_data_list[26] != "" else "--",
            all_data_list[1] if all_data_list[1] != "" else "--"
        ]
        # 增添表内容
        self.pt.add_row(trains_msg)
        # 将提取的信息返回,用于保存
        return trains_msg

    def save_data(self, trains_data_list, date, begin, end):
        num = input("如果展示不清晰,需要保存时请扣1:")
        if num == "1":
            wb = Workbook()
            sheet = wb.create_sheet("车次信息", -1)
            # 遍历表格索引,写入数据
            for x in range(len(trains_data_list)):
                for y in range(len(trains_data_list[x])):
                    sheet.cell(x + 1, y + 1).value = trains_data_list[x][y]
            wb.save(f"./data/{date}_{begin}_{end}.xlsx")
            print("数据保存完成!")


if __name__ == '__main__':
    # 更新城市对应的英文代码,需要时再启用
    # get_city_data()
    date = input("请输入出发日期(YYYY-MM-DD):")
    begin = input("请输入出发地:")
    end = input("请输入目的地:")
    # 读取生成的json文件
    city_list = json.load(open('./data/city_data.json', 'r'))
    # 获取城市对应的英文代码
    begin_id = city_list[begin]
    end_id = city_list[end]
    gt = GetTrains(date, begin_id, end_id)
    trains_data_list = gt.run()
    # 是否需要保存数据
    gt.save_data(trains_data_list, date, begin, end)
    print(
        "12306直达链接(复制到浏览器打开):",
        "https://kyfw.12306.cn/otn/leftTicket/init?"
        "linktypeid=dc&"
        f"fs={begin},{begin_id}&"
        f"ts={end},{end_id}&"
        f"date={date}&"
        "flag=N,N,Y"
    )
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用Python爬虫,查询12306车次信息 的相关文章

随机推荐

  • (基于安卓app开发的毕业设计)家庭图书管理系统(附论文+源码)

    大家好 我是职场程序猿 感谢您阅读本文 欢迎一键三连哦 当前专栏 安卓app毕业设计 精彩专栏推荐 微信小程序毕业设计 Java毕业设计 目录 一 项目简介 二 系统功能结构图 三 系统核心功能模块部分截图 3 1信息展示界面 2 2分类展
  • 数学建模Word排版——样式

    样式 与大多数论文写作类似 使用样式能够极大提高排版速度 提前准备好自己的样式文件 可以快速进行写作 一般是边写边排版 初期只应用所需样式即可 最后再对图表的位置 次序等进行进一步调整 创建所需样式 标题名称 为了便于区分 可以考虑加上中文
  • proteus 遇到Program file is not specified和Real Time Simulation failed to start.

    proteus 遇到Program file is not specified和Real Time Simulation failed to start 在使用protues做仿真的时候 点击开始运行 发现 点进去看看错误原因 这其实是粗心
  • Django常用命令

    python manage py migrate 新建项目 django admin py startproject 项目名 以下命令要先进入项目目录下才能执行 cd 项目名 新建app 一个项目可以有多个app 通用的app也可以在多个项
  • 如何做一个超链接,打开一个新窗口而保留原来的窗口?

    让整个网页页面内的链接都在新窗口打开 在head中加入 让一个DIV里所有链接在新窗口打开 在body中加入 a href 链接路径 target blank a 这样就可以啦 这就是表示在新的窗口打开链接 blank 在新窗口中打开链接
  • 组合特征(五)countvector(w)+doc(w)+hash(w)

    将countvector word hash word 和doc2vec word 拼接成新特征 import pickle from scipy import sparse from scipy sparse import hstack
  • 对数及对比度拉伸变换

    对数及对比度拉伸变换 对数和对比度拉伸变换是 动态范围操作的基本工具 表达式 g c log 1 f 其中c是一个常数 f是浮点数 对数变换 应用 压缩动态范围 实现了图像灰度扩展和压缩功能 扩展低灰度值而压缩高灰度值 让图像的灰度分布更加
  • 在Linux服务器上下载并安装Nginx

    在Linux服务器上下载并安装Nginx 下载安装包 进入官网 http nginx org en download html 下载稳定版 进入usr下的local文件夹 cd usr local 在user下的local中创建nginx文
  • 【毕业设计】基于单片机的心率血氧健康监测手表 - 物联网 嵌入式

    文章目录 0 前言 1 简介 2 主要器件 3 实现效果 4 设计原理 4 1 硬件准备 4 2 传感器和算法 5 部分核心代码 5 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目缺少创新和亮点 往往达不到
  • How to debug release mode program in visual studio

    一般情况下 因为release模式的需求 在release模式下的程序是没有debug符号信息的 但是我们可以通过修改Visual studio中的选项来enable release模式的程序的debug 右键项目 选择属性 关闭如下优化选
  • kolla 部署 openstack v1.0

    准备 hosts配置 cat lt lt EOF gt gt etc hosts 192 168 179 90 kolla ansible controller 192 168 179 91 kolla ansible compute EO
  • 2023国赛数学建模B题思路分析 - 多波束测线问题

    1 赛题 B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术 声波在均匀介质中作匀 速直线传播 在不同界面上产生反射 利用这一原理 从测量船换能器垂直向海底发射声波信 号 并记录从声波发射到信号接收的传播时间
  • ItelliJIDEA安装与破解

    IntelliJ IDEA下载地址 官网下载 http www jetbrains com idea IntelliJ IDEA安装教程就不赘述了 百度一下就有 下面主要讲IntelliJ IDEA破解步骤 1 下载补丁 下载地址 http
  • 傅里叶变换的实质--正交之美

    引 最近在搞一个音频解码器 将随意录制好的声音按照不同的频率分离出不同的音频流 然后推到不同的音箱中 如果再考虑一下音场的谐性 那就是一个N 1声道的解码系统了 我只是想在女儿 或者儿子 出生之前为她做点事情 以便能最终做出个东西送给她 或
  • Python2.7.X的安装

    感谢关注的同志 今天写一下Python2 7 X的安装 与大家分享 1 官网下载 在python官网下载https www python org downloads 我这里下载的是Python 2 7 15 https download c
  • 汇尔M1折腾记

    汇尔M1原价挺贵的 现在淘宝11块钱 所以买了几个 带近4000毫安电池 可当充电宝 usb口支持3G上网卡分享 一个可移动的路由器 好像厂家倒闭了 其他功能基本与普通路由器一致 使用了openwrt系统 我也不知道这个是啥 好像是个路由器
  • make 时遇到 /usr/bin/ld: cannot find -lstdc++

    在linux环境编译应用程式或lib的source code时常常会出现如下的错误讯息 usr bin ld cannot find lxxx 这些问题都是因为找不到相应的lib文件 其中xxx即表示函式库文件名称 如 libc so li
  • STM32 进阶教程 15 - 串口DMA收发

    前言 串口操作相信大家一定很熟悉 如果你已经会串口的收发数据 并可以灵活使用轮询及中断方式对串口进行数据收发 那么恭喜你 学完本节内容后 也将可以学会串口的更高级操作方式 DMA方式 DMA操作串口可以大大减轻MCU的负担 同时也可以加快数
  • C++ stdlib.h

    stdlib 头文件即standard library标准库头文件 stdlib 头文件里包含了C C 语言的最常用的系统函数 该文件包含了的C语言标准库函数的定义 stdlib h里面定义了五种类型 一些宏和通用工具函数 类型例如size
  • 利用Python爬虫,查询12306车次信息

    效果展示 分析目标网站 进入12306官网 以商丘南到汝州为例 在点击查询后会跳转到查询结果的网站 右键点检查或审查元素 在弹出的控制台中点网络或network 如果没有显示数据的话 刷新一下网页就有了 在点击Fetch XHR后会发现有一