am928 发表于 2025-2-25 16:36:00

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

[[[]]0730]

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

环境准备,一步到位

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

<p><pre style="margin-top20px;margin-bottom: 20px;border-radius: 8px;padding: 16px;box-shadow: rgba(00, 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: Consol, 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,我们能够便捷地获取列车信息。

https://img2.baidu.com/it/u=3180760774,1768697059&fm=253&fmt=JPEG&app=138&f=JPEG?w=800&h=1067

<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>
站点编码,一查就有

https://img2.baidu.com/it/u=1723080194,170535472&fm=253&fmt=JPEG&app=138&f=JPEG?w=500&h=707

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 来预测余票的趋势,等研究出来之后我们再继续交流!
页: [1]
查看完整版本: 春节抢票不再愁:Python工程师教你用Python编写自动查票工具