用 WorkBuddy 自动分析 12306 高铁余票:从数据抓取到报告生成

6 阅读

要解决的实际问题

节假日或出差时,很多人会频繁查询 12306 高铁票。但官方 APP 或网页只能一页页翻看车次,无法一次性获取整条线路的所有车次信息。如果想做余票统计、时段分析或筛选最优车次,手动复制粘贴几十上百趟车次到 Excel 再画图,效率极低。

文章配图

我们真正需要回答的问题包括:

文章配图

  • 这条线路一天到底有多少趟 G 字头高铁?运力在各时段如何分布?
  • 二等座整体紧张吗?哪些时间段的车次最容易没票?
  • 最快的标杆车有哪些?它们的余票情况如何?
  • 能不能直接输出一份带表格和图表的正式报告,方便存档或分享?

文章配图

手动处理这些问题,半天时间就没了。而借助 WorkBuddy AI 工作台,可以把整个流程自动化:数据获取 → 清洗整理 → 可视化 → 文档生成。

文章配图

工具环境说明

文章配图

本次操作完全在 WorkBuddy 内部完成,无需在本地安装 Python 或其他依赖。平台提供隔离的运行环境,开箱即用。核心组件包括:

文章配图

  • WorkBuddy AI 工作台:支持技能调用、文件读写和脚本执行
  • 内置 12306 技能:封装官方查询接口,可指定日期、车站和车次类型,返回结构化 JSON 数据
  • Python 3.13 + openpyxl:将 JSON 数据导出为格式化的 Excel 表格
  • python-docx + matplotlib:生成带图表和目录的 Word 分析报告

文章配图

所有脚本都在平台沙盒中运行,这是该方案最大的便利之处——零环境配置。

文章配图

获取原始车次数据

文章配图

首先,在 WorkBuddy 中安装 12306 技能(免费)。然后输入以下指令:

文章配图

@skill:12306 查询明天上海虹桥到南京南的高铁时刻和二等座余票。

文章配图

系统会自动调用接口,返回结构化 JSON 数据。测试时间为 2026 年 8 月 24 日晚间,目标日期为次日(8 月 25 日),仅统计 G 字头高速动车组。

文章配图

返回的部分车次示例如下:

文章配图

车次 出发→到达 耗时 二等座
G2 06:43→07:58 1h15m
G4 07:00→08:11 1h11m 11 张
G94 08:55→09:54 59m 1 张
G100 10:04→11:03 59m

文章配图

WorkBuddy 会将结果以 HTML 表格形式展示,点击即可查看完整列表。

文章配图

数据统计与业务洞察

文章配图

基于返回的 JSON 数据,我们先做整体统计:

文章配图

  • G 字头总车次:261 趟
  • 二等座有票:236 趟(约 90.4%)
  • 二等座无票:25 趟(约 9.6%)
  • 首末班车:05:45 / 22:05
  • 最短运行时间:59 分钟
  • 显示具体余票张数的车次:13 趟

可以看出,上海虹桥至南京南是典型的公交化运营线路,平均不到 4 分钟就有一班车,整体运力非常充足。无票车次主要集中在早高峰和运行时间最短的标杆快车(59–70 分钟),这些车因耗时短、停站少而抢手。

时段分布分析

将车次按出发小时分组后发现,全天各时段均有稳定投放,二等座无票车次呈零散分布,并未出现某个大时间段整体无票的情况。这意味着只要出行时间稍灵活,避开热门标杆车,很容易买到票。

运行时长分布

绝大多数车次耗时在 1–2 小时之间。最快标杆车仅需 59 分钟,但也有个别过路车接近 6 小时。其中,运行时间 ≤70 分钟的标杆快车共 26 趟,这类车抢票压力最大。如果抢不到,选择 1–1.5 小时区间的车次,票源会宽裕很多。

生成 Excel 明细表

接下来,我们将原始数据转为格式化的 Excel 文件。使用以下 Python 脚本(已在 WorkBuddy 中运行):

import json
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter

SRC = r"C:/Users/dbrg/WorkBuddy/2026-08-24-23-38-02/query.json"
OUT = r"C:/Users/dbrg/WorkBuddy/2026-08-24-23-38-02/上海虹桥-南京南-2026-08-25.xlsx"

with open(SRC, encoding="utf-8") as f:
    data = json.load(f)

HEADERS = ["车次", "出发站", "到达站", "出发时间", "到达时间", "历时",
           "商务/特等", "一等座", "二等座", "无座", "状态"]

def fmt_dur(d):
    if not d or d == "--":
        return d
    h, m = d.split(":")
    return f"{int(h)}h{int(m):02d}m"

def fmt_status(v):
    return "可购" if v == "Y" else "不可购"

wb = Workbook()
ws = wb.active
ws.title = "高铁时刻表"

title_fill = PatternFill("solid", fgColor="1F4E78")
title_font = Font(bold=True, color="FFFFFF", size=11)
center = Alignment(horizontal="center", vertical="center")
thin = Side(style="thin", color="BFBFBF")
border = Border(left=thin, right=thin, top=thin, bottom=thin)

for c, h in enumerate(HEADERS, 1):
    cell = ws.cell(row=1, column=c, value=h)
    cell.fill = title_fill
    cell.font = title_font
    cell.alignment = center
    cell.border = border

green = PatternFill("solid", fgColor="C6EFCE")
red = PatternFill("solid", fgColor="FFC7CE")
green_font = Font(color="006100")
red_font = Font(color="9C0006")
ze_col = HEADERS.index("二等座") + 1
status_col = HEADERS.index("状态") + 1

r = 2
for t in data:
    row = [
        t.get("trainCode", ""),
        t.get("fromStation", ""),
        t.get("toStation", ""),
        t.get("departTime", ""),
        t.get("arriveTime", ""),
        fmt_dur(t.get("duration", "")),
        t.get("swz", "--"),
        t.get("zy", "--"),
        t.get("ze", "--"),
        t.get("wz", "--"),
        fmt_status(t.get("canBuy", "")),
    ]
    for c, v in enumerate(row, 1):
        cell = ws.cell(row=r, column=c, value=v)
        cell.alignment = center
        cell.border = border
    ze_val = row[ze_col - 1]
    zc = ws.cell(row=r, column=ze_col)
    if ze_val == "无":
        zc.fill = red
        zc.font = red_font
    elif ze_val not in ("--", ""):
        zc.fill = green
        zc.font = green_font
    sc = ws.cell(row=r, column=status_col)
    if row[status_col - 1] == "可购":
        sc.fill = green
        sc.font = green_font
    else:
        sc.fill = red
        sc.font = red_font
    r += 1

widths = [9, 11, 11, 10, 10, 9, 11, 9, 9, 8, 9]
for i, w in enumerate(widths, 1):
    ws.column_dimensions[get_column_letter(i)].width = w

ws.freeze_panes = "A2"
ws.auto_filter.ref = f"A1:{get_column_letter(len(HEADERS))}{r-1}"

wb.save(OUT)
print(f"已生成: {OUT}")

生成的 Excel 表格包含所有车次明细,二等座“有票”标绿,“无票”标红,并启用了自动筛选和首行冻结,方便后续查看。

生成完整 Word 分析报告

最后一步是将分析结果整合成正式文档。在 WorkBuddy 中调用 docx 技能,输入提示词:

基于docx技能基于excel数据对明日火车票余票情况进行一个全面的解读,形成一份完整的票务说明文档。

系统会运行以下脚本(节选关键逻辑):

  • 统计总车次、有票/无票比例
  • 按出发小时分组计算时段分布
  • 按运行时长分桶(<1h, 1–1.5h 等)
  • 提取标杆车次(≤70 分钟)和推荐车次(≤80 分钟且有票)
  • 使用 matplotlib 绘制饼图和柱状图
  • 通过 python-docx 构建带封面、目录、章节和附录的 Word 文档

最终生成的报告包含:

  • 封面与目录:自动生成,支持 Word 更新域
  • 总体概述:车次总数、余票覆盖率、首末班时间
  • 开行特征:高密度公交化、历时跨度、席别结构
  • 余票态势:有票/无票占比饼图 + 具体余票车次表
  • 时段分析:各小时车次数量与余票柱状图
  • 运行时长分布:车次数量饼图 + 标杆车次余票表
  • 推荐车次:运行快且有票的前 15 趟车
  • 购票建议:如尽早锁定标杆车、错峰出行、关注具体余数等
  • 数据说明:强调数据为动态值,仅供参考
  • 附录:全部 261 趟车次的完整明细表

这份文档可直接用于内部参考或存档,省去了手动整理的繁琐过程。

流程复盘与适用场景

整套自动化流水线为:

12306 技能查询 → JSON 原始数据 → openpyxl 输出 Excel → python-docx 生成 Word 报告

相比手动操作,优势明显:

  1. 数据完整:一次性获取全天所有车次,避免遗漏
  2. 量化分析:所有结论基于真实数据,非主观判断
  3. 产出专业:直接生成带图表的正式文档
  4. 高度复用:只需修改出发站、到达站和日期,即可用于其他线路

这套模式不仅适用于高铁票务,还可扩展到其他场景,比如交通流量统计、公开 API 数据采集、定期报告自动生成等。只要数据源可通过技能或脚本访问,就能套用“拉取-处理-可视化-输出”的通用框架。

数据说明与免责

  • 数据采集于 2026 年 8 月 24 日晚间,12306 余票为实时动态值,出行当日可能变化
  • “有”表示余票充足但未显示具体张数;数字为真实剩余票数;“无”表示当前无票
  • 仅统计 G 字头高速动车组,不含 D 字头或其他列车
  • 本方案仅为技术演示,购票请以 12306 官方信息为准