上一篇文章已经生成了界面设计的py文件,下面通过代码来调试主窗体中各控件的细节处理,以及对应的属性。

(1)打开window.py文件,在右侧代码区域的setupUi()方法中自改主窗体的最大值与最小值,用于保持主窗体大小不变,无法扩大或缩小。代码如下:

(2)将图片资源img文件夹复制到该项目中,然后导入PyQt5.QtGui模块中的QPalette、QPixmap、QColor用于对控件设置背景图片,为对象名label_title_img的Label控件设置背景图片,该控件用于显示顶部图片。代码如下:

(3)设置查询部分widget控件的背景图片,该控件起到容器的作用,在设置背景图片时并没有Label控件那么简单。首先需要为该控件开启自动填充背景功能,然后创建调色板对象,指定调色板背景图片,最后为控件设置对应的调色板即可。代码如下:

(4)通过diamante修改窗体或控件文字时,需要在retranslateUI()方法中进行设置,代码如下:

(5)导入sys模块 ,然后在代码块的最外层创建show_MainWindow()方法,该方法用于显示窗体。代码如下:

(6)在代码块的最外层模拟Python的程序入口,然后调用显示窗体的show_MainWindow()方法。代码如下:
 既然是爬票程序,那么一定需要一个爬取的对象,就以12306中国铁路客户服务中心所提供的查票地址来获取火车票的相关信息。在发送请求时,地址中需要填写必要的参数,否则后台无法返回前台所需要的正确信息,所以首先需要分析网页请求参数,步骤如下:

(1)使用火狐浏览器打开12306官网(http://www.12306.cn/),输入出发地和目的地,然后单击查询按钮。快捷键Ctrl+shift+E打开网络监视器。

(2)单机网络请求显示请求细节的窗口,在该窗口中默认会显示消息头的相关数据,此处可以获取完整的请求地址。

(3)在请求地址的上方选择参数选项,将显示该地址中必要参数

得到了请求地址与请求参数后,可以发现请求参数中的出发地和目的地均为车站名的英文缩写。而这个英文缩写的字母是通过输入中文车站名转换而来的,所以需要在网页中仔细查找是否有将车站名自动转换为英文缩写的请求信息。

(1)关闭并重新打开网络监视器,然后按快捷键F5进行余票查询网页的的刷新,此时在网络监视器中选择类型为js的网络请求。在文件类型中仔细分析文件内容是否有,与车站名相关的信息如下:
 (2)选中与车站名相关的网络请求,在请求细节中找到该请求的完整地址。然后再网页中打开改地址测试返回数据。

 (3)打开pycharm开发工具,在check tickets目录的右键菜单中选择new-Python File命令,创建一个名称为get_stations.py的文件,然后再菜单栏中选择File->Default Settings菜单项,安装requests模块。

(4)在get_stations.py文件中分别导入requests、re以及os模块,然后创建getStation()方法,该方法用于发送获取地址信息的网络请求,并将返回的数据转换为需要的类型。代码如下:

(5)分别创建 write()、read()以及isStation()方法,分别用于写入文件、读取文件以及判断车站文件是否存在,代码如下:

 (6)打开window.py文件,首先导入get_stations文件下的所有方法,然后在模拟Python的程序入口处修改代码。首先判断是否有所有车站信息的文件,如果没有该文件,就下载车站信息文件,然后显示窗体,如果有,将直接显示窗体。修改后代码如下:

(7)在window.py文件右键菜单中选择Run Window命令,运行主窗体,主窗体界面显示后再my_pytorch目录下将自动下载station.text文件,改文件可以实现车站名称与对应的英文缩写的转换。

 得到了获取车票信息的网络请求地址,然后又分析出请求地址的必要参数以及车站名称转换的文件。接下来就需要将主窗体中输入的出发地、目的地以及出发日3个重要参数配置到查票的请求地址中,然后分析并接受所查询车票的对应信息。

(1)在浏览器中粘贴我们复制的完整查票地址。注意区分,这个是带日期的,和第一个不同。

(2)发现可用数据后,在项目中创建query_request.py文件,在该文件中首先导入get_stations文件下的所有方法,然后分别创建名称为data与type_data的列表(list),分别用于保存整理好的车次信息与分类后的车次信息。代码如下:

从返回的加密信息中可以看出信息很乱,所以需要创建data=[]列表(list)来保存后期整理好的车次信息,然后需要将车次分类,如高铁、动车等,所以需要创建type_data=[]列表(list)来保存分类后的车次信息。

(3)创建query()方法,在调用该方法时需要3个参数,分别为出发日期、出发地以及目的地。然后创建查询请求的完整地址并通过format()方法为地址进行格式化。再将返回的json数据转换为字典类型,最后通过字典类型键值的方法取出对应的数据并进行整理与分类。代码如下:

def query(date, from_station, to_station):data.clear()  # 清空数据# 查询请求地址cookie='''_uab_collina=162924939911126111496577; JSESSIONID=CEEECF63AAAFADCE84C14B125B0BAF33; BIGipServerotn=569377290.38945.0000; RAIL_EXPIRATION=1629515371138; RAIL_DEVICEID=WFdNvPKASOeg84FkzqmTwo2V9HOZ6PCi13Lo07y6AhvsDwxtH1RABivB_bw-vQMlxSMsGwtFKOTfbxEc62F2DgkmiF_j_Hlgro1ClJXVYItFRoWurt7-HkK3S634C9mkoLsQfZk9xvy_Y1_-sbJCQvt8VE1XIoU1; BIGipServerpool_passport=199492106.50215.0000; route=9036359bb8a8a461c164a04f8f50b252; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toStation=%u4E0A%u6D77%2CSHH; _jc_save_fromDate=2021-08-19; _jc_save_toDate=2021-08-18; _jc_save_wfdc_flag=dc'''header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0','Connection':'keep-alive','accept':'*/*','Cookie':cookie}url = 'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(date, from_station, to_station)# 发送查询请求response = requests.get(url,headers=header)# # 将json数据转换为字典类型,通过键值对取数据result = response.json()result = result['data']['result']# 判断车站文件是否存在if isStations() == True:stations = eval(read())  # 读取所有车站并转换为dic类型if len(result) != 0:  # 判断返回数据是否为空for i in result:# # 分割数据并添加到列表中tmp_list = i.split('|')# 因为查询结果中出发站和到达站为站名的缩写字母,所以需要在车站库中找到对应的车站名称from_station = list(stations.keys())[list(stations.values()).index(tmp_list[6])]to_station = list(stations.keys())[list(stations.values()).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]]newSeat = []# 循环将座位信息中的空既“”,改成--这样好识别for s in seat:if s == "":s = "--"else:s = snewSeat.append(s)  # 保存新的座位信息data.append(newSeat)return data  # 返回整理好的车次信息

(4)一次创建获取高铁信息、移除高铁信息、获取动车、移除动车、获取直达、移除直达、获取特快、移除特快、获取快速以及移除快速的方法。以上方法用于车次分类数据的处理,代码如下:

# 获取高铁信息的方法
def g_vehicle():if len(data) != 0:for g in data:  # 循环所有火车数据i = g[0].startswith('G')  # 判断车次首字母是不是高铁if i:  # 如果是将该条信息添加到高铁数据中type_data.append(g)#移除高铁信息的方法
def r_g_vehicle():if len(data) != 0:for g in data:i = g[0].startswith('G')if i:   #移除高铁信息type_data.remove(g)# 获取动车信息的方法
def d_vehicle():if len(data) != 0:for d in data:  # 循环所有火车数据i = d[0].startswith('D')  # 判断车次首字母是不是动车if i == True:  # 如果是将该条信息添加到动车数据中type_data.append(d)
# 移除动车信息的方法
def r_d_vehicle():if len(data) != 0:for d in data:i = d[0].startswith('D')if i == True:   #移除动车信息type_data.remove(d)# 获取直达车信息的方法
def z_vehicle():if len(data) != 0:for z in data:  # 循环所有火车数据i = z[0].startswith('Z')  # 判断车次首字母是不是直达if i == True:  # 如果是将该条信息添加到直达数据中type_data.append(z)# 移除直达车信息的方法
def r_z_vehicle():if len(data) != 0:for z in data:i = z[0].startswith('Z')if i == True:  # 移除直达车信息type_data.remove(z)# 获取特快车信息的方法
def t_vehicle():if len(data) != 0:for t in data:  # 循环所有火车数据i = t[0].startswith('T')  # 判断车次首字母是不是特快if i == True:  # 如果是将该条信息添加到特快车数据中type_data.append(t)
# 移除特快车信息的方法
def r_t_vehicle():if len(data) != 0:for t in data:i = t[0].startswith('T')if i == True:  # 移除特快车信息type_data.remove(t)# 获取快速车数据的方法
def k_vehicle():if len(data) != 0:for k in data:  # 循环所有火车数据i = k[0].startswith('K')  # 判断车次首字母是不是快车if i == True:  # 如果是将该条信息添加到快车数据中type_data.append(k)# 移除快速车数据的方法
def r_k_vehicle():if len(data) != 0:for k in data:i = k[0].startswith('K')if i == True:  # 移除快车信息type_data.remove(k)

完成了车票信息查询请求的文件后,接下来需要将获取的车票信息显示在快手爬票的主窗体当中。实现步骤如下:

(1)打开window.py文件,导入PyQt5.QtCore模块中的Qt类,然后导入PyQt5.QtWidgets与PyQt5.QtGui模块下的所有方法,再导入query_request文件中的所有方法即可。代码如下:

(2)在setupUi()方法中找到用于显示车票信息的tableView 表格控件。然后为该控件设置相关属性,关键代码如下:

  # 显示车次信息的列表self.tableView = QtWidgets.QTableView(self.centralwidget)self.tableView.setGeometry(QtCore.QRect(0, 320, 960, 440))self.tableView.setObjectName("tableView")self.model = QStandardItemModel();  # 创建存储数据的模式# 根据空间自动改变列宽度并且不可修改列宽度self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)# 设置表头不可见self.tableView.horizontalHeader().setVisible(False)# 纵向表头不可见self.tableView.verticalHeader().setVisible(False)# 设置表格内容文字大小font = QtGui.QFont()font.setPointSize(10)self.tableView.setFont(font)# 设置表格内容不可编辑self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)# 垂直滚动条始终开启self.tableView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

(3)导入time模块,该模块提供了用于处理时间的各种方法。然后再代码块的最外层创建get_time()方法用于获取系统的当前时间,再创建is_valid_date()方法用于判断输入的日期是否是一个有效的日期字符串,代码如下:

(4)依次创建change_G()、change_D()、change_Z()、change_T()、change_K()方法,以上方法均为车次分类复选框的事件处理,由于代码几乎相同,此处提供的关键代码如下:
 (5)创建messageDialog()方法,该方法用于显示主窗体非法操作的消息提示框。然后创建displayTable()方法,该方法用于显示车次信息的表格与内容。代码如下:

(6)创建on_click()方法,该方法是查询按钮的单机事件。在该方法中首先要获取出发地、目的地与出发日期3个编辑框的输入内容,然后对3个编辑框中输入 的内容进行合法监测,符合规范后调用query()方法提交车票查询的请求并且将返回的数据赋值给data,最后通过调用displayTable()方法实现在表格中显示车票查询的全部信息。代码如下:

# 查询按钮的单击事件def on_click(self):get_from = self.textEdit.toPlainText()  # 获取出发地get_to = self.textEdit_2.toPlainText()  # 获取到达地get_date = self.textEdit_3.toPlainText()  # 获取出发时间# 判断车站文件是否存在if isStations() == True:stations = eval(read())  # 读取所有车站并转换为dic类型# 判断所有参数是否为空,出发地、目的地、出发日期if get_from != "" and get_to != "" and get_date != "":# 判断输入的车站名称是否存在,以及时间格式是否正确if get_from in stations and get_to in stations and is_valid_date(get_date):# 获取输入的日期是当前年初到现在一共过了多少天inputYearDay = time.strptime(get_date, "%Y-%m-%d").tm_yday# 获取系统当前日期是当前年初到现在一共过了多少天yearToday = time.localtime(time.time()).tm_yday# 计算时间差,也就是输入的日期减掉系统当前的日期timeDifference = inputYearDay - yearToday# 判断时间差为0时证明是查询当前的查票,# 以及29天以后的车票。12306官方要求只能查询30天以内的车票if timeDifference >= 0 and timeDifference <= 28:from_station = stations[get_from]  # 在所有车站文件中找到对应的参数,出发地to_station = stations[get_to]  # 目的地print(from_station)data = query(get_date, from_station, to_station)  # 发送查询请求,并获取返回的信息if len(data) != 0:  # 判断返回的数据是否为空# 如果不是空的数据就将车票信息显示在表格中self.displayTable(len(data), 16, data)else:self.messageDialog('警告', '没有返回的网络数据!')else:self.messageDialog('警告', '超出查询日期的范围内,''不可查询昨天的车票信息,以及29天以后的车票信息!')else:self.messageDialog('警告', '输入的站名不存在,或日期格式不正确!')else:self.messageDialog('警告', '请填写车站名称!')else:self.messageDialog('警告', '未下载车站查询文件!')

(7)在retranslateUi()方法中,首先设置在出发日的编辑框中显示系统的当前日期,然后设置查询按钮的单击事件,最后分别设置高铁、动车、直达、特快以及快车复选框中与取消事件。关键代码如下:

self.textEdit_3.setText(get_time())        #出发日显示当天日期
self.pushButton.clicked.connect(self.on_click)    #查询按钮指定单击事件的方法
self.checkBox_G.stateChanged.connect(self.change_G) #高铁选中与取消事件
self.checkBox_D.stateChanged.connect(self.change_D) #动车选中与取消事件
self.checkBox_Z.stateChanged.connect(self.change_Z) #直达车选中与取消事件
self.checkBox_T.stateChanged.connect(self.change_T) #特快车选中与取消事件
self.checkBox_K.stateChanged.connect(self.change_K) #快车选中与取消事件

(8)在Window.py文件右键菜单中选择Run window命令,运行主窗体,然后输入符合规范的出发地、目的地与出发日期,单击“查询”按钮,显示查询的余票信息。

开发快手爬票项目(中)相关推荐

  1. 开发快手爬票项目(下)

    首先附上完整代码,下节最终章会将做个过程中遇到的问题做个总结: window.py # -*- coding: utf-8 -*-# Form implementation generated fro ...

  2. 开发快手爬票项目(最终章)

    本节将介绍本人在开发过程中遇到的一些问题. 首先是面临着程序虽然能够正常运行,但是点击查询并不出现效果,并出现强退的场景. 原因:爬取网页时出现json.decoder.JSONDecodeError ...

  3. 快手爬票(爬取火车票信息)

    今天突发奇想,想搞一个爬虫,再加上PyQt5的GUI实践,就可以打造出来一个快手爬票:也不废话,上代码: (注:图片文件资源已整合到代码中,用CMD即可运行.) 需要的扩展库: altgraph==0 ...

  4. Mac笔记本中是用Idea开发工具在Java项目中调用python脚本遇到的环境变量问题解决...

    问题描述: mac笔记本本身会自带几个python版本,比如python2.7版本,我没有改动mac默认的python版本,只是安装了python3.7版本. 使用Pycharm开发Python项目没 ...

  5. javaweb开发微信公众号项目中怎么使用WEUI

    前言 使用java对微信公众号进行代码开发,关于前端实现则使用微信开发好的WEUI样式框架,那么具体怎么在项目中引入和适用WEUI,在网上找了好多资料,很多都是用在小程序上,并没有找到java上面的, ...

  6. python嵌入式系统开发_Python在嵌入式项目中辅助开发.PDF

    22 SYSPRACTICE 系统实践 on在嵌入式项目中的辅助开发 Pyth 彭树林 摘要:嵌入式系统设计开发过程中常会遇到诸如算法分析.原型验证.自动化测试.辅助工具设计等工作,其 开发效率和质量 ...

  7. flutter不支持热更新_在iOS原生项目中使用Flutter,热更新

    前言: Flutter 因其自建的渲染引擎,背靠谷歌的支持,近来俘获了不少的开发小伙伴,越来越多的开发者尝试使用Flutter进行开发,在原生项目中嵌入Flutter来完成复杂度不高的页面成为了一个不 ...

  8. 分享.NET开发中经常使用到的代码片段 完全从实际项目中提取出来,也可被反反复复的重复借用...

    几年前,一篇<ASP.NET开发人员经常使用的三十三种代码>非常流行,它总结了一些经常在ASP.NET开发中使用到的代码,直接可以拿来使用.今天重读这篇文章,有感而发,善于总结也是进步,于 ...

  9. GIS项目中数据开源、工具开源、开发开源的解决方案

    GIS项目中数据开源.工具开源.开发开源的解决方案 参考文章: (1)GIS项目中数据开源.工具开源.开发开源的解决方案 (2)https://www.cnblogs.com/naaoveGIS/p/ ...

最新文章

  1. Windows批量添加防火墙例外端口
  2. python监听udp端口_python检测远程udp端口是否打开
  3. Oracle-锁解读
  4. C#开发VS LUA开发
  5. 机器人局部避障的动态窗口法(dynamic window approach) (转)
  6. 中石油训练赛 - 奎奎画画(思维+并查集+离线处理)
  7. 一秒执行一次_《一秒钟》:一贯的粗旷式抓大放小,张艺谋的自命题作业总是要观众自己再做一遍...
  8. Matlab中的eig函数和Opecv中eigen()函数的区别
  9. 为什么vsdebug没有生成obj文件_用iPad实时扫描生成模型??两款实用App
  10. Codeforces Round #198 (Div. 1) B,C 动态规划
  11. C++学习系列笔记(三)
  12. 快看这个机器人,在使用双截棍!| 附正经paper
  13. STM32出现HardFault故障的解决方法
  14. 如何在不联网的情况下安装 Silverlight Tools
  15. 科密考勤机对比和参考价
  16. mac抓包工具charles破解版安装及简单使用
  17. 概率论笔记(一)重要公式
  18. 2020-08-24 光纤通信第五章知识点整理
  19. android 8代号,Android 8.0来了:代号或是“奥利奥”
  20. response.sendRedirect()的用法

热门文章

  1. 关于requests.exceptions.SSLError: HTTPSConnectionPool(host='XXX', port=443)问题
  2. 为什么优秀的程序员bug很少?因为他们……
  3. 【译】浏览器如何工作:在现代web浏览器场景的之下
  4. 普罗米修斯 mysql监控_Promethus(普罗米修斯)监控Mysql数据库
  5. 点云上的深度学习及其在三维场景理解中的应用————PointNet(一)
  6. 如何0基础学stm32?
  7. 隔空投送教程|如何将文件从iPhone或iPad空投到Mac计算机?
  8. 自己做量化交易软件(20)通达信公式选股程序的实现
  9. Cairo 图形指南 (5) —— 形状与填充
  10. 在 boot 操作过程中的 FIRST_BOOT阶段,安装失败,出现错误