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

2023-10-18 12:59

本文主要是介绍利用Python爬虫,查询12306车次信息,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

效果展示:

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

分析目标网站:

  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 jsondef 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个为字典keyresult_x = list_data[1:len(list_data):5]# 从下标'2'开始, 每间隔5个为字典valueresult_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_dataclass 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_listdef 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_msgdef 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")

这篇关于利用Python爬虫,查询12306车次信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/232723

相关文章

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python正则表达式匹配和替换的操作指南

《Python正则表达式匹配和替换的操作指南》正则表达式是处理文本的强大工具,Python通过re模块提供了完整的正则表达式功能,本文将通过代码示例详细介绍Python中的正则匹配和替换操作,需要的朋... 目录基础语法导入re模块基本元字符常用匹配方法1. re.match() - 从字符串开头匹配2.

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

通过Docker容器部署Python环境的全流程

《通过Docker容器部署Python环境的全流程》在现代化开发流程中,Docker因其轻量化、环境隔离和跨平台一致性的特性,已成为部署Python应用的标准工具,本文将详细演示如何通过Docker容... 目录引言一、docker与python的协同优势二、核心步骤详解三、进阶配置技巧四、生产环境最佳实践

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、