TrainQuery.API 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package TrainQuery.API --version 1.0.0
                    
NuGet\Install-Package TrainQuery.API -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TrainQuery.API" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TrainQuery.API" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="TrainQuery.API" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TrainQuery.API --version 1.0.0
                    
#r "nuget: TrainQuery.API, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TrainQuery.API@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TrainQuery.API&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=TrainQuery.API&version=1.0.0
                    
Install as a Cake Tool

TrainQuery.API - 12306火车票查询类库

📦 NuGet包信息

  • 包名称: TrainQuery.API
  • 当前版本: 1.0.0
  • 作者: 孟煌喆
  • 目标框架: .NET Framework 4.8.1
  • 依赖项: Newtonsoft.Json (≥13.0.3)

🚀 快速开始

安装包

# Package Manager Console
Install-Package TrainQuery.API

# .NET CLI
dotnet add package TrainQuery.API

# PackageReference
<PackageReference Include="TrainQuery.API" Version="1.0.0" />

基础使用示例

using TrainQuery.API;
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        using (var service = new TrainQueryService())
        {
            // 订阅调试信息
            service.DebugMessage += (sender, message) => 
                Console.WriteLine($"[DEBUG] {message}");
            
            // 1. 初始化车站数据(必须首先调用)
            Console.WriteLine("正在初始化车站数据...");
            bool initSuccess = await service.InitializeStationsAsync();
            
            if (!initSuccess)
            {
                Console.WriteLine("车站数据初始化失败");
                return;
            }
            
            // 2. 查询车票
            Console.WriteLine("正在查询车票...");
            var tickets = await service.QueryDirectTicketsAsync(
                fromStation: "北京",
                toStation: "上海",
                date: DateTime.Now.AddDays(1).ToString("yyyy-MM-dd")
            );
            
            // 3. 显示结果
            Console.WriteLine($"找到 {tickets.Count} 个车次");
            foreach (var ticket in tickets.Take(5))
            {
                Console.WriteLine($"\n车次: {ticket.TrainNo}");
                Console.WriteLine($"  出发: {ticket.FromStation} ({ticket.DepartureTime})");
                Console.WriteLine($"  到达: {ticket.ToStation} ({ticket.ArrivalTime})");
                Console.WriteLine($"  历时: {ticket.Duration}");
                Console.WriteLine($"  状态: {ticket.Status}");
            }
        }
    }
}

📚 详细使用说明

1. 初始化服务

// 创建服务实例(推荐使用using语句确保资源释放)
using var service = new TrainQueryService();

// 可选:订阅调试信息
service.DebugMessage += (sender, message) => 
    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {message}");

// 可选:订阅车站加载完成事件
service.StationsLoaded += (sender, count) => 
    Console.WriteLine($"已加载 {count} 个车站");

2. 车站数据初始化

重要:必须首先调用此方法!

// 初始化车站数据
bool success = await service.InitializeStationsAsync();

if (!success)
{
    // 初始化失败,可能是网络问题
    Console.WriteLine("车站数据初始化失败,请检查网络连接");
    return;
}

// 成功后可进行查询操作

3. 查询直达车票

var tickets = await service.QueryDirectTicketsAsync(
    fromStation: "北京",           // 出发站(中文站名)
    toStation: "上海",             // 到达站(中文站名)
    date: "2024-01-20",           // 日期格式:yyyy-MM-dd
    trainTypes: new List<string> { "G", "D" }  // 可选:过滤车次类型
);

4. 查询中转换乘方案

var transferSolutions = await service.QueryTransferSolutionsAsync(
    fromStation: "北京",
    toStation: "广州",
    date: "2024-01-20"
);

foreach (var solution in transferSolutions)
{
    Console.WriteLine($"中转方案: {solution.MiddleStationName}");
    Console.WriteLine($"  总历时: {solution.AllDuration}");
    Console.WriteLine($"  等待时间: {solution.WaitTime}");
    
    foreach (var segment in solution.Segments)
    {
        Console.WriteLine($"    车次: {segment.StationTrainCode}");
        Console.WriteLine($"      {segment.FromStationName} → {segment.ToStationName}");
    }
}

5. 查询票价信息

var priceInfo = await service.QueryTicketPriceAsync(
    trainNo: "G1",                // 车次
    fromStation: "北京南",         // 出发站
    toStation: "上海虹桥",         // 到达站
    date: "2024-01-20"            // 日期
);

Console.WriteLine($"车次 {priceInfo.TrainNo} 票价:");
foreach (var price in priceInfo.Prices)
{
    Console.WriteLine($"  {price.Key}: {price.Value:N1}元");
}

6. 查询列车经停站

var stopStations = await service.QueryTrainStopStationsAsync(
    trainNo: "G1",                // 车次
    fromStation: "北京南",         // 出发站
    toStation: "上海虹桥",         // 到达站
    date: "2024-01-20"            // 日期
);

Console.WriteLine($"车次 G1 经停站 ({stopStations.Count} 个):");
foreach (var station in stopStations)
{
    string stationName = station.TryGetValue("station_name", out var name) ? name : "";
    string arriveTime = station.TryGetValue("arrive_time", out var arrive) ? arrive : "--";
    string startTime = station.TryGetValue("start_time", out var start) ? start : "--";
    
    Console.WriteLine($"  {stationName}: 到达 {arriveTime}, 出发 {startTime}");
}

🎯 进阶用法

Windows Forms 应用示例

using TrainQuery.API;
using System;
using System.Windows.Forms;

public partial class MainForm : Form
{
    private TrainQueryService _service;
    
    public MainForm()
    {
        InitializeComponent();
        _service = new TrainQueryService();
        
        // 订阅调试消息
        _service.DebugMessage += (sender, message) =>
            txtDebug.Invoke(new Action(() => 
                txtDebug.AppendText(message + Environment.NewLine)));
    }
    
    private async void btnQuery_Click(object sender, EventArgs e)
    {
        try
        {
            btnQuery.Enabled = false;
            lblStatus.Text = "查询中...";
            
            // 初始化车站数据
            if (!await _service.InitializeStationsAsync())
            {
                MessageBox.Show("车站数据初始化失败");
                return;
            }
            
            // 查询车票
            var tickets = await _service.QueryDirectTicketsAsync(
                txtFrom.Text,
                txtTo.Text,
                dtpDate.Value.ToString("yyyy-MM-dd")
            );
            
            // 显示结果
            dgvResults.Rows.Clear();
            foreach (var ticket in tickets)
            {
                dgvResults.Rows.Add(
                    ticket.TrainNo,
                    ticket.FromStation,
                    ticket.ToStation,
                    ticket.DepartureTime,
                    ticket.ArrivalTime,
                    ticket.Duration,
                    ticket.Status
                );
            }
            
            lblStatus.Text = $"找到 {tickets.Count} 个车次";
        }
        catch (Exception ex)
        {
            MessageBox.Show($"查询失败: {ex.Message}", "错误", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        finally
        {
            btnQuery.Enabled = true;
        }
    }
    
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _service?.Dispose();
        base.OnFormClosing(e);
    }
}

ASP.NET Web Forms 示例

using TrainQuery.API;
using System;
using System.Threading.Tasks;
using System.Web.UI;

public partial class QueryPage : Page
{
    private TrainQueryService GetService()
    {
        // 使用Session存储服务实例
        if (Session["TrainQueryService"] == null)
        {
            var service = new TrainQueryService();
            Session["TrainQueryService"] = service;
        }
        return (TrainQueryService)Session["TrainQueryService"];
    }
    
    protected async void btnQuery_Click(object sender, EventArgs e)
    {
        try
        {
            lblMessage.Text = "查询中...";
            
            var service = GetService();
            
            // 异步初始化车站数据
            if (!await service.InitializeStationsAsync())
            {
                lblMessage.Text = "车站数据初始化失败";
                return;
            }
            
            // 查询车票
            var tickets = await service.QueryDirectTicketsAsync(
                txtFrom.Text,
                txtTo.Text,
                txtDate.Text
            );
            
            // 绑定到GridView
            gvTickets.DataSource = tickets;
            gvTickets.DataBind();
            
            lblMessage.Text = $"找到 {tickets.Count} 个车次";
        }
        catch (Exception ex)
        {
            lblMessage.Text = $"查询失败: {ex.Message}";
        }
    }
}

⚠️ 重要注意事项

1. 初始化要求

必须首先调用 InitializeStationsAsync() 方法:

// 正确用法
using var service = new TrainQueryService();
await service.InitializeStationsAsync();  // 必须先调用
var tickets = await service.QueryDirectTicketsAsync(...);

// 错误用法(会抛出异常)
using var service = new TrainQueryService();
var tickets = await service.QueryDirectTicketsAsync(...);  // 错误!未初始化

2. 异常处理

try
{
    using var service = new TrainQueryService();
    
    // 可能抛出异常的调用
    var tickets = await service.QueryDirectTicketsAsync(...);
}
catch (ArgumentException ex)
{
    // 参数错误,如车站不存在
    Console.WriteLine($"参数错误: {ex.Message}");
}
catch (InvalidOperationException ex)
{
    // 操作顺序错误,如未初始化
    Console.WriteLine($"操作错误: {ex.Message}");
}
catch (HttpRequestException ex)
{
    // 网络错误
    Console.WriteLine($"网络错误: {ex.Message}");
}
catch (Exception ex)
{
    // 其他错误
    Console.WriteLine($"未知错误: {ex.Message}");
}

3. 线程安全

  • 服务实例不是线程安全的
  • 建议每个线程或请求创建独立的实例
  • 避免在多线程中共享同一个实例

4. 资源管理

// 推荐:使用using语句确保资源释放
using (var service = new TrainQueryService())
{
    // 使用服务...
}

// 或者手动调用Dispose()
var service = new TrainQueryService();
try
{
    // 使用服务...
}
finally
{
    service.Dispose();
}

🔧 配置和优化

1. 网络超时设置

类库内置了网络超时设置(默认30秒),但12306服务器响应时间可能变化:

  • 正常情况:3-10秒
  • 高峰期:可能超过15秒

2. 调试信息

// 启用调试信息输出
service.DebugMessage += (sender, message) => 
{
    Console.WriteLine($"[DEBUG] {message}");
    
    // 记录到文件
    File.AppendAllText("train_query.log", 
        $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}{Environment.NewLine}");
};

3. 性能优化建议

// 1. 重用服务实例(在同一线程中)
using var service = new TrainQueryService();
await service.InitializeStationsAsync();

// 多次查询使用同一个实例
var tickets1 = await service.QueryDirectTicketsAsync(...);
var tickets2 = await service.QueryDirectTicketsAsync(...);

// 2. 并行查询多个日期(谨慎使用,避免被限制)
var tasks = new List<Task<List<TicketInfo>>>();
for (int i = 0; i < 3; i++)
{
    var date = DateTime.Now.AddDays(i).ToString("yyyy-MM-dd");
    tasks.Add(service.QueryDirectTicketsAsync("北京", "上海", date));
}

var results = await Task.WhenAll(tasks);

🐛 常见问题解决

Q1: 出现"找不到车站代码"错误

原因: 车站名称不正确或车站数据未加载 解决:

// 1. 确保已调用InitializeStationsAsync()
// 2. 使用正确的中文站名(如"北京"不是"北京市")
// 3. 常见站名示例:
//    - "北京", "上海", "广州", "深圳"
//    - "北京南", "上海虹桥", "广州南"

Q2: 返回HTML而不是JSON数据

原因: 12306反爬机制触发 解决:

// 1. 等待一段时间后重试
await Task.Delay(2000);

// 2. 检查网络环境,避免使用VPN或代理

// 3. 确保使用最新版本

Q3: 查询速度慢

原因: 网络延迟或12306服务器繁忙 解决:

// 1. 使用异步编程避免阻塞UI线程
// 2. 添加进度提示
// 3. 考虑缓存查询结果

Q4: Newtonsoft.Json版本冲突

原因: 项目中存在多个Newtonsoft.Json版本 解决:


<dependentAssembly>
    <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>

📋 数据模型说明

TicketInfo(车票信息)

public class TicketInfo
{
    public string TrainNo { get; set; }            // 车次
    public string FromStation { get; set; }        // 出发站
    public string ToStation { get; set; }          // 到达站
    public string DepartureTime { get; set; }      // 出发时间
    public string ArrivalTime { get; set; }        // 到达时间
    public string Duration { get; set; }           // 历时
    public string Status { get; set; }             // 状态(有票/无票)
    
    // 座位余量信息
    public Dictionary<string, string> SeatAvailability { get; set; }
}

TransferSolution(中转换乘方案)

public class TransferSolution
{
    public string AllDuration { get; set; }        // 总历时
    public string MiddleStationName { get; set; }  // 中转站
    public string WaitTime { get; set; }           // 等待时间
    public List<TransferSegment> Segments { get; set; } // 行程分段
    public Dictionary<string, decimal> TotalPrices { get; set; } // 总票价组合
}

PriceInfo(票价信息)

public class PriceInfo
{
    public string TrainNo { get; set; }                     // 车次
    public Dictionary<string, decimal> Prices { get; set; } // 座位类型 -> 价格
}

🔄 版本历史

版本 1.0.0 (2024-01-19)

  • ✅ 车票查询(直达车次)
  • ✅ 中转换乘方案查询
  • ✅ 票价查询
  • ✅ 列车经停站查询
  • ✅ 车站数据自动加载
  • ✅ 详细的错误处理和调试信息
  • ✅ 完整的.NET Framework 4.8.1支持
  • ✅ Newtonsoft.Json 13.0.3+ 依赖

📞 技术支持

  • NuGet包页面: TrainQuery.API
  • 问题反馈: 通过NuGet包页面留言
  • 注意事项: 本库基于12306官方API实现,使用前请确保遵守相关服务条款

🚂 关于 12306 接口的说明

本项目在实现车票查询、余票监控等功能时,所调用的火车票数据接口,来源于中国铁路12306官方网站(kyfw.12306.cn)及其相关服务域名。

⚠️ 重要须知与风险提示

请注意以下几点:

  1. 非官方合作接口:本项目使用的接口为12306网站为自身前端功能所提供的服务,并非12306官方对外公开的合作或商业API。因此,不存在官方的《API使用规定》或服务条款
  2. 接口不确定性:这些接口的结构、参数、返回数据格式可能在不经通知的情况下发生变更,导致项目功能暂时失效[citation:10]。
  3. 访问限制风险:12306系统对高频、非正常访问行为(如自动化脚本频繁查询)设有严格的防护机制,包括但不限于IP封禁、请求限流(返回403/429等状态码)等[citation:5]。
  4. 法律与合规风险:任何未经授权的自动化访问、大规模抓取数据、或干扰系统正常运行的行为,都可能违反《网络安全法》等相关法律法规及12306网站自身的用户协议[citation:5]。
  5. 项目目的:本项目仅限于技术学习与研究交流,旨在理解大型并发系统的设计(如缓存策略[citation:1]、分布式锁[citation:1]等),严禁用于任何商业盈利、大规模抢票、干扰12306系统正常运行或其他非法用途

📖 技术参考与学习价值

项目中关于高并发、缓存、分布式锁等技术的实现(如责任链模式验证[citation:1]、分布式锁处理[citation:1]、令牌限流[citation:1]等),参考了业界对12306这类高流量系统的技术分析和开源学习项目。这些技术实现本身具有很高的学习价值,与实际使用的数据接口是解耦的。

📄 许可证

MIT License - 详见NuGet包中的LICENSE文件


开始使用: Install-Package TrainQuery.API

祝你使用愉快!如有问题,请查看常见问题部分或通过NuGet页面反馈。

Product Compatible and additional computed target framework versions.
.NET Framework net481 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 98 1/20/2026
1.0.0 98 1/19/2026

初始版本功能:
     1. 车票查询(直达)
     2. 中转换乘方案
     3. 票价查询
     4. 经停站信息
     5. 车站数据自动加载