2019-2020-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 1943
实验日期:2020年6月10日
必修/选修: 公选课
1.实验内容
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
2. 实验过程及结果
2.1
首先明确实验目标:利用QT搭建UI用户友好界面,获取用户输入内容;编写爬虫代码,依照用户输入的数据进行爬取相应的机票,在ctrip网站上爬取到机票后,并将爬取的内容进行可视化处理,把机票信息显示到表格之中。
2.2.1
明确我的第一步是编写一个爬虫,先将ctrip网站的机票信息爬取到,爬取网址:https://flights.ctrip.com/itinerary/api/12808/products
值得注意的是:如果我们直接把网址复制到浏览器然后访问,是根本就没有办法查询到结果的。如下图:
#尝试通过火狐浏览器先访问,然后打开开发者界面点击“编辑并重发”
#同样也没有任何相应
因为我们这种访问方式仅仅是通过get方法访问服务器,想要它返回正常查询结果的必须用post方法访问才能实现,post就是提交,提交上去我们查询的起点、终点、日期、请求头等等一系列的相关数据ctrip的服务器才能给你返回内容。
2.2.2
编写爬虫代码query_ticket.py
import json import requests def query(dcityname,acityname,date,dcity,acity): url = "https://flights.ctrip.com/itinerary/api/12808/products" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362", "Content-Type": "application/json", # 声明文本类型为 json 格式, 'Cookie':'DomesticUserHostCity=053|%c7%ed%d6%d0;www.baidu.com%7C%7C%7C%7C%239' } date = date[0:4] + '-' + date[4:6] + '-' + date[6:8] data = {"flightWay": "Oneway", "classType": "ALL", "hasChild": "false", "hasBaby": "false", "searchIndex": 1, "airportParams": [ {"dcity": dcity, "acity": acity, "dcityname": dcityname, "acityname": acityname, "date": date}], "army": "false", "token": "037a9639c3ed3a76f30df6ef5fd8d4b9"} response = requests.post(url, data=json.dumps(data), headers=headers).text routeList = json.loads(response)["data"].get('routeList') data = [] if routeList is None: print("\n=========未查询到航班数据!============") return counter = 0 for route in routeList: if len(route.get('legs')) == 1: legs = route.get('legs')[0] flight = legs.get('flight') Airline = flight.get('airlineName') # 将航班信息添加到列表当中 data.append(Airline) FlightNumber = flight.get('flightNumber') # 将航班号添加到列表当中 data.append(FlightNumber) Date = flight.get('departureDate')[-8:-3] + "--" + flight.get('arrivalDate')[-8:-3] # 将经历时长添加到列表当中 data.append(Date) PunctualityRate = flight.get('punctualityRate') # 将准点率添加到列表当中 data.append(PunctualityRate) Price = legs.get('characteristic').get('lowestPrice') # 将机票价格添加到列表当中 data.append(Price) # 一轮下来添加了五个数据,记录到总数中 counter += 5 return data,counter
2.2.3
#下面get_station.py文件就是能够获取所有国内的哪些城市有机场以及获取机场的三字码
import re import os import urllib.request from query_ticket import * def main1(dcityname, acityname, date): ''' 函数功能:用于获取车站信息,各个站点对应的机场三字码 ''' # response即为获得到的所有站点数据 response = getUrl() stations = getStations(response) # 从获取到站点数据的所有信息中筛选出中文部分,即为城市名称 cities = getCities(stations) #####列表1 # 从获取到站点数据的所有信息中筛选出含字母大写部分,即为机场三字码 letters = getLetters(stations) #####列表2 # 判断是否保存站点信息的文件在本地已经存在 isStations = existStation() if (isStations == False): # 判断是否已经存在文件 writeStations(str(stations)) # 不存在文件则新建一个站名文件 index_dcity = cities.index(dcityname) # 得到出发城市在列表中的索引 index_acity = cities.index(acityname) # 得到目的城市在列表中的索引 dcity = letters[index_dcity] # 得到出发城市的三字码 acity = letters[index_acity] # 得到目的城市的三字码 ######################以下为测试数据###################### # print(dcityname, acityname, date, dcity, acity) # print(type(dcityname)) # print(type(date)) # print(type(dcity)) # flyapi(dcityname,acityname,date) ####################################################### # 调用query_ticket文件中的query方法,得到爬取到的数据列表,以及列表中元素个数 data,counter=query(dcityname, acityname, date, dcity, acity) # 测试数据 # print(data,counter) return data,counter # 获取站名文件 def getUrl(): url = "https://flights.ctrip.com/itinerary/api/poi/get" response = urllib.request.urlopen(url) return response def getStations(response): str1 = response.read().decode() stations = re.findall(r'[\u4e00-\u9fa5]+\([A-Z]+\)', str1) return stations def getCities(stations): stations = str(stations) cities = re.findall(r'[\u4e00-\u9fa5]+', stations) return cities def getLetters(stations): stations = str(stations) letters = re.findall(r'[A-Z]+', stations) return letters def writeStations(stations): file = open('stations_info.txt', 'w', encoding='utf-8_sig') file.write(stations) file.close() def existStation(): isStations = os.path.exists('stations_info.txt') return isStations if __name__ == '__main__': main1("xxx", "xxx", "20200202")
2.2.4
下面的代码都是用辅助工具QTdesigner完成的,qtdesigner是非常好的界面设计软件,能够直接拖拽控件实现界面的布局,值得注意的是,完成一个UI项目以后,自动生成的是一个.Ui文件。需要在pycharm中设置Tools,将Ui文件转换成py文件。
教程链接:https://www.cnblogs.com/feigen/p/11082745.html
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'UI.ui' # # Created by: PyQt5 UI code generator 5.14.2 # # WARNING! All changes made in this file will be lost! # 该部分的导入为按照pycharm提示导入 from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtWidgets import QHeaderView, QAbstractItemView from get_stations import main1 # 该部分设置UI界面的代码大部分由QTdesigner编译完成并用工具转化成python代码 class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(960, 786) MainWindow.setMinimumSize(QtCore.QSize(960, 786)) MainWindow.setMaximumSize(QtCore.QSize(960, 786)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/png/img/huabanfuben (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.widget = QtWidgets.QWidget(self.centralwidget) self.widget.setGeometry(QtCore.QRect(0, 0, 961, 141)) self.widget.setObjectName("widget") self.widget_2 = QtWidgets.QWidget(self.widget) self.widget_2.setGeometry(QtCore.QRect(0, 0, 961, 141)) self.widget_2.setStyleSheet("background-image: url(:/jpg/img/img3.jpg);") self.widget_2.setObjectName("widget_2") self.widget_3 = QtWidgets.QWidget(self.centralwidget) self.widget_3.setGeometry(QtCore.QRect(0, 220, 961, 561)) self.widget_3.setObjectName("widget_3") self.tableView = QtWidgets.QTableView(self.widget_3) self.tableView.setGeometry(QtCore.QRect(0, 0, 961, 571)) self.tableView.setObjectName("tableView") # 不设置具体行、列数,由数据的变化而变化 self.model = QStandardItemModel() # 根据空间自动改变列宽度并且不可修改列宽度 self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 设置表头 self.model.setHorizontalHeaderLabels(["航空公司", "航班", "起飞时间--到达时间", "准点率", "最低价格"]) # 设置表格内容文字大小 font = QtGui.QFont() font.setPointSize(10) self.tableView.setFont(font) # 设置表格内容不可编辑 self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) # 垂直滚动条始终开启 self.tableView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.tableView.setModel(self.model) self.widget_query = QtWidgets.QWidget(self.centralwidget) self.widget_query.setGeometry(QtCore.QRect(0, 140, 960, 80)) self.widget_query.setObjectName("widget_query") self.label = QtWidgets.QLabel(self.widget_query) self.label.setGeometry(QtCore.QRect(30, 30, 72, 15)) self.label.setObjectName("label") self.textEdit = QtWidgets.QTextEdit(self.widget_query) self.textEdit.setGeometry(QtCore.QRect(100, 20, 104, 31)) self.textEdit.setObjectName("textEdit") self.label_2 = QtWidgets.QLabel(self.widget_query) self.label_2.setGeometry(QtCore.QRect(230, 30, 72, 15)) self.label_2.setObjectName("label_2") self.textEdit_2 = QtWidgets.QTextEdit(self.widget_query) self.textEdit_2.setGeometry(QtCore.QRect(310, 20, 104, 31)) self.textEdit_2.setObjectName("textEdit_2") self.label_3 = QtWidgets.QLabel(self.widget_query) self.label_3.setGeometry(QtCore.QRect(430, 30, 72, 15)) self.label_3.setObjectName("label_3") self.textEdit_3 = QtWidgets.QTextEdit(self.widget_query) self.textEdit_3.setGeometry(QtCore.QRect(510, 20, 104, 31)) self.textEdit_3.setObjectName("textEdit_3") self.pushButton = QtWidgets.QPushButton(self.widget_query) self.pushButton.setGeometry(QtCore.QRect(660, 20, 93, 28)) self.pushButton.setObjectName("pushButton") # 点击按钮时connect到事件on_click() self.pushButton.clicked.connect(lambda:self.on_click()) self.pushButton_2 = QtWidgets.QPushButton(self.widget_query) self.pushButton_2.setGeometry(QtCore.QRect(790, 20, 93, 28)) self.pushButton_2.setObjectName("pushButton_2") MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "TMR—您身边的出行管家")) self.label.setText(_translate("MainWindow", "出发城市:")) self.label_2.setText(_translate("MainWindow", "目的城市:")) self.label_3.setText(_translate("MainWindow", "出发时间:")) self.pushButton.setText(_translate("MainWindow", "开始查询")) self.pushButton_2.setText(_translate("MainWindow", "显示机票")) # 查询按钮的单击事件 def on_click(self): dcity = self.textEdit.toPlainText() # 获取出发地 acity = self.textEdit_2.toPlainText() # 获取到达地 date = self.textEdit_3.toPlainText() # 获取出发时间 # # 此数据为调试时的测试数据 # dcity = "北京" # 获取出发地 # # acity = "上海" # 获取到达地 # # date = "20200620" # 获取出发时间 data,counter = main1(dcity, acity, date) # # 此数据为调试时的测试数据 # data=['吉祥航空', 'HO1260', '06:50--09:05', '', '305', '东方航空', 'MU5138', '07:00--09:20', '96.67%', '450', '中国国航', 'CA1831', '07:30--09:40', '100.00%', '450'] # counter = 15 self.displayTable(data,counter) # 展示表格 def displayTable(self,data,counter): self.model.clear() # 设置行数hi,实际上就是列表中的元素个数counter除以5 hi = counter//5 # 设置变量a,来保存列表中元素对应的索引visit a = 0 # 设置表头 self.model.setHorizontalHeaderLabels(["航空公司", "航班", "起飞时间--到达时间", "准点率", "最低价格"]) # 循环行 for row in range(hi): # 循环列 for column in range(5): # 循环i用来记录data列表中的元素 i = str(data[a]) # print(i,a) if i == '':# 处理没有数据的情况 item = QStandardItem("@_@ NONE") else: # 添加表格内容 item = QStandardItem(i) # 向表格存储模式中添加表格具体信息 self.model.setItem(row, column, item) a = a+1 import imge_rc
这里的import imge_rc是导入Ui界面所用的图片利用qtdesigner生成Ui文件时同时会生成一个.qrc的文件,是用来保存导入图片的,当我们在代码中使用时,也应该将.qrc文件转换成.py文件。相关教程:https://blog.csdn.net/qq_42980303/article/details/88047132
2.2.5
创建一个show_my_window.py做主程序,用来显示窗口
# Author:Small Z #-*- coding = utf-8 -*- #@Time = 2020/6/7 12:02 #@Author number : 20194314 #@Software : PyCharm import sys from UI import * def show_MainWindow(): app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_()) if __name__ == '__main__': show_MainWindow()
2.3运行结果的展示
经过以上的步骤,代码其实已经大功告成了。看看演示效果
这个是运行show_my_window.py后的结果,输入出发地、目的地及日期,点击“开始查询”就能显示结果了~~
3. 实验过程中遇到的问题和解决过程
3.1
在导入pyqt5库的时候引用PyQt5的时候显示错误“ModuleNotFoundError: No module named 'pyqt5'”
参考博客网址https://www.cnblogs.com/shengQiming/p/11571980.html
我发现博主介绍的方法很多人都成功了,但是我却仍然报错,我用了博主介绍的方法三
”在已有项目的最后面找到External Libraries ,然后找到venv目录,把pyvenv.cfg文件中的如下参数改成true” "include-system-site-packages = true"
结果我在操作的时候把“true”写成了“ture”一直消耗了很久时间
3.2
在添加External Tools——Trctopy的时候直接复制了参考网址中的argument:$ FileName$ -o $ FileNameWithoutExtension $命令。
不知道$ FileNameWithoutExtension $应该是$符号和字母之间是不能有空格的,直接一直都报错File does not exist 'FileNameWithoutExtension'及File does not exist 'FileNameWithoutExtension$_rc.py’
3.3
son.dumps()和json.loads()的使用,参考文章https://www.cnblogs.com/hjianhui/p/10387057.html
3.4
如何做好:界面获取输入数据------>数据传入爬票程序------->爬票程序返回机票信息------->用户界面显示数据 的衔接
利用
self.textEdit.toPlainText()
方法获取用户输入;
利用
item = QStandardItem(i)
self.model.setItem(row, column, item)
向表格中添加信息
在这个过程是观看bilibili网站的https://www.bilibili.com/video/BV154411n79k?p=63视频
3.5
在步骤:爬票程序返回机票信息------->用户界面显示数据的衔接过程中,最后一行机票的价格始终没有办法显示,尝试了很多解决方法,最后发现返回的价格其实是整型数据,而写入列表的内容只能是str字符串型数据,所以用到了强制类型转换将机票价格转为字符串型数据,最终才能在表格中显示。
4. 感悟与思考
编程学习是终身的,也是互通的。为什么是终身的呢?因为如果一段时间你不学,你很快就会忘记当初已经感觉“掌握”了的内容,python有很多库的应用,各种用法应该可以说是应有尽有,学习一门编程语言不可能在很短的时间内就能把TA的全部东西都能学完的,尽管经历过一学期的学习,我还是感觉python需要我去探索的空间还有很大很大,所以需要我们终身学习,感受编程的强大... ...那为什么是互通的呢?比如C语言中函数的内容我就是借鉴了python才更好地理解的,同时做C语言的一些解题思想我也应用到了python中,比如计数循环;提供列表cities寻找出元素相应的索引,再用索引去访问列表letters,找到对应的三字码。再将数据传入查票爬虫。学习了一个学期的python课程,老师在课堂上讲的非常少,实际上,老师是锻炼了我们的自学能力,有了这一能力,无论是在那一门课程都能很好地学习到想要的内容,全部靠自己使用好互联网这一把武器应对各种问题,这样,我感觉到了暑假,我还是会学习python一些相应的知识,毕竟我已经多多少少都有了一些自学能力。在一个学期的python学习中也发现了一些“宝藏”,比如B站。我觉得老师把时间留给同学们自己去探索的教学方法是非常值得肯定的。毕竟人生还有很长的路要走,还有很多等待着我们去探索的地方,在这门课程的学习过程中一些经验我们可以举一反三,应用到我们的人生中。
5. 参考资料
《python编程 从入门到实践》