2023年7月6日发(作者:)
python爬取12306(⽕车票分析助⼿)说明⽂字:1.本项⽬只是⼀个练习,熟悉python爬⾍技术,没有任何⽤途2.最后运⾏的结果有时候会成功,有时候会显⽰错误界⾯,如下图所⽰。因为12306怎么可能允许你⼀直爬它呢开发⼯具准备:开发⼯具:PyCharm内置模块:sys,time,datetime,os,json,re第三⽅模块:PyQt5,pyqt5-tools,requests,matplotlib准备⼯作: 下载数据⽂件:(车站名称⽂件)和(起售时间⽂件)创建get_⽂件:import jsonimport re #通过正则表达式匹配处理相应的字符串import os #判断某个路径下的某个⽂件import requests #处理⽹络请求def get_selling_time(): url='/index/script/core/common/qss_' response=(url,verify=True) #请求并进⾏验证 print() print(type()) json_str=l('{[^}]+}',) #匹配括号内所有内容 print(json_str) time_js=(json_str[0]) #解析JSON数据 print(time_js) write(str(time_js),'') #调⽤写⼊⽅法def get_station(): #发送请求获取所有车站名称,通过输⼊的站名称转化查询地址的参数 url='/otn/resources/js/framework/station_?station_version=1.9151' response=(url,verify=True) #请求并进⾏验证 #print() print(type()) #[u4e00-u9fa5]+表⽰匹配给定字符中任意⼀个汉字 stations=l('([u4e00-u9fa5]+)|([A-Z]+)',) #获取需要的车站名称 #print(stations) #print(type(stations)) stations=dict(stations) #转换为字典 #print(stations) stations=str(stations) #转换为字符串类型否则⽆法写⼊⽂件 #print(stations) write(stations, '') #调⽤写⼊⽅法#写⼊⽂件def write(stations,file_name): file=open(file_name,'w',encoding='utf_8_sig') #以写模式打开⽂件 (stations) #写⼊数据 ()#读⽂件def read(file_name): file=open(file_name,'r',encoding='utf_8_sig') #以读模式打开⽂件 data=ne() #读取⽂件 () return data#判断⽂件是否存在def is_stations(file_name): is_stations=(file_name) return is_stationsif __name__=='__main__': if is_stations('') is False: get_station() #
下载所有车站⽂件 if is_stations('') is False: get_selling_time() #
下载起售时间⽂件 在PyCharm中设置PyQt5⼯具: Designer:主要进⾏主窗体的UI设计,最后保存的是ui⽂件⼯具:将.ui⽂件转成.py⽂件⼯具:将.qrc⽂件转成.py⽂件配置链接:⽤Qt Designer设计的主窗体效果图:项⽬结构:img_resources⽂件夹主要是图⽚资源⽂件,主窗体UI设计⽤到的两个.png图⽚和⼀个.qrc图⽚资源⽂件ui⽂件夹保存的是Qt Designer设计的窗体ui⽂件是⽤Pyuic⼯具转换的img_是⽤Pyrcc⼯具转换的get_是下载车站名称与起售时间代码,运⾏之后会出现(车站名称⽂件)和(起售时间⽂件)最重要的就是:query_查询⽹络请求代码 以及 show_显⽰与控制窗体代码。项⽬说明:根据上⾯设计的主窗体效果图,可以看出来,该项⽬主要分为三⼤模块:车票查询、卧铺售票分析、车票起售时间。第⼆个模块最复杂,所以先说简单的两个模块****⼩⼩提⽰:以下内容把我⾃⼰⼿写总结的图和代码配合起来看,⽐较容易理解 * ***模块⼀:车票查询:下⾯这个图是我整理的车票查询的步骤:关键代码如下:1. 在show_⽂件中创建on_click()⽅法,在该⽅法中:⾸先获取输⼊的内容,然后进⾏参数审核,接着发送查询请求调⽤query()⽅法,最后将查询结果显⽰在窗体表格中(调⽤displayTable()⽅法)。#主窗体的查询按钮def on_click(self): get_from=nText() #
获取出发地 get_to=it_nText() #
获取到达地 get_date=it_nText() #
获取出发时间 #
判断车站⽂件是否存在 if is_stations('') is True: stations=eval(read('')) #
读取所有车站并转换为字典类型 #判断所有参数是否为空 if get_from!="" and get_to!="" and get_date!="": #判断输⼊的车站名称是否都存在,以及时间格式是否正确 if get_from in stations and get_to in stations and _valid_date(get_date): #计算时间差 time_difference=_difference(_time(),get_date).days # 12306官⽅要求智能查询30天以内的车票 if 0 <= time_difference <= 29: #在所有车站⽂件中找到对应的参数、出发地 from_station=stations[get_from] #print(from_station) to_station=stations[get_to] #print(to_station) #发送查询请求,并获取返回信息 data=query(get_date,get_from,from_station,get_to,to_station) print('正确1') ox_default() if len(data) !=0: #将车票信息显⽰到表格中 print('正确2') yTable(len(data),16,data) else: messageDialog('警告','没有返回的⽹络数据!') else: messageDialog('警告', '超出查询⽇期的范围!') else: messageDialog('警告', '输⼊的站名不存在,或⽇期格式不正确!') else: messageDialog('警告', '请填写车站名称!') else: messageDialog('警告', '未下载车站查询⽂件!')#消息提⽰框,参数title为提⽰框标题⽂字,message为提⽰信息def messageDialog(title,message): msg_box=QMessageBox(g,title,message) msg__()2. 在query_⽂件中创建query()⽅法,该⽅法需要三个参数:出发⽇期、出发地、⽬的地。在该⽅法中,查询请求地址是通过format()⽅法对地址进⾏格式化。由于获取到的JSON信息⽐较乱,所以在获取指定数据时通过split()进⾏分割,通过与浏览器余票查询页⾯中的数据逐个对⽐找出数据所对应的位置。5-7 ⽬的地 3 车次 6 出发地 8 出发时间 9 到达时间 10 历时 26 ⽆坐 29 硬座24 软座 28 硬卧 33 动卧 23 软卧 21 ⾼级软卧 30 ⼆等座 31 ⼀等座 32 商务座特等座data=[] #
保存整理好的车次信息type_data=[] #
保存分类后的车次信息(如⾼铁,动车等)def query(date,get_from,from_station,get_to,to_station): () #
清空数据 type_() #
查询请求地址 url = '/otn/leftTicket/query?_date={}&_station={}&_station={}&purpose_codes=ADULT'.format(date, from_station, to_station)
response=(url,headers=header,verify=False) #
发送查询请求 if with(u'ufeff'): = ('utf8')[3:].decode('utf8') ng = 'utf-8' print() result = () result=result['data']['result']
#
判断车站⽂件是否存在 if is_stations('') : stations=eval(read('')) #
读取所有车站并转换为字典类型 if len(stations)!=0: #
判断返回数据是否为空 for i in result: tmp_list=('|') #
分割数据并添加到列表中 print(tmp_list) #因为查询结果中出发站和到达站为站名的缩写字母,所以需要在车站库中找到对应的车站名称 from_station=list(())[list(()).index(tmp_list[6])] to_station=list(())[list(()).index(tmp_list[7])] #创建座位数组,由于返回的作为数据中含有空值,所以将空改成“--”这样好识别 seat=[tmp_list[3],from_station,to_station,tmp_list[8],tmp_list[9],tmp_list[10], tmp_list[32],tmp_list[31],tmp_list[30],tmp_list[21],tmp_list[23], tmp_list[33],tmp_list[28],tmp_list[24],tmp_list[29],tmp_list[26]] print(seat) newSeat=[] #循环将座位信息中的空值改成“--” for s in seat: if s=="": s="--" else: s=s (s) #
保存新的座位信息 (newSeat) print(newSeat) return data #
返回整理好的车次信息3. 在show_⽂件中创建displayTable()⽅法,⽤于将车票信息显⽰到主窗体的表格中。#显⽰车次信息的表格#train参数为共有多少趟列车,该参数为表格的⾏#info参数为每趟列车的具体信息,例如有座、⽆座、卧铺等,该参数作为表格的列def displayTable(self,train,info,data): () for row in range(train): for column in range(info): #添加表格内容 item=QStandardItem(data[row][column]) #向表格存储模式中添加表格具体信息 m(row,column,item) #设置表格存储数据的模式 el()4. on_click()⾥⾯调⽤的⼀些基本⽅法如下(很好理解):_valid_date()判断输⼊的⽇期是否合法;_difference()计算两个⽇期相差的天数;_time()获取当前⽇期;ox_default()车次类型的复选框取消勾选。#判断是否是⼀个有效的⽇期字符串def is_valid_date(self, str): try: me(str, "%Y-%m-%d") return True except: return False#
获取系统当前时间并转换请求数据所需要的格式def get_time(self): #
获得当前时间时间戳 now = int(()) #
转换为其它⽇期格式,如:"%Y-%m-%d %H:%M:%S" timeStruct = ime(now) strTime = me("%Y-%m-%d", timeStruct) return strTime#
计算购票时间差,因为只能提前购买29天的车票def time_difference(self, in_time, new_time): #
将字符串⽇期转换为struct_time时间对象 in_time = me(in_time, "%Y-%m-%d") new_time = me(new_time, "%Y-%m-%d") #
将struct_time时间对象转换为datetime对象 in_time = me(in_time[0], in_time[1], in_time[2]) new_time = me(new_time[0], new_time[1], new_time[2]) #
返回两个变量相差的值,就是相差天数 return new_time - in_time#
将所有车次分类复选框取消勾选def checkBox_default(self): ox_cked(False) ox_cked(False) ox_cked(False) ox_cked(False) ox_cked(False)运⾏结果如下图:(运⾏不出来的话,也别担⼼,我也只是偶尔⼀次运⾏成功了。这个图⽚是⽹上找到的,就是让⼤家看看结果是什么样⼦,有便于更好的理解代码,⼼态放好哈哈哈哈哈)模块⼆:车票起售时间查询这个模块⽐较简单些,我整理的步骤如下:1. 在show_中创建query_time_click()⽅法,查询并显⽰车票起售时间。⾸先调⽤query_time()⽅法查询起售车站对应的站名与起售时间,然后将⽹格布局清空,接着创建控件并设置属性,最后加载控件并且显⽰#车票起售时间查询按钮的事件处理def query_time_click(self): station=() #
获取需要查询的起售车站 #print(station) stations_time=eval(read('')) #
读取所有车站与起售时间并转换为字典类型 stations=eval(read('')) #
读取所有车站并转换为字典类型 if station in stations_time: #
查询起售车站对应的站名与起售时间 name_lit,time_list=query_time((station)) #
每次点循环删除管理器的控件 if ()!=0: while (): item=(0) #
获取第⼀个控件 widget=() #
删除控件 Later() i=-1 #
⾏数标记 for n in range(len(name_lit)): x=n % 4 # x
确定每⾏显⽰的个数 0,1,2,3
每⾏4个 #
当x为0的时候设置换⾏
⾏数+1 if x==0: i+=1 =t() #
创建布局 ectName("widget"+str(n)) #
给布局命名 #
设置布局样式 leSheet('QWidget#' + "widget" + str(n) + "{border:2px solid rgb(175, 175, 175);background-color: rgb(255, 255, 255);}") #
创建个Qlabel控件⽤于显⽰图⽚
设置控件在QWidget中 =() gnment(enter) #
设置⼤⼩ metry((10,10,210,65)) font=() #
创建字体对象 ntSize(11) #
设置字体⼤⼩ d(True) #
开启粗体属性 ght(75) #
设置⽂字粗细 t(font) #
设置字体 #
设置显⽰站名与起售时间 t(name_lit[n]+' '+time_list[n]) #
把动态创建的widegt布局添加到gridLayout中 i,x分别代表:⾏数以及每⾏的个数 get(,i,x) #
设置⾼度为动态⾼度根据⾏数确定⾼度
每⾏300 AreaWidgetContents_imumHeight((i+1)*100) #
设置⽹格布局控件动态⾼度 metry((0,0,950,((i+1)*100)))2. 在query_中⾸先创建两个⽤于保存车站名称与起售时间的列表,然后创建query_time()⽅法,⽤于发送查询车票起售时间的⽹络请求,这⾥是post请求,⽽且会⽤到表单参数{“station_telecode”:station}。将返回的信息添加⾄对应的列表当中,最后将两个列表信息返回。station_name_list=[] #保存起售车站名称列表station_time_list=[] #保存起售车站对应时间列表#查询车票起售时间def query_time(station): station_name_() station_time_() #读取所有车站并转换为字典类型 stations=eval(read('')) url='/index/otn/index12306/queryScSname' #表单参数,station参数为需要搜索车站的英⽂缩写 form_data={"station_telecode":station} response=(url,data=form_data,verify=True) #
请求并进⾏验证 ng='utf-8' #
对请求所返回的数据进⾏编码 json_data=() #
解析json数据 data=json_('data') #
获取json中可⽤数据,也就是查询车站所对应的站名 for i in data: #
遍历查询车站所对应的所有站名 if i in stations: #
在站名时间⽂件中,判断是否存在该站名 station_name_(i) #
有该站名就将站名添加⾄列表中 for name in station_name_list: #
遍历筛选后的站名 time=(name) #
通过站名获取对应的时间 station_time_(time) #
将时间保存⾄列表 return station_name_list,station_time_list运⾏结果如图:(这个是可以运⾏出来的,因为它和模块⼀车票查询⽤到的url是不⼀样的,不会对12306造成损失。如果运⾏不出结果,就说明代码有问题了)模块三:卧铺售票分析(⽐较难理解)先给出我整理的复杂的步骤,看这密密⿇⿇的字就知道内容很多主要分为两个部分:部分⼀是卧铺售票分析区域。第⼆个部分卧铺车票数量折线图。下⾯分别来说这两个部分:模块三部分⼀:卧铺售票的查询与分析1. 在show_⽂件中创建query_ticketing_analysis_click()⽅法,作为卧铺售票分析查询按钮的事件处理⽅法。该⽅法先获取输⼊的出发地和⽬的地。再调⽤query_ticketing_analysis()⽅法分别查询今天、三天内、五天内的卧铺信息。(query_ticketing_analysis()⽅法在后⾯的代码5)#卧铺售票分析查询按钮的事件处理⽅法def query_ticketing_analysis_click(self): _table=[] #
保存窗体表格的车次信息 today_car_() #
清空今天列车信息,已处理是否有票 three_car_() #
清空三天列车信息,已处理是否有票 five_car_() #
清空五天列车信息,已处理是否有票 today_() #
清空今天列车信息,未处理是否有票 three_() #
清空三天列车信息,未处理是否有票 five_() #
清空五天列车信息,未处理是否有票 get_from=it_analysis_nText() #
获取出发地 get_to=it_analysis_nText() #
获取到达地 stations=eval(read('')) #
读取所有车站并转换为字典类型 if get_from!="" and get_to!="": if get_from in stations and get_to in stations: from_station=stations[get_from] #
在所有车站⽂件中找到对应的参数,出发地 to_station=stations[get_to] #
⽬的地 today=() #
获取当天⽇期 three_set=lta(days=+2) #
三天内偏移天数 five_set=lta(days=+4) #
五天内偏移天数 three_day=(today+three_set).strftime('%Y-%m-%d') #
三天格式化后的⽇期 five_day=(today+five_set).strftime('%Y-%m-%d') #
五天格式化后的⽇期 today=me('%Y-%m-%d') #
今天格式化后的⽇期 #发送查询今天卧铺票信息的⽹络请求,并获取返回的信息 query_ticketing_analysis(today,from_station,to_station,1) #
发送查询三天内卧铺票信息的⽹络请求,并获取返回的信息 query_ticketing_analysis(three_day,from_station,to_station,3) #
发送查询五天内卧铺票信息的⽹络请求,并获取返回的信息 query_ticketing_analysis(five_day,from_station,to_station,5)上⾯这个代码⽐较好理解。假如这⾥的today为2020-08-19,那么three_day就是2020-08-21,five_day就是2020-08-23。2. 然后在query_ticketing_analysis_click()⽅法中,将所有车次信息进⾏整合及筛选。这⾥⽤到了集合set。因为集合最好的⼀个⽤途就是去掉重复元素,集合中每个元素都是唯⼀的。主要是两个for循环info_set=set() #
创建筛选车次集合,将相同车次进⾏整合,查看共有⼏趟列车for i in today_car_list+three_car_list+five_car_list: #因为在集合中必须是字符串才能整合,所以将车次信息转换为字符串类型,⽅便车次整合 info_(str(i[0:6]))for info in info_set: #
遍历车次信息 info=eval(info) #
将车次信息再次转换成列表 is_today_ture=False #
判断今天是否存在某趟列车的标记 for i in today_car_list: #
遍历今天的车次信息,该车次信息是没有筛选的信息 if info[0] in i: #
判断整合后的车次,在今天的车次信息中是否存在 is_today_ture=True #
存在就进⾏标记 (i[6]) #
如果存在就将车次信息中是否有卧铺的信息添加⾄整合后的车次信息中 break if is_today_ture==False: #
如果今天没有某⼀趟车信息就标记为-- ('--') is_three_true=False for i in three_car_list: if info[0] in i: is_three_true=True (i[6]) break if is_three_true==False: ('--') is_five_true=False for i in five_car_list: if info[0] in i: is_five_true=True (i[6]) break if is_five_true==False: ('--') _(info) #
将最后的结果添加⾄窗体表格的列表中这⾥可能⽐较难理解⼀些!有的⼈可能会问为什么要整合筛选车次信息呢?怎么整合的呢?举个例⼦吧,⽐较好理解⼀些。假如上⾯1的代码调⽤query_ticketing_analysis()⽅法之后today_car_list,three_car_list,five_car_list的结果分别是:today_car_list=[['k105','北京西','深圳','23:18','04:20','29:02','⽆']]three_car_list=[['k105','北京西','深圳','23:18','04:20','29:02','有'], ['z313','北京','深圳','08:30','12:50','28:20','有'],]
five_car_list=[['k105','北京西','深圳','23:18','04:20','29:02','有'], ['z313','北京','深圳','08:30','12:50','28:20','有'],]上⾯三个列表关于车次k105的⼀些基本信息(车次、出发地、⽬的地、出发时间、到达时间、历时)重复了三次,有些累赘;⽽车次z313重复了两次。这些重复信息只出现⼀次就可以了,所以要⽤set集合对他们进⾏整合筛选。for i in today_car_list+three_car_list+five_car_list: #因为在集合中必须是字符串才能整合,所以将车次信息转换为字符串类型,⽅便车次整合 info_(str(i[0:6]))如果是同⼀个车次,那么today_car_list,three_car_list,five_car_list三个列表中前6个元素(i[0]到i[5])的内容都是相同的。这是第⼀个for循环,每次将i[0]到i[5]的内容(也就是车次、出发地、⽬的地、出发时间、到达时间、历时)转成字符串,再添加到集合⾥,可以过滤掉多余的重复信息。也就是说today_car_list,three_car_list,five_car_list三个列表的最后⼀个 ‘有’或’⽆’ 不在info_set⾥⾯。这时候info_set⾥的内容就是下图所⽰,每个车次的基本信息只出现⼀次,⽽没有重复。{"['k105', '北京西', '深圳', '23:18', '04:20', '29:02']",
"['z313', '北京', '深圳', '08:30', '12:50', '28:20']"}代码2的第2个for循环是将today_car_list,three_car_list,five_car_list三个列表中的最后⼀个元素 ‘有’或’⽆’ 添加到info⾥⾯。如果列表中没有该车次,就将’- -‘添加到info⾥⾯。就如today_car_list没有z313车次,所以在info⾥⾯z313车次的“今天”卧铺票信息就是’- -’。执⾏第2个for循环后,info⾥的内容就是下图所⽰[['k105', '北京西', '深圳', '23:18', '04:20', '29:02', '⽆', '有', '有'],['z313', '北京', '深圳', '08:30', '12:50', '28:20', '--', '有', '有']]3. 然后在query_ticketing_analysis_click()⽅法中,将已经筛选好的车次信息与对应卧铺是否有票信息显⽰在主窗体的表格中,然后对车次与卧铺信息进⾏积分计算。Count(len(_table)) #
设置表格⾏数umnCount(9)#设置表格内容⽂字⼤⼩font=()ntSize(12)t(font)#根据窗体⼤⼩拉伸表格ntalHeader().setSectionResizeMode(h)#遍历最终的信息for row in range(len(_table)): fraction=0 #
分数,根据该分数判断列车的紧张程度 for column in range(9): if column==6: #
如果某趟列车当天⽆票 if _table[row][column]=='⽆' or _table[row][column]=='--': fraction+=3 #
计三分 if column==7: if _table[row][column]=='⽆' or _table[row][column]=='--': fraction+=2 if column==8: if _table[row][column]=='⽆' or _table[row][column]=='--': fraction+=1 #分数⼤于或等于5分的车次为红⾊,说明该车次卧铺⾮常紧张 if fraction>=5: #定位是哪趟车次符合该条件,遍历该车次信息 for i in range(len(_table[row])): #表格中的信息 item=WidgetItem(_table[row][i]) kground(QColor(255,0,0)); #
设置该车次背景颜⾊ m(row,i,item) #
设置表格显⽰的内容 #橙⾊,说明改车次卧铺紧张 if 1 <= fraction <= 4: #定位是哪趟车次符合该条件,遍历该车次信息 for i in range(len(_table[row])): #表格中的信息 item=WidgetItem(_table[row][i]) kground(QColor(255,170,0)); #
设置该车次背景颜⾊ m(row,i,item) #
设置表格显⽰的内容 #说明该车次卧铺不紧张 if fraction==0: #定位是哪趟车次符合该条件,遍历该车次信息 for i in range(len(_table[row])): #表格中的信息 item=WidgetItem(_table[row][i]) kground(QColor(85,170,0)); #
设置该车次背景颜⾊ m(row,i,item) #
设置表格显⽰的内容根据该代码就可以得出k105得分是3分,属于卧铺紧张。z313也是3分,属于卧铺紧张。4. query_ticketing_analysis_click()⽅法的整体代码如下#卧铺售票分析查询按钮的事件处理⽅法def query_ticketing_analysis_click(self): _table=[] #
保存窗体表格的车次信息 _table=[] #
保存窗体表格的车次信息 today_car_() #
清空今天列车信息,已处理是否有票 three_car_() #
清空三天列车信息,已处理是否有票 five_car_() #
清空五天列车信息,已处理是否有票 today_() #
清空今天列车信息,未处理是否有票 three_() #
清空三天列车信息,未处理是否有票 five_() #
清空五天列车信息,未处理是否有票 get_from=it_analysis_nText() #
获取出发地 get_to=it_analysis_nText() #
获取到达地 stations=eval(read('')) #
读取所有车站并转换为字典类型 if get_from!="" and get_to!="": if get_from in stations and get_to in stations: from_station=stations[get_from] #
在所有车站⽂件中找到对应的参数,出发地 to_station=stations[get_to] #
⽬的地 today=() #
获取当天⽇期 three_set=lta(days=+2) #
三天内偏移天数 five_set=lta(days=+4) #
五天内偏移天数 three_day=(today+three_set).strftime('%Y-%m-%d') #
三天格式化后的⽇期 five_day=(today+five_set).strftime('%Y-%m-%d') #
五天格式化后的⽇期 today=me('%Y-%m-%d') #
今天格式化后的⽇期 #发送查询今天卧铺票信息的⽹络请求,并获取返回的信息 query_ticketing_analysis(today,from_station,to_station,1) #
发送查询三天内卧铺票信息的⽹络请求,并获取返回的信息 query_ticketing_analysis(three_day,from_station,to_station,3) #
发送查询五天内卧铺票信息的⽹络请求,并获取返回的信息 query_ticketing_analysis(five_day,from_station,to_station,5) info_set=set() #
创建筛选车次集合,将相同车次进⾏整合,查看共有⼏趟列车 for i in today_car_list+three_car_list+five_car_list: #因为在集合中必须是字符串才能整合,所以将车次信息转换为字符串类型,⽅便车次整合 info_(str(i[0:6])) for info in info_set: #
遍历车次信息 info=eval(info) #
将车次信息再次转换成列表 is_today_ture=False #
判断今天是否存在某趟列车的标记 for i in today_car_list: #
遍历今天的车次信息,该车次信息是没有筛选的信息 if info[0] in i: #
判断整合后的车次,在今天的车次信息中是否存在 is_today_ture=True #
存在就进⾏标记 (i[6]) #
如果存在就将车次信息中是否有卧铺的信息添加⾄整合后的车次信息中 break if is_today_ture==False: #
如果今天没有某⼀趟车信息就标记为-- ('--') is_three_true=False for i in three_car_list: if info[0] in i: is_three_true=True (i[6]) break if is_three_true==False: ('--') is_five_true=False for i in five_car_list: if info[0] in i: is_five_true=True (i[6]) break if is_five_true==False: ('--') ('--') _(info) #
将最后的结果添加⾄窗体表格的列表中 Count(len(_table)) #
设置表格⾏数 umnCount(9) #设置表格内容⽂字⼤⼩ font=() ntSize(12) t(font) #根据窗体⼤⼩拉伸表格 ntalHeader().setSectionResizeMode(h) #遍历最终的信息 for row in range(len(_table)): fraction=0 #
分数,根据该分数判断列车的紧张程度 for column in range(9): if column==6: #
如果某趟列车当天⽆票 if _table[row][column]=='⽆' or _table[row][column]=='--': fraction+=3 #
计三分 if column==7: if _table[row][column]=='⽆' or _table[row][column]=='--': fraction+=2 if column==8: if _table[row][column]=='⽆' or _table[row][column]=='--': fraction+=1 #分数⼤于或等于5分的车次为红⾊,说明该车次卧铺⾮常紧张 if fraction>=5: #定位是哪趟车次符合该条件,遍历该车次信息 for i in range(len(_table[row])): #表格中的信息 item=WidgetItem(_table[row][i]) kground(QColor(255,0,0)); #
设置该车次背景颜⾊ m(row,i,item) #
设置表格显⽰的内容 #橙⾊,说明改车次卧铺紧张 if 1 <= fraction <= 4: #定位是哪趟车次符合该条件,遍历该车次信息 for i in range(len(_table[row])): #表格中的信息 item=WidgetItem(_table[row][i]) kground(QColor(255,170,0)); #
设置该车次背景颜⾊ m(row,i,item) #
设置表格显⽰的内容 #说明该车次卧铺不紧张 if fraction==0: #定位是哪趟车次符合该条件,遍历该车次信息 for i in range(len(_table[row])): #表格中的信息 item=WidgetItem(_table[row][i]) kground(QColor(85,170,0)); #
设置该车次背景颜⾊ m(row,i,item) #
设置表格显⽰的内容5.在query_中 创建query_ticketing_analysis()⽅法,在该⽅法中⾸先发送查询请求,然后分别对今天、三天内、五天内的车票信息进⾏处理与分类。today_car_list=[] #
保存今天列车信息,已经处理是否有票three_car_list=[] #
保存三天列车信息,已经处理是否有票five_car_list=[] #
保存五天列车信息,已经处理是否有票today_list=[] #
保存今天列车信息,未处理是否有票three_list=[] #
保存三天列车信息,未处理是否有票five_list=[] #
保存五天列车信息,未处理是否有票#查询卧铺售票分析数据def query_ticketing_analysis(date,from_station,to_station,which_day): #查询请求地址 url = '/otn/leftTicket/query?_date={}&_station={}' '&_station={}&purpose_codes=ADULT'.format(date,from_station,to_station) #发送查询请求 response=(url) #
将json数据转换为字典类型,通过键值对取数据 result=() result=result['data']['result'] #
判断车站⽂件是否存在 if is_stations('') : stations=eval(read('')) #
读取所有车站并转换为字典类型 if len(stations)!=0: #
判断返回数据是否为空 for i in result: #
分割数据并添加到列表中 tmp_list=('|') #print(tmp_list) #因为查询结果中出发站和到达站为站名的缩写字母,所以需要在车站库中找到对应的车站名称 from_station=list(())[list(()).index(tmp_list[6])] to_station=list(())[list(()).index(tmp_list[7])] #创建座位数组,其中包含⾼级软卧、软卧、硬卧 seat=[tmp_list[3],from_station,to_station,tmp_list[8],tmp_list[9],tmp_list[10], tmp_list[21],tmp_list[23],tmp_list[28]] #print(seat) #判断今天的车次信息 if which_day==1: #将⾼铁、动、C开头的车次排除 if seat[0].startswith('G') is False and seat[0].startswith('D') is False and seat[0].startswith('C') is False: #将⾼级软卧、软卧、硬卧未处理信息添加⾄列表中 today_(seat) #判断某车次是否有票 new_seat=is_ticket(tmp_list,from_station,to_station) #将判断后的车次信息添加⾄对应的列表中 today_car_(new_seat) #判断三天的车次信息 if which_day==3: if seat[0].startswith('G') is False and seat[0].startswith('D') is False and seat[0].startswith('C') is False: three_(seat) new_seat=is_ticket(tmp_list,from_station,to_station) three_car_(new_seat) #判断五天的车次信息 if which_day==5: if seat[0].startswith('G') is False and seat[0].startswith('D') is False and seat[0].startswith('C') is False: five_(seat) new_seat=is_ticket(tmp_list,from_station,to_station) five_car_(new_seat)此代码运⾏完之后,得到的6个列表的数据类似于下图:today_list、three_list、five_list包含的信息主要的是:车次、出发地、⽬的地、出发时间、到达时间、历时、⾼级软卧票数、软卧票数、硬卧票数today_car_list、three_car_list、five_car_list包含的信息主要的是:车次、出发地、⽬的地、出发时间、到达时间、历时、是否有卧铺票today_list=[['k105','北京西','深圳','23:18','04:20','29:02','--','⽆','⽆']]today_car_list=[['k105','北京西','深圳','23:18','04:20','29:02','⽆']]three_list=[['k105','北京西','深圳','23:18','04:20','29:02','--','有','有'], ['z313','北京','深圳','08:30','12:50','28:20','--','⽆','有'],]three_car_list=[['k105','北京西','深圳','23:18','04:20','29:02','有'], ['z313','北京','深圳','08:30','12:50','28:20','有'],]
five_list=[['k105','北京西','深圳','23:18','04:20','29:02','有','有','有'], ['z313','北京','深圳','08:30','12:50','28:20','⽆','10','有'],]five_car_list=[['k105','北京西','深圳','23:18','04:20','29:02','有'], ['z313','北京','深圳','08:30','12:50','28:20','有'],]today_list、three_list、five_list三个列表主要⽤于后⾯的计算并显⽰卧铺数量的折线图today_car_list、three_car_list、five_car_li列表主要⽤某车次是否还有卧铺车票以及分析车票的紧张程度6. 上⾯的query_ticketing_analysis()⽅法调⽤了is_ticket()⽅法,⽤于判断某车次是否还有卧铺车票。如果⾼级软卧、软卧、硬卧其中有⼀个有票的话(对应的是’有’或者是数字),就说明该类车次有卧铺车票。否则就是没票#判断⾼级软卧、软卧、硬卧是否有票def is_ticket(tmp_list,from_station,to_station): #
判断⾼级软卧、软卧、硬卧任何⼀个有票的话,就说明该趟车有卧铺票 if tmp_list[21]=='有' or tmp_list[23]=='有' or tmp_list[28]=='有': tmp_tem='有' else: #
判断⾼级软卧、软卧、硬卧对应的如果是数字说明也有票,其他为⽆票 if tmp_list[21].isdigit() or tmp_list[23].isdigit() or tmp_list[28].isdigit(): tmp_tem='有' else: tmp_tem='⽆' #创建新的座位列表,显⽰某趟车是否有卧铺票 new_seat=[tmp_list[3],from_station,to_station,tmp_list[8],tmp_list[9],tmp_list[10],tmp_tem] return new_seat运⾏结果如下图:(如果运⾏不出来结果是正常的,还是因为12306不让你爬取它哦,主要还是理解代码)模块三部分⼆:卧铺车票折线图绘制1. 在show_⽂件中创建show_brokenn_line()⽅法,⽤于显⽰卧铺车票数量的折线图。主要会⽤到today_list、three_list、five_list这三个列表的信息。⾸先对每个车次中今天、三天内、五天内的卧铺数量分别进⾏统计(调⽤statistical_quantity()⽅法),然后实现当车次信息量⼤时,添加滚动条扩⼤折线图⾼度,然后创建⾃定义的画布对象,在调⽤broken_line()⽅法实现折线图的绘制,最后将折线图显⽰在主窗体的⽔平布局中。#
显⽰卧铺车票数量折线图def show_broken_line(self): train_number_list=[] #
保存车次 tickets_number_list=[] #
保存今天,三天内,五天内所有车次的卧铺数量 #
遍历车次信息 for train_number in _table: number_list=[] #
临时保存车票数量 if ()!=0: #
每次点循环删除管理器的组件 while (): item=(0) #
获取第⼀个组件 widget=() #
删除组件 Later() is_today_true=False #
判断今天是否存在某趟列车的标记 for today in today_list: #
判断今天的车次信息中是否有该车次 if train_number[0] in today: is_today_true=True #
存在就进⾏标记 number=tical_quantity(today[6:9]) #
调⽤统计车票数量的⽅法 number_(number) #
将车票数量添加⾄临时列表中 break if is_today_true==False: #
如果今天没有某⼀趟列车,说明该车次⽆票为0 number_(0) is_three_true = False for three_today in three_list: if train_number[0] in three_today: is_three_true = True number = tical_quantity(three_today[6:9]) number_(number) break if is_three_true == False: number_(0) is_five_true = False for five_today in five_list: if train_number[0] in five_today: is_five_true = True number = tical_quantity(five_today[6:9]) number_(number) break if is_five_true == False: number_(0) tickets_number_(number_list) train_number_(train_number[0]) #车次信息⼤时,添加滚动条扩⼤折线图⾼度 if len(train_number_list)>=9: imumHeight(len(train_number_list)*30) metry((0,0,951,(len(train_number_list)*30))) #创建画布对象 line=PlotCanvas() _line(tickets_number_list,train_number_list) #
调⽤折线图⽅法 get(line) #
将折线图添加⾄底部⽔平布局当中2. 在show_⽂件中创建statistical_quantity()⽅法,⽤于统计车票数量。如果是有,增加20个车票;如果是⽆,增加0个车票;如果是数字,就增加对应的数字。#统计车票数量def statistical_quantity(selfself,msg): number=0 #
车票初始值 for i in msg: if i=='有': #
如果是有,增加20个车票 number+=20 if i=='⽆' or i=='': #
如果是⽆或者空,就增加0个车票 number+=0 if t(): #
如果是数字,就直接增加对应的数字 number+=int(i) return number3. 创建⽂件,在该⽂件中创建PlotCanvas类,通过__init()__()⽅法进⾏初始化,创建图形以及初始化画布。然后创建broken_line()⽅法,⽤于显⽰车票⾛势的折线图。(感觉基本上都是固定的写法)import matplotlib #
导⼊图表模块from d_qt5agg import FigureCanvasQTAgg as FigureCanvas #
图形画布import as plt #
导⼊绘图模块class PlotCanvas(FigureCanvas): def __init__(self,parent=None,width=0,height=0,dpi=100): #避免中⽂乱码 ms['-serif']=['SimHei'] ms['e_minus']=False #创建图形 fig=(figsize=(width,height),dpi=dpi) #初始化图形画布 FigureCanvas.__init__(self,fig) ent(parent) #
设置⽗类 def broken_line(self,number,train_list): """ linewidth:折线的宽度 marker:折点的形状 markerfacecolor:折点实⼼颜⾊ markersize:折点⼤⼩ """ day_x=['今天','三天内','五天内'] # x轴折线点 for index,n in enumerate(number): #绘制折线 (day_x,n,linewidth=1,marker='o',markerfacecolor='blue',markersize=8,label=train_list[index]) (bbox_to_anchor=(-0.03,1)) #
让图例⽣效。并设置图例显⽰位置 ('卧铺车票数量⾛势图')运⾏结果如下图:(同样运⾏不出来折线图是正常的)哎呀呀呀!我终于写完了。感觉写的好长,⾃⼰完成的⼀个⼩项⽬,记录⼀下吧。有什么问题或者需要源代码的,可以评论。我看到的就会回复
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1688606938a154641.html
评论列表(0条)