前言
每逢节假日,12306购票难一直是大家头疼的问题。虽然官方提供了完整的购票系统,但有时候我们只需要查询余票信息,而不需要登录和购票。本文将带你从零开始,实现一个完整的12306火车票查询爬虫,支持输入出发地、目的地,自动查询并显示所有可用车次的余票信息。
这个项目经历了多个版本的迭代:从最开始写死URL的简单版本,到支持城市名动态输入的优化版,再到使用代理池和反爬虫对抗的完整版。通过本文,你不仅能学会如何爬取12306的数据,还能掌握Python爬虫开发中的多个核心技巧。
技术背景
本项目涉及以下核心技术和库:
- requests:Python最流行的HTTP请求库,用于向12306接口发送查询请求
- json:解析12306返回的JSON格式数据
- prettytable:在终端以表格形式美观地展示车次信息
- 面向对象编程:通过
TrainInfo类封装列车信息,提升代码可维护性 - 反爬虫对抗:随机User-Agent、Cookie模拟、代理IP池
12306的查票接口是 https://kyfw.12306.cn/otn/leftTicket/queryO,这个接口不需要登录即可访问,是学习爬虫的绝佳入门案例。
实现思路
整个项目的架构设计如下:
1 | ┌─────────────────────────────────────────────────┐ |
核心难点在于理解12306返回数据的格式:车次信息被编码成以 | 分隔的长字符串,每个位置对应不同的字段(车次号、出发时间、到达时间、各席别余票等)。
核心代码解析
1. 城市代码映射(city.json)
12306的API使用的不是城市名,而是三字车站代码。我们需要一个映射文件:
1 | { |
2. TrainInfo 类——面向对象封装车次信息
1 | class TrainInfo: |
为什么要用类封装? 直接将12个参数 disperse 在代码里很难维护,用类封装后,新增字段或修改字段顺序时只需修改类定义,解析处的代码无需变动。
3. 解析12306返回的管道符分隔数据
这是整个爬虫最核心的部分。12306返回的 result 是一个字符串列表,每个字符串形如:
1 | "book|240000G1010C|G1|北京南|VNP|上海虹桥|AOH|06:00|13:28|07:28|...(共38+个字段)" |
1 | # 将响应内容解析为JSON格式 |
4. 使用 PrettyTable 美化输出
1 | from prettytable import PrettyTable |
运行后终端输出效果类似:
1 | +------+--------+----------+----------+------+--------+--------+--------+------+------+------+------+------+--------+--------+--------+------------+ |
5. 代理IP池的使用
为了对抗12306的频率限制,项目还实现了代理IP池功能:
1 | import json |
proxies.json 中存储了多个代理IP的信息,包括IP地址、端口、位置、HTTPS支持情况等,爬虫每次请求时随机选择一个代理,有效降低被封禁的风险。
6. 完整优化版代码(支持用户输入)
1 | import json |
运行效果
程序运行后,终端交互过程如下:
1 | 请输入出发的城市:北京 |
总结与优化方向
通过本文的12306爬虫项目,我们学习了:
- HTTP请求与JSON解析:使用requests向API发起GET请求,解析JSON响应
- 数据解析技巧:处理管道符分隔的字符串格式
- 面向对象设计:用类封装数据,提升代码可维护性
- 终端美化:用PrettyTable输出整齐的表格
- 反爬虫基础:随机User-Agent、Cookie携带、代理IP池
可优化方向:
- 增加日期输入:目前日期是写死的,可以让用户输入任意日期
- 增加.filter过滤:按出发时间段、只看高铁、只看有票等条件过滤
- 数据持久化:将查询结果保存为Excel或JSON文件
- 异步请求:使用
aiohttp或httpx提升大量查询时的性能 - 自动抢票:结合12306登录接口,实现自动下单(注意:实际使用需遵守12306服务条款)
- 站点自动补全:输入”北”自动提示”北京”、”北京南”等,提升用户体验
免责声明:本项目仅供学习交流使用,请勿用于商业目的或恶意刷票。使用本代码产生的任何后果由使用者自行承担。