前言:
第一篇博客,记录一下最近的一点点小成果。
一切的学习都从兴趣开始。最近忽然想学习一下 pyqt 和 python 的网络爬虫知识,于是就自己找了一个课题做了起来。因为我正好是个 NBA 球迷,就想到了通过网络爬虫来抓取比赛结果,方便本地进行查找的项目。
这个项目总共分为三步:
1. 界面制作:
选择对应的球队,显示球队图标和比赛结果
2. 网络爬虫:
访问特定网页,查找赛季至今的比赛结果信息,并保存到本地作为待查找的文件
3. 工具打包:
将上述两部分联调,并生成 exe 工具直接调用
我预备从以上三个部分,分别记录该查询工具的实现过程。
界面制作:
界面制作一直都想学,发现 pyqt 这个工具非常的顺手,可以通过直接拖拽模块来画界面。为了描述专业点,下面均称之为 Qt designer。
1. 界面设计:
下面先直接把最后设计的界面和效果图展示,然后挑几个值得一提的模块简单介绍一下:
QComboBox:产生下拉菜单的模块。下拉菜单中的可选项均可通过双击模块添加。被选中之后会产生一个“currentIndexChanged()”事件,根据这个时间去修改二级菜单中的对应项。
QLabel:用于显示查找结果。原先使用的是QLineEdit,由于自带一个鼠标靠近变色的事件,后来就用QLabel代替,QLabel还没有边框,在生成界面的时候显得简洁。
QLabel是一个非常好用的模块,界面中的VS字样,显示比分、球队图标、比赛时间,还有查询工具那张图,均是用QLabel实现的。
Layout:用于排版。将多个图标以整齐的方式排布在一起。整个结果栏使用了一个大的横向Layout,每一个小的比分又用了小的纵向Layout放置球队图标,比分等信息。
剩下的就是如何编排,让界面显得美观,这个就见仁见智了。
图1 NBA比赛查询系统界面
2. 生成代码:
将生成的 .ui文件转换成python可以运行的代码,有一条专门的指令,以我的这个工具为例:
pyuic4 -o NBA_schedule_2.py -x NBA_schedule_2.ui
这样就可以将 .ui文件变成 .py文件来运行。
将这条指令写成了批处理文件,每次修改完界面文件之后,执行一下即可。直接运行此py文件,就可以得到相应的界面。
这步中生成的代码由于会随着每次修改界面文件而改变,所以在文件在直接修改是很不明智的,以后修改起来也会很麻烦。最好的方法是将此文件作为模块,用另一个py文件去调用里面的类。
3. 功能实现:
新建一个类,继承ui生成的界面文件中的类,在新的类中定义信号。
本工具中有三个信号:
(1) 开始查询信号
(2) 客队赛区选定信号
(3) 主队赛区选定信号
当赛区选定之后,QComboBox会产生一个 currentIndexChanged 的信号,程序接收这个信号,对二级菜单进行修改,即重新加载球队。
点击“GO”按钮,开始查询交手情况。会将比分和球队logo显示在界面上,尚未开始的比赛只显示球队logo,标识主客。
功能一:选择球队
加载二级菜单,是利用QComboBox的信号,并再利用QComboBox的方法填充内容。
if roadteam_district == u'太平洋赛区': self.RoadTeam_combo.setItemText(0, _translate("Dialog", "太阳", None)) self.RoadTeam_combo.setItemText(1, _translate("Dialog", "勇士", None)) self.RoadTeam_combo.setItemText(2, _translate("Dialog", "湖人", None)) self.RoadTeam_combo.setItemText(3, _translate("Dialog", "快船", None)) self.RoadTeam_combo.setItemText(4, _translate("Dialog", "国王", None))
由于填入QComboBox的内容不是str格式,因此做了一个_translate转换。转换方式在生成界面py文件的时候会一并生成,并不需要自己编写代码。
功能二:查找比赛结果
查询交手情况先从本地的一个.csv文件开始查询,.csv每一行的存放可以事先规定好,这是要从网页端爬取下来的,因此格式可以自己规定。当前采取的格式是“客队,主队,比分,比赛时间”。
用QComboBox的currentText方法获取待查询的球队名称。由于读到的格式并不是字符串,将其转成字符串之后,再进行查询。
最开始的工具区分主队客队,两队主客交换会认为是另一组结果,随后将主客队的查询方式去掉,只查询两支球队的相互战绩,在显示的时候采取左边客队,右边主队的显示方式。寻找的范围以常规赛为主,因此最多有四场交手记录,也可以就此规定界面的长度。常规赛开始时间是2017年10月18日,因此比赛时间小于20171018的比赛,均不会纳入查找范畴。
功能三:加载球队logo
由于从.csv文件中读出的球队,是按照先客后主的顺序,所以可以将读到文件中的第一个值作为客队名,第二个值作为主队名,依照队名,到对应的字典中寻找图片地址,并加载到界面中。
NBA_LOGO_DICT = {u'火箭':'./NBA_team/HOU.png', u'马刺':'./NBA_team/SAS.png', u'鹈鹕':'./NBA_team/NOP.png', u'灰熊':'./NBA_team/MEN.png', u'小牛':'./NBA_team/DAL.png', u'勇士':'./NBA_team/GSW.png', u'湖人':'./NBA_team/LAL.png', u'快船':'./NBA_team/LAC.png', u'太阳':'./NBA_team/PHX.png', u'国王':'./NBA_team/SAC.png', u'开拓者':'./NBA_team/POR.png', u'森林狼':'./NBA_team/MIN.png', u'掘金':'./NBA_team/DEN.png', u'雷霆':'./NBA_team/OKC.png', u'爵士':'./NBA_team/UTA.png', u'凯尔特人':'./NBA_team/BOS.png', u'猛龙':'./NBA_team/TOR.png', u'76人':'./NBA_team/PHI.png', u'尼克斯':'./NBA_team/NYK.png', u'篮网':'./NBA_team/BKN.png', u'奇才':'./NBA_team/WAS.png', u'热火':'./NBA_team/MIA.png', u'黄蜂':'./NBA_team/CHA.png', u'魔术':'./NBA_team/ORL.png', u'老鹰':'./NBA_team/ATL.png', u'活塞':'./NBA_team/DET.png', u'骑士':'./NBA_team/CLE.png', u'步行者':'./NBA_team/IND.png', u'雄鹿':'./NBA_team/MIL.png', u'公牛':'./NBA_team/CHI.png'}
球队名称均用utf-8格式编码。搜索到多少场比赛,加载多少个图标,包括未开赛的。未开赛的比赛加载图标,可以显示主客情况。
功能四:修改界面字体颜色大小
由于任意两队之间的比赛场次不固定,如果这赛季只打两场比赛,但是加载出了4个框,会显得非常冗余,应该根据实际比赛的场次,来加载相应数目的对战框。
在每次搜索开始之前,会先将上次的框清空。
for i in range(4): self.r_pic_list[i].setPixmap(QtGui.QPixmap(_fromUtf8(None))) self.h_pic_list[i].setPixmap(QtGui.QPixmap(_fromUtf8(None))) self.game_list[i].setStyleSheet(_fromUtf8("font: 16pt \"微软雅黑\";")) self.game_list[i].setText(_translate("Dialog", '', None)) self.score_list[i].setText(_translate("Dialog", '', None))
其中r_pic_list,h_pic_list,game_list,score_list为自行创建的列表
self.score_list = [self.Score1_label, self.Score2_label, self.Score3_label, self.Score4_label] self.game_list = [self.GameDay1_label, self.GameDay2_label, self.GameDay3_label, self.GameDay4_label] self.r_pic_list = [self.RoadTeam_pic_1, self.RoadTeam_pic_2, self.RoadTeam_pic_3, self.RoadTeam_pic_4] self.h_pic_list = [self.HostTeam_pic_1, self.HostTeam_pic_2, self.HostTeam_pic_3, self.HostTeam_pic_4]
之后再行加载搜索到场次数目的比赛结果。
比分框用白色背景,黑色字体,字号规定为20号。加载比分时,直接将比分数字写入框内即可。
比赛时间框用蓝色背景,白色字体,字号为14号。在未加载比赛结果时,希望蓝色背景不显示,因此在清空框内内容时,重新设置了label框,清除了背景色。再次加载比赛时间,才限定背景色等等。
for i in range(len(result_list)): r_t = "%s" %result_list[i][0] h_t = "%s" %result_list[i][1] self.r_pic_list[i].setPixmap(QtGui.QPixmap(_fromUtf8(NBA_LOGO_DICT[r_t]))) self.h_pic_list[i].setPixmap(QtGui.QPixmap(_fromUtf8(NBA_LOGO_DICT[h_t]))) self.game_list[i].setStyleSheet(_fromUtf8("background-color: rgb(100, 200, 200);\n" "color: rgb(255, 255, 255);\n" "font: 14pt \"微软雅黑\";")) self.game_list[i].setText(_translate("Dialog", result_list[i][3], None)) self.score_list[i].setText(_translate("Dialog", result_list[i][2], None))
至此,界面端功能大致介绍完毕。
完整程序可参考以下链接:点我
相关链接:
PYTHON 实现 NBA 赛程查询工具(二)—— 网络爬虫