python 3 爬取12306余票:升级版-----图形化界面

 

由于上篇博客已经把爬取12306功能实现,这篇主要是在上篇文章上进行升级。想知道爬取12306实现过程点击:点我进入python 3爬取12306这里的图形化界面参考这位博主:点击原博主地址,写的真漂亮,一直以为tkinter写的图形化界面不可能漂亮到哪去,这篇文章就让我改变了认识。就借鉴了他的,果然不是自己写的,调试代码很繁琐,调试了几天,终于弄好了。

先上一张图片镇楼!

环境:python3  +编译器(我用是Python idle 和pycharm)可以用你自己喜欢的编译器。

tkinter(python自带库 图形化界面生成)+time(时间库)

我觉得图形化界面没得什么说的,就是固定的用法, 只是界面的好看与否在于人的想法。所以直接上代码吧,不懂的函数直接百度,一看就明白。这里直接上源代码:总共三个Py文件,一个图形界面,一个爬取12306功能的文件,一个把所有车站和代码关联起来的文件。重点建议理解功能文件。可以看上篇文章:python 3爬取12306

图形界面:

import time,datetime
from tkinter import*
from tkinter import messagebox
from tkinter import ttk
from tkinter.ttk import Treeview
from stationsInfo import station_codes,station_names
from SearchTicketsAPI import *
from datetime import timedelta,date
K = []
D = []
G = []
Z = []
T = []
ticket=[]

def GetStation():
      del ticket[:]
     # cleanTree()
      #cleanTree2()
      for item in tree.get_children():
            tree.delete(item)
      from_station=var_from.get().strip()
      to_station=var_to.get().strip()
      date=str(number.get())
      ticket_list =ShowTicket(station_codes[from_station], station_codes[to_station], date)
      i = 0
      for item in ticket_list:
          if re.match(r'^K', item[0]):
              K.append(i)
          if re.match(r'^D', item[0]):
              D.append(i)
          if re.match(r'^G|C', item[0]):
              G.append(i)
          if re.match(r'^Z', item[0]):
              Z.append(i)
          if re.match(r'^T', item[0]):
              T.append(i)
          i = i + 1
      length = len(ticket_list)
      for i in range(length):
          ticket.append(ticket_list[i])
      pos = FilterFun()
      for i in pos:
          tree.insert('', i, values=ticket_list[i])

      children = tree.get_children()
      for num in range(len(children)):
          if (num % 2 == 0):
              tree.item(children[num], tags=["evenLine"])
          else:
              tree.item(children[num], tags=["evenLine2"])
      tree.tag_configure("evenLine", background='#DEF1EC')
      tree.tag_configure("evenLine2", background='#F1EBDE')
      ticket_list = []
def selectall():
    if all.get() == '1':
        g.set('1')
        d.set('1')
        z.set('1')
        t.set('1')
        k.set('1')
    else:
        g.set('0')
        d.set('0')
        z.set('0')
        t.set('0')
        k.set('0')
    FilterFun2()

def filterG():
    all.set('0')
    FilterFun2()

def filterD():
    all.set('0')
    FilterFun2()

def filterZ():
    all.set('0')
    FilterFun2()

def filterT():
    all.set('0')
    FilterFun2()

def filterK():
    all.set('0')
    FilterFun2()

def cleanTree():
    del ticket[:]
    for item in tree.get_children():
        tree.delete(item)
    all.set('0')
    selectall()
    days.set('1')
def cleanTree2():
    for item in tree.get_children():
        tree.delete(item)

def FilterFun():
    temp = []
    if d.get() == '1':
        for i in D:
            temp.append(i)
    if k.get() == '1':
        for i in K:
            temp.append(i)
    if z.get() == '1':
        for i in Z:
            temp.append(i)
    if t.get() == '1':
        for i in T:
            temp.append(i)
    if g.get() == '1':
        for i in G:
            temp.append(i)
    if g.get() == '0' and d.get() == '0' and k.get() == '0' and z.get() == '0' and t.get() == '0':
        for i in range(len(ticket)):
            temp.append(i)
    return temp

def FilterFun2():
    cleanTree2()
    pos = FilterFun()

    for i in pos:
        tree.insert('', i, values=ticket[i])
    children = tree.get_children()
    for num in range(len(children)):
        if (num % 2 == 0):
            tree.item(children[num], tags=["evenLine"])
        else:
            tree.item(children[num], tags=["evenLine2"])
    tree.tag_configure("evenLine", background='#DEF1EC')
    tree.tag_configure("evenLine2", background='#F1EBDE')

            

window=Tk()
window.geometry("1135x600+400+300")
window.title("火车票查询系统")
window.iconbitmap("train.ico")   #左上角图形界面上的那个小图标,需要一个点.ico格式的图,并且命名为train.ico,或者把这行代码去掉,也能运行
#Frame框架,将几个组件放在一起
TopFrame=Frame(window,width=1135,height=150)
TLFrame=Frame(TopFrame,width=985,height=150)
TRFrame=Frame(TopFrame,width=150,height=150)
TLTFrame=Frame(TLFrame,width=985,height=75)
TLBFrame=Frame(TLFrame,width=985,height=75)

Label(TLTFrame,text="始发站:",font=("宋体",11)).pack(side=LEFT)
#StringVar跟踪变量的值的变化,以保证值的变更随时可以显示在界面上
var_from=StringVar()
From_s=Entry(TLTFrame,textvariable=var_from,width=14).pack(side=LEFT)
Label(TLTFrame,text="终点站:",font=("宋体",11),padx=20).pack(side=LEFT)
var_to=StringVar()
to_s=Entry(TLTFrame,textvariable=var_to,width=14).pack(side=LEFT)
#创建一个下拉列表
Label(TLTFrame,text="出发日期:",font=("宋体",11),padx=20).pack(side=LEFT)
number=StringVar()
numberChose=ttk.Combobox(TLTFrame,width=12,textvariable=number)
values=[]
y=int(time.strftime("%Y",time.localtime()))
m=int(time.strftime("%m",time.localtime()))
d=int(time.strftime("%d",time.localtime()))
i=0
yy=y
mm=m
dd=d
while i<20:#20天数据
      if m in (1,3,5,7,8,10,12):
            if d+i>31:
                  dd=d+i-31
                  mm=m+1
                  if mm>12:
                    yy=y+1
                    mm-=12
            else: dd=d+i
      elif m in (4,6,9,11):
          if d+i>30:
              dd=d+i-30
              mm=m+1
              if mm>12:
                  yy=y+1
                  mm-=12
          else: dd=d+i
      else:
          if d+i>28:
              dd=d+i-28
              mm=m+1
              if mm>12:
                  yy=y+1
                  mm-=12
          else: dd=d+i
      Time='%d-%02d-%02d'%(yy,mm,dd)
      values.append(Time)
      i+=1
numberChose['values']=tuple(values)
numberChose.current(0)# 设置下拉列表默认显示的值,0为 numberChosen['values'] 的下标值
numberChose.pack(side=LEFT)

Label(TRFrame, text='查询天数:', font=('宋体', (11)),padx=5).pack(side=LEFT)
days = StringVar()
daysEntry = Entry(TRFrame, textvariable=days, width=5).pack(side=LEFT)
days.set('1')
Label(TRFrame,text=' ',font=("宋体",(11)),padx=5).pack(side=LEFT)
search=Button(TRFrame,text='查询余票',width=10,command=GetStation,bg='Cyan').pack(side=LEFT)
#Label(TRFrame,text=' ',font=("宋体",(11)),padx=5).pack(side=LEFT)

TLFrame.pack(side=LEFT)
TLTFrame.pack(side=TOP)
TLBFrame.pack(side=BOTTOM)
TRFrame.pack(side=RIGHT)
TopFrame.pack(side=TOP,pady=5)

# 车型选择
Label(TLBFrame, text='车次类型:', font=('宋体', (11))).pack(side=LEFT)
all = StringVar()

g = StringVar()

d = StringVar()

z = StringVar()

t = StringVar()

k = StringVar()

Checkbutton(TLBFrame, text = '全部',padx=10,variable = all,command=selectall).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'G/C-高铁/城际',padx=10, command=filterG, variable = g).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'D-动车',padx=10,variable = d,command=filterD).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'Z-直达',padx=10,variable = z,command=filterZ).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'T-特快',padx=10,variable = t,command=filterT).pack(side=LEFT)
Checkbutton(TLBFrame, text = 'K-快车',padx=10,variable = k,command=filterK).pack(side=LEFT)

all.set('0')
d.set('0')
g.set('0')
t.set('0')
k.set('0')
z.set('0')
# 车票展示
ShowFrame = Frame(window, width=780, height=450, bg='pink')
ShowFrame.pack(side=TOP)

scrollBar = Scrollbar(ShowFrame)
scrollBar.pack(side=RIGHT,fill=Y)
tree = Treeview(ShowFrame,
                columns=('c0','c1','c2','c3','c4','c5','c6','c7','c8','c9','c10','c11','c12','c13','c14','c15'),
                show="headings",
                height=540,
                yscrollcommand=scrollBar.set)
tree.column('c0',width=50,anchor='center')
tree.column('c1',width=100,anchor='center')
tree.column('c2',width=100,anchor='center')
tree.column('c3',width=100,anchor='center')
tree.column('c4',width=50,anchor='center')
tree.column('c5',width=60,anchor='center')
tree.column('c6',width=60,anchor='center')
tree.column('c7',width=60,anchor='center')
tree.column('c8',width=60,anchor='center')
tree.column('c9',width=60,anchor='center')
tree.column('c10',width=60,anchor='center')
tree.column('c11',width=60,anchor='center')
tree.column('c12',width=60,anchor='center')
tree.column('c13',width=60,anchor='center')
tree.column('c14',width=60,anchor='center')
tree.column('c15',width=60,anchor='center')
#显示车次,出发/到达站,出发/到达时间,历时,商务座,一等座,二等座,软卧,动卧,硬卧,硬座,无座
tree.heading('c0',text='车次')
tree.heading('c1',text='出发/到达站')
tree.heading('c2',text='出发/到达时间')
tree.heading('c3',text='历时')
tree.heading('c4',text='商务座')
tree.heading('c5',text='一等座')
tree.heading('c6',text='二等座')
tree.heading('c7',text='高级软卧')
tree.heading('c8',text='软卧')
tree.heading('c9',text='动卧')
tree.heading('c10',text='硬卧')
tree.heading('c11',text='软座')
tree.heading('c12',text='硬座')
tree.heading('c13',text='无座')
tree.heading('c14',text='其他')
tree.heading('c15',text='备注')
tree.pack(side=LEFT,fill=BOTH)

window.mainloop()

功能文件:


#
import requests,re,datetime,time,json
from tkinter import*
from tkinter import messagebox
from datetime import timedelta,date
from prettytable import PrettyTable
from stationsInfo import station_codes,station_names

def getHtmltext(url,headers):
    r=requests.get(url,headers=headers)
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    return r.text

def Show(html):
        html = json.loads(html)
        table = PrettyTable(
            ["车次", "出发/到达车站", "出发/到达时间", "历时", "商务座", "一等座", "二等座", "高级软卧", "软卧", "动卧", "硬卧", "软座", "硬座", "无座", "其他",
             "备注"])
        tickets = []
        for i in html['data']['result']:
            name = [
                "station_train_code",
                "from_station_name",
                'start_time',
                "lishi",
                "swz_num",
                "zy_num",
                "ze_num",
                "gr_num",
                "rw_num",
                "dw_num",
                "yw_num",
                "rz_num",
                "yz_num",
                "wz_num",
                "qt_num",
                "note_num"
            ]
            data = {
                "station_train_code": '',
                "from_station_name": '',
                "to_station_name": '',
                'start_time': '',
                'end': '',
                "lishi": '',
                "swz_num": '',
                "zy_num": '',
                "ze_num": '',
                "dw_num": '',
                "gr_num": '',
                "rw_num": '',
                "yw_num": '',
                "rz_num": '',
                "yz_num": '',
                "wz_num": '',
                "qt_num": '',
                "note_num": ''
            }
            item = i.split('|')  # 用"|"进行分割
            data['station_train_code'] = item[3]  # 车次在3号位置
            data['from_station_name'] = item[6]  # 始发站信息在6号位置
            data['to_station_name'] = item[7]  # 终点站信息在7号位置
            data['start_time'] = item[8]  # 出发时间信息在8号位置
            data['arrive_time'] = item[9]  # 抵达时间在9号位置
            data['lishi'] = item[10]  # 经历时间在10号位置
            data['swz_num'] = item[32] or item[25]  # 特别注意:商务座在32或25位置
            data['zy_num'] = item[31]  # 一等座信息在31号位置
            data['ze_num'] = item[30]  # 二等座信息在30号位置
            data['gr_num'] = item[21]  # 高级软卧信息在31号位置
            data['rw_num'] = item[23]  # 软卧信息在23号位置
            data['dw_num'] = item[27]  # 动卧信息在27号位置
            data['yw_num'] = item[28]  # 硬卧信息在28号位置
            data['rz_num'] = item[24]  # 软座信息在24号位置
            data['yz_num'] = item[29]  # 硬座信息在29号位置
            data['wz_num'] = item[26]  # 无座信息在26号位置
            data['qt_num'] = item[22]  # 其他信息在22号位置
            data['note_num'] = item[1]  # 备注在1号位置
            # 如果没有信息用'-'代替
            for pos in name:
                if data[pos] == '':
                    data[pos] = '-'
            cont = []
            cont.append(data)
            for x in cont:
                tmp = []
                for y in name:
                    if y == "from_station_name":
                        s = station_names[data['from_station_name']] + '--' +station_names[data["to_station_name"]]
                        tmp.append(s)
                    elif y == "start_time":
                        s =data['start_time']+ '--' +data["arrive_time"]
                        tmp.append(s)
                    elif y == "station_train_code":
                        s =data['station_train_code']
                        tmp.append(s)
                    else:
                        tmp.append(data[y])
                tickets.append(tmp)
            """
            for ticket in tickets:
                table.add_row(ticket)
        print(table)
        """
        return tickets
def ShowTicket(from_station,to_station,date):
    time = str(date)
    from_station = from_station
    to_station = to_station
    url = "https://kyfw.12306.cn/otn/leftTicket/query?"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5702.400 QQBrowser/10.2.1893.400"
    }
    # time=entry_time.get()
    # start=entry_start.get()
    # end=entry_end.get()
    url = url + 'leftTicketDTO.train_date=' + time + '&leftTicketDTO.from_station=' + from_station + '&leftTicketDTO.to_station=' + to_station + '&purpose_codes=ADULT'
    html = getHtmltext(url, headers)
    tickets=Show(html)
    return  tickets

车站和车站代码关联:

import re,requests

url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971'
response = requests.get(url, verify=False)
stations = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
station_codes=dict(stations)
station_names=dict(zip(station_codes.values(),station_codes.keys()))

把三个文件放在同一目录下,运行图形界面就ok了!!就能成功如博主最开始截图一样查票了。

如果对你有用请给博主点赞~~谢谢~~

  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落凡尘.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值