春节抢票不再愁: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]