用Python轻松抓取12306列车时刻表数据5分钟从入门到实战每次需要查询列车时刻表时你是不是还在手动打开12306官网输入车次、日期然后一页页翻看对于需要批量获取数据的研究者或数据分析师来说这种方式效率实在太低。今天我们就用Python的Requests库教你如何快速抓取12306的列车时刻表数据整个过程不到5分钟就能搞定。1. 准备工作环境配置与接口分析在开始编写代码前我们需要先准备好Python环境和必要的库。打开你的终端或命令行工具执行以下命令安装所需依赖pip install requests pandasRequests库是Python中最流行的HTTP客户端库而Pandas则是数据处理的神器能让我们轻松地将抓取到的JSON数据转换为结构化的DataFrame。通过分析12306的接口我们发现获取列车时刻表数据主要分为两步根据车次查询列车编号通过车次名称如G123和日期获取列车唯一编号根据列车编号查询详细时刻表使用上一步获取的列车编号查询该车次所有经停站的到发时间等信息这两个接口都是GET请求返回JSON格式数据非常适合用Python处理。2. 构建请求模拟浏览器访问12306的接口通常会有一些反爬机制我们需要设置合理的请求头来模拟浏览器访问。以下是构建请求的关键点import requests headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://www.12306.cn/ } def get_train_no(train_code, date): url https://search.12306.cn/search/v1/train/search params { keyword: train_code, date: date # 格式: yyyyMMdd } response requests.get(url, paramsparams, headersheaders) return response.json()注意日期格式在不同接口中可能不同第一个接口使用yyyyMMdd格式而第二个接口使用yyyy-MM-dd格式这是常见的坑点。3. 解析复杂JSON数据从接口获取的数据通常是嵌套的JSON结构我们需要学会如何提取所需信息。让我们看一个完整的示例def get_train_schedule(train_no, date): url https://kyfw.12306.cn/otn/queryTrainInfo/query params { leftTicketDTO.train_no: train_no, leftTicketDTO.train_date: date, # 格式: yyyy-MM-dd rand_code: } response requests.get(url, paramsparams, headersheaders) data response.json() if data.get(status) and data.get(data): stations data[data][data] # 提取关键信息 schedule [] for station in stations: item { 站序: station[station_no], 站名: station[station_name], 到达时间: station[arrive_time], 发车时间: station[start_time], 停留时间: station[stopover_time] if stopover_time in station else --, 历时天数: station[arrive_day_str] } schedule.append(item) return schedule else: raise Exception(f获取时刻表失败: {data.get(messages, 未知错误)})4. 完整工作流与数据保存现在我们把所有步骤整合起来形成一个完整的解决方案import pandas as pd from datetime import datetime def get_12306_schedule(train_code, search_date): # 第一步获取列车编号 search_date_str search_date.strftime(%Y%m%d) train_info get_train_no(train_code, search_date_str) if not train_info.get(status) or not train_info.get(data): raise Exception(f未找到车次信息: {train_info.get(errorMsg, 未知错误)}) train_no train_info[data][0][train_no] # 第二步获取时刻表 schedule_date_str search_date.strftime(%Y-%m-%d) schedule get_train_schedule(train_no, schedule_date_str) # 转换为DataFrame df pd.DataFrame(schedule) df[车次] train_code df[查询日期] schedule_date_str # 保存为CSV filename f{train_code}_{search_date_str}.csv df.to_csv(filename, indexFalse, encodingutf_8_sig) return df # 使用示例 if __name__ __main__: df get_12306_schedule(G123, datetime(2023, 7, 15)) print(df.head())这段代码会输出一个包含列车所有经停站信息的表格并自动保存为CSV文件。输出结果类似这样站序站名到达时间发车时间停留时间历时天数车次查询日期01北京南--08:00--当日到达G1232023-07-1502天津南08:3008:322分钟当日到达G1232023-07-15........................5. 高级技巧与错误处理在实际使用中你可能会遇到各种问题。下面分享几个实用技巧请求重试机制网络请求可能会失败添加重试逻辑提高稳定性from time import sleep def safe_request(url, params, max_retries3): for i in range(max_retries): try: response requests.get(url, paramsparams, headersheaders, timeout10) if response.status_code 200: return response except Exception as e: print(f请求失败重试 {i1}/{max_retries}: {e}) sleep(2) raise Exception(f请求失败已达到最大重试次数 {max_retries})日期验证确保查询日期有效def validate_date(date_str, fmt%Y%m%d): try: date datetime.strptime(date_str, fmt) if date datetime.now(): raise ValueError(查询日期不能早于当前日期) return date except ValueError as e: raise ValueError(f日期格式错误: {e})批量查询如果需要查询多个车次def batch_query(train_codes, date): results [] for code in train_codes: try: df get_12306_schedule(code, date) results.append(df) print(f成功获取 {code} 时刻表) except Exception as e: print(f获取 {code} 时刻表失败: {e}) return pd.concat(results) if results else None6. 数据应用场景获取到的列车时刻表数据可以用于多种分析场景旅行时间分析计算不同车次的全程耗时站点流量统计分析各站的经停车次数量票价与时间关系结合票价数据研究性价比延误分析如果有历史数据可以研究常见延误区间这里有一个简单的分析示例计算车次的总运行时间def calculate_duration(df): first_station df.iloc[0] last_station df.iloc[-1] start_time first_station[发车时间] end_time last_station[到达时间] # 转换为时间对象进行计算 fmt %H:%M start datetime.strptime(start_time, fmt) end datetime.strptime(end_time, fmt) duration end - start # 处理跨天情况 if duration.days 0: duration timedelta(days1) duration return str(duration)7. 法律与道德注意事项在使用这类技术时请务必注意遵守网站的使用条款不要过度频繁请求避免对服务器造成压力合理设置请求间隔建议在请求间添加1-2秒的延迟仅用于合法用途获取的数据不要用于商业用途或违反相关法规尊重数据版权明确数据的版权归属和使用限制在实际项目中我发现最实用的技巧是将这些函数封装成一个类方便复用和管理。比如可以创建一个TrainScheduleFetcher类初始化时设置headers和请求参数然后提供各种查询方法。