找回密码
 立即注册
搜索
查看: 27|回复: 0

春节抢票不再愁:Python工程师教你用Python编写自动查票工具

[复制链接]

3万

主题

0

回帖

9万

积分

管理员

积分
90907
发表于 2025-2-25 16:36:00 | 显示全部楼层 |阅读模式
[[[]]0730]

春节即将来临,抢票的大战又要拉开帷幕了。身为一名工程师,我在今天要教给你如何编写一个自动查票的工具,以便让你能够摆脱手动刷新所带来的烦恼。

环境准备,一步到位

在开发这个工具之前,我们需要安装几个重要的包。其一用于发送 HTTP 请求,其二用于优雅地展示查询结果。

<p><pre style="margin-top[id_1638773058]20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(0[id_1720966087]0, 0, 0.55[id_70507052] 0px 2px 10px;background: rgb(40, 42, 54);color: rgb(51, 51, 51);letter-spacing: normal;text-align: left;">                                <div style="top: 0px;right: 0px;background: rgb(26, 115, 232);color: white;padding: 4px 12px;border-radius: 0px 8px;font-size: 12px;">python</div><br  />                            <code style="font-family: Consol[id_585665494], Monaco, "Courier New", monospace;font-size: 14px;display: block;line-height: 1.6;color: rgb(248, 248, 242);background: rgb(40, 44, 52);padding: 1em;overflow-x: auto;"><span style="color: rgb(92, 99, 112);font-style: italic;"># 安装必要的包</span><br  />使用 pip 安装 requests 和 prettytable 这两个库。可以通过在命令行中输入 "pip install requests" 来安装 requests 库,输入 "pip install prettytable" 来安装 prettytable 库。<br  /></code><br  />                        </pre></p>
核心功能,说干就干

实现最核心的查票功能。利用请求 12306 的 API,我们能够便捷地获取列车信息。



<p><pre style="margin-top: 20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;background: rgb(40, 42, 54);color: rgb(51, 51, 51);letter-spacing: normal;text-align: left;">                                <div style="top: 0px;right: 0px;background: rgb(26, 115, 232);color: white;padding: 4px 12px;border-radius: 0px 8px;font-size: 12px;">python</div><br  />                            <code style="font-family: Consolas, Monaco, "Courier New", monospace;font-size: 14px;display: block;line-height: 1.6;color: rgb(248, 248, 242);background: rgb(40, 44, 52);padding: 1em;overflow-x: auto;"><span style="color: rgb(198, 120, 221);">import</span> requests<br  /><span style="color: rgb(198, 120, 221);">from</span> datetime <span style="color: rgb(198, 120, 221);">import</span> datetime<br  /><span style="color: rgb(198, 120, 221);">import</span> json<br  /><br  /><span style="color: rgb(198, 120, 221);">def</span> <span style="color: rgb(97, 174, 238);">query_tickets</span>(from_station, to_station, train_date) 分别代表出发站、到达站和列车日期。从出发站到到达站,在列车日期这一天,这些信息共同构成了特定的行程安排。出发站和到达站明确了行程的起点和终点,列车日期则确定了行程的具体时间。它们相互关联,缺一不可,共同为出行提供了关键的信息。<br  />    <span style="color: rgb(92, 99, 112);font-style: italic;"># API地址</span><br  />    url = <span style="color: rgb(152, 195, 121);">f 的链接为 https://kyfw.12306.cn/otn/leftTicket/query</span><br  />    params = {<br  />        <span style="color: rgb(152, 195, 121);">'leftTicketDTO.train_date'</span>: train_date,<br  />        <span style="color: rgb(152, 195, 121);">'leftTicketDTO.from_station'</span>: from_station,<br  />        <span style="color: rgb(152, 195, 121);">'leftTicketDTO.to_station'</span>: to_station,<br  />        <span style="color: rgb(152, 195, 121);">'purpose_codes'</span>: <span style="color: rgb(152, 195, 121);">'ADULT'</span><br  />    }<br  />    <br  />使用 requests 库发送一个 GET 请求,请求的地址是 url,并且携带了 params 作为请求参数。<br  />    <span style="color: rgb(198, 120, 221);">return</span> response.json()<br  /></code><br  />                        </pre></p>
数据处理,美化展示

获取到数据之后,我们要将其整理成便于阅读的格式。通过以下方式来达成表格化展示:

<p><pre style="margin-top: 20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;background: rgb(40, 42, 54);color: rgb(51, 51, 51);letter-spacing: normal;text-align: left;">                                <div style="top: 0px;right: 0px;background: rgb(26, 115, 232);color: white;padding: 4px 12px;border-radius: 0px 8px;font-size: 12px;">python</div><br  />                            <code style="font-family: Consolas, Monaco, "Courier New", monospace;font-size: 14px;display: block;line-height: 1.6;color: rgb(248, 248, 242);background: rgb(40, 44, 52);padding: 1em;overflow-x: auto;"><span style="color: rgb(198, 120, 221);">from</span> prettytable <span style="color: rgb(198, 120, 221);">import</span> PrettyTable<br  /><br  /><span style="color: rgb(198, 120, 221);">def</span> <span style="color: rgb(97, 174, 238);">format_ticket_data</span>(ticket_data):<br  />    <span style="color: rgb(92, 99, 112);font-style: italic;"># 创建表格对象</span><br  />    table = PrettyTable()<br  />    table.field_names = [<span style="color: rgb(152, 195, 121);">'车次'</span>, <span style="color: rgb(152, 195, 121);">'出发时间'</span>, <span style="color: rgb(152, 195, 121);">'到达时间'</span>, <span style="color: rgb(152, 195, 121);">'二等座'</span>, <span style="color: rgb(152, 195, 121);">'一等座'</span>, <span style="color: rgb(152, 195, 121);">'商务座'</span>]<br  />    <br  />    <span style="color: rgb(92, 99, 112);font-style: italic;"># 添加数据行</span><br  />    <span style="color: rgb(198, 120, 221);">for</span> train <span style="color: rgb(198, 120, 221);">in</span> ticket_data[<span style="color: rgb(152, 195, 121);">'data'</span>][<span style="color: rgb(152, 195, 121);">'result'</span>]:<br  />        info = train.split(<span style="color: rgb(152, 195, 121);">'|'</span>)<br  />        table.add_row([<br  />            info[<span style="color: rgb(209, 154, 102);">3</span>],  <span style="color: rgb(92, 99, 112);font-style: italic;"># 车次</span><br  />            info[<span style="color: rgb(209, 154, 102);">8</span>],  <span style="color: rgb(92, 99, 112);font-style: italic;"># 出发时间</span><br  />            info[<span style="color: rgb(209, 154, 102);">9</span>],  <span style="color: rgb(92, 99, 112);font-style: italic;"># 到达时间</span><br  />            info[<span style="color: rgb(209, 154, 102);">30</span>] <span style="color: rgb(198, 120, 221);">or</span> <span style="color: rgb(152, 195, 121);">'--'</span>,  <span style="color: rgb(92, 99, 112);font-style: italic;"># 二等座</span><br  />            info[<span style="color: rgb(209, 154, 102);">31</span>] <span style="color: rgb(198, 120, 221);">or</span> <span style="color: rgb(152, 195, 121);">'--'</span>,  <span style="color: rgb(92, 99, 112);font-style: italic;"># 一等座</span><br  />            info[<span style="color: rgb(209, 154, 102);">32</span>] <span style="color: rgb(198, 120, 221);">or</span> <span style="color: rgb(152, 195, 121);">'--'</span>   <span style="color: rgb(92, 99, 112);font-style: italic;"># 商务座</span><br  />        ])<br  />    <span style="color: rgb(198, 120, 221);">return</span> table<br  /></code><br  />                        </pre></p>
自动刷新,持续监控

为了实现自动刷新功能,我们可以加入定时任务:

<p><pre style="margin-top: 20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;background: rgb(40, 42, 54);color: rgb(51, 51, 51);letter-spacing: normal;text-align: left;">                                <div style="top: 0px;right: 0px;background: rgb(26, 115, 232);color: white;padding: 4px 12px;border-radius: 0px 8px;font-size: 12px;">python</div><br  />                            <code style="font-family: Consolas, Monaco, "Courier New", monospace;font-size: 14px;display: block;line-height: 1.6;color: rgb(248, 248, 242);background: rgb(40, 44, 52);padding: 1em;overflow-x: auto;"><span style="color: rgb(198, 120, 221);">import</span> time<br  /><span style="color: rgb(198, 120, 221);">from</span> datetime <span style="color: rgb(198, 120, 221);">import</span> datetime<br  /><br  /><span style="color: rgb(198, 120, 221);">def</span> <span style="color: rgb(97, 174, 238);">monitor_tickets</span>从起始站出发,到达终点站,在火车日期这一天,时间间隔为……<span style="color: rgb(209, 154, 102);">60</span>):<br  />    <span style="color: rgb(230, 192, 123);">print</span>(<span style="color: rgb(152, 195, 121);">f'开始监控<span style="color: rgb(224, 108, 117);">{from_station}</span>到<span style="color: rgb(224, 108, 117);">{to_station}</span>的车票...'</span>)<br  />    <span style="color: rgb(198, 120, 221);">while</span> <span style="color: rgb(86, 182, 194);">True</span>:<br  />        <span style="color: rgb(198, 120, 221);">try</span>:<br  />调用 query_tickets 函数,传入起始站 from_station、终点站 to_station 和日期 train_date 来获取结果。<br  />使用 result 对表格进行格式化操作,得到的结果即为 table 。<br  />            <span style="color: rgb(230, 192, 123);">print</span>(<span style="color: rgb(152, 195, 121);">f'\n最新查询时间:<span style="color: rgb(224, 108, 117);">{datetime.now().strftime(<span style="color: rgb(152, 195, 121);">"%Y-%m-%d %H:%M:%S"</span>)}</span>'</span>)<br  />            <span style="color: rgb(230, 192, 123);">print</span>(table)<br  />            time.sleep(interval)  <span style="color: rgb(92, 99, 112);font-style: italic;"># 间隔时间</span><br  />        <span style="color: rgb(198, 120, 221);">except</span> Exception <span style="color: rgb(198, 120, 221);">as</span> e:<br  />            <span style="color: rgb(230, 192, 123);">print</span>(<span style="color: rgb(152, 195, 121);">f'查询出错:<span style="color: rgb(224, 108, 117);">{<span style="color: rgb(230, 192, 123);">str</span>(e)}</span>'</span>)<br  />            time.sleep(<span style="color: rgb(209, 154, 102);">5</span>)  <span style="color: rgb(92, 99, 112);font-style: italic;"># 出错后短暂等待</span><br  /></code><br  />                        </pre></p>
站点编码,一查就有



12306 系统采用特定编码来表示车站,我们将常用的车站编码存放在字典中。

<p><pre style="margin-top: 20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;background: rgb(40, 42, 54);color: rgb(51, 51, 51);letter-spacing: normal;text-align: left;">                                <div style="top: 0px;right: 0px;background: rgb(26, 115, 232);color: white;padding: 4px 12px;border-radius: 0px 8px;font-size: 12px;">python</div><br  />                            <code style="font-family: Consolas, Monaco, "Courier New", monospace;font-size: 14px;display: block;line-height: 1.6;color: rgb(248, 248, 242);background: rgb(40, 44, 52);padding: 1em;overflow-x: auto;">STATION_DICT = {<br  />    <span style="color: rgb(152, 195, 121);">'北京'</span>: <span style="color: rgb(152, 195, 121);">'BJP'</span>,<br  />    <span style="color: rgb(152, 195, 121);">'上海'</span>: <span style="color: rgb(152, 195, 121);">'SHH'</span>,<br  />    <span style="color: rgb(152, 195, 121);">'广州'</span>: <span style="color: rgb(152, 195, 121);">'GZQ'</span>,<br  />    <span style="color: rgb(152, 195, 121);">'深圳'</span>: <span style="color: rgb(152, 195, 121);">'SZQ'</span>,<br  />    <span style="color: rgb(152, 195, 121);">'杭州'</span>: <span style="color: rgb(152, 195, 121);">'HZH'</span>,<br  />    <span style="color: rgb(92, 99, 112);font-style: italic;"># 更多站点编码...</span><br  />}<br  /></code><br  />                        </pre></p>
哈,写完核心功能,我们来测试一下:

<p><pre style="margin-top: 20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;background: rgb(40, 42, 54);color: rgb(51, 51, 51);letter-spacing: normal;text-align: left;">                                <div style="top: 0px;right: 0px;background: rgb(26, 115, 232);color: white;padding: 4px 12px;border-radius: 0px 8px;font-size: 12px;">python</div><br  />                            <code style="font-family: Consolas, Monaco, "Courier New", monospace;font-size: 14px;display: block;line-height: 1.6;color: rgb(248, 248, 242);background: rgb(40, 44, 52);padding: 1em;overflow-x: auto;"><span style="color: rgb(198, 120, 221);">if</span> __name__ == <span style="color: rgb(152, 195, 121);">'__main__'</span>:<br  />    <span style="color: rgb(92, 99, 112);font-style: italic;"># 测试查询北京到上海的车票</span><br  />    monitor_tickets(<br  />        STATION_DICT[<span style="color: rgb(152, 195, 121);">'北京'</span>],<br  />        STATION_DICT[<span style="color: rgb(152, 195, 121);">'上海'</span>],<br  />        <span style="color: rgb(152, 195, 121);">'2024-02-01'</span>,<br  />        interval=<span style="color: rgb(209, 154, 102);">30</span>  <span style="color: rgb(92, 99, 112);font-style: italic;"># 30秒刷新一次</span><br  />    )<br  /></code><br  />                        </pre></p>
今天给大家分享这个工具,这个灵感源自于我去年抢票时所经历的痛苦。代码较为简单,然而其实用性却很强。倘若有任何新奇的想法,随时可以在评论区告知我。另外,最近我正在研究如何利用 AI 来预测余票的趋势,等研究出来之后我们再继续交流!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|【宏智网络】 ( 京ICP备20013102号 )

GMT+8, 2025-6-24 09:14 , Processed in 0.104983 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表